code

Visual Studio에서 단위 테스트의 실행 순서 제어

codestyles 2020. 11. 16. 08:19
반응형

Visual Studio에서 단위 테스트의 실행 순서 제어


좋아요, 이것에 대한 좋은 정보를 검색했습니다. 일단 초기화되면 변경할 수 없거나 변경하고 싶지 않은 속성을 설정하는 정적 클래스를 호출하는 일련의 단위 테스트가 있습니다.

내 문제는 테스트가 실행되도록 설정된 순서를 적용 할 수 없다는 것입니다. 가능하다면 정적 속성이 신뢰할 수있는 방식으로 설정되는 방식으로 실행할 수 있고 Assert를 수행 할 수 있지만 안타깝게도 Microsoft.VisualStudio.TestTools.UnitTesting 프레임 워크는 겉보기에 임의의 순서로 실행합니다. .

그래서이 http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.priorityattribute.aspx찾았 습니다. 설명 섹션에 "이 속성은 테스트 시스템에서 사용되지 않습니다. 사용자 지정 목적으로 사용자에게 제공됩니다. " 어? 그러면 무슨 소용이 있습니까? 그들은 내가이 멋진 속성을 활용하기 위해 내 자신의 테스트 래퍼를 작성하기를 기대합니까 (그 정도의 노력을 기울이고 싶다면 쉽게 작성할 수 있습니다 ...)

그래서, 충분한 폭언; 요컨대, 단위 테스트가 실행되는 순서를 제어하는 ​​방법이 있습니까?

[TestMethod]
[Priority(0)]

등은 작동하지 않는 것 같습니다. 이는 Microsoft가 작동하지 않는다고 말했기 때문에 의미가 있습니다.

또한 "격리 위반"에 대한 의견을 남기지 마십시오. TestClass는 개별 TestMethods가 아니라 내가 테스트중인 것을 분리합니다. 그럼에도 불구하고 각 테스트는 독립적으로 잘 실행될 수 있으며 정적 클래스를 해체 할 방법이 없기 때문에 임의의 순서로 함께 실행할 수 없습니다.

오, "주문 테스트"에 대해서도 알고 있습니다.


테스트를 하나의 거대한 테스트로 병합하면 효과가 있습니다. 테스트 방법을 더 읽기 쉽게 만들기 위해 다음과 같이 할 수 있습니다.

[TestMethod]
public void MyIntegratonTestLikeUnitTest()
{
    AssertScenarioA();

    AssertScenarioB();

    ....
}

private void AssertScenarioA()
{
     // Assert
}

private void AssertScenarioB()
{
     // Assert
}

실제로 당신이 가지고있는 문제는 아마도 구현의 테스트 가능성을 향상시켜야한다고 제안합니다.


당신은 재생 목록을 사용할 수 있습니다

테스트 방법을 마우스 오른쪽 버튼으로 클릭-> 재생 목록에 추가-> 새 재생 목록

실행 순서는 재생 목록에 추가하는 것과 같지만 변경하려면 파일이 있습니다.

여기에 이미지 설명 입력


ClassInitialize속성 메서드를 언급하는 사람이 보이지 않습니다 . 속성은 매우 간단합니다.

단위 테스트가 실행될 환경의 측면을 준비하기 위해 [ClassInitialize()]또는 [TestInitialize()]특성으로 표시된 메서드를 만듭니다 . 이것의 목적은 단위 테스트를 실행하기위한 알려진 상태를 설정하는 것입니다. 예를 들어 [ClassInitialize()]또는 [TestInitialize()]방법을 사용하여 테스트에서 사용할 특정 데이터 파일을 복사, 변경 또는 생성 할 수 있습니다.

[ClassCleanup()]또는 [TestCleanUp{}]특성으로 표시된 메서드를 만들어 테스트가 실행 된 후 환경을 알려진 상태로 되돌립니다. 이것은 폴더의 파일을 삭제하거나 데이터베이스를 알려진 상태로 되 돌리는 것을 의미 할 수 있습니다. 이에 대한 예는 주문 입력 애플리케이션에서 사용되는 방법을 테스트 한 후 재고 데이터베이스를 초기 상태로 재설정하는 것입니다.

  • [ClassInitialize()]ClassInitialize클래스에서 첫 번째 테스트를 실행하기 전에 코드를 실행하는 데 사용 합니다.

  • [ClassCleanUp()]ClassCleanup클래스의 모든 테스트가 실행 된 후 코드를 실행하는 데 사용 합니다.

  • [TestInitialize()]TestInitialize각 테스트를 실행하기 전에 코드를 실행하는 데 사용 합니다.

  • [TestCleanUp()]TestCleanup각 테스트가 실행 된 후 코드를 실행하는 데 사용 합니다.


댓글 작성자가 이미 지적했듯이 다른 테스트에 의존하는 테스트는 설계 결함을 나타냅니다. 그럼에도 불구하고 그것을 달성하는 방법이 있습니다. 여기 에서 이전에 질문 한 질문에서 답변했듯이 기본적으로 테스트 시퀀스를 보장하는 단일 테스트 컨테이너 인 정렬 된 단위 테스트를 만들 수 있습니다.

MSDN에 대한 가이드 : http://msdn.microsoft.com/en-us/library/ms182631.aspx


Visual Studio 테스트 프레임 워크가 제공하는 Ordered Test 기능을 이미 언급 했으므로 무시하겠습니다. 또한이 정적 클래스를 테스트하기 위해 수행하려는 작업이 "나쁜 생각"이라는 것을 알고있는 것 같으므로 무시하겠습니다.

대신 원하는 순서로 테스트가 실행되도록 실제로 보장 할 수있는 방법에 초점을 맞 춥니 다. 하나의 옵션 (@gaog에서 제공)은 "하나의 테스트 방법, 많은 테스트 함수"로, TestMethod속성으로 표시된 단일 함수 내에서 원하는 순서로 테스트 함수를 호출합니다 . 이것이 가장 간단한 방법이며 유일한 단점은 첫 번째 테스트 기능이 실패하면 나머지 테스트 기능이 실행되지 않는다는 입니다.

상황에 대한 설명과 함께 이것이 사용하도록 제안하는 솔루션입니다.

굵게 표시된 부분이 문제인 경우 내장 된 데이터 기반 테스트 기능을 활용하여 격리 된 테스트를 순서대로 실행할 수 있습니다. 더 복잡하고 약간 더러워 보이지만 작업이 완료됩니다.

간단히 말해, 테스트를 실행해야하는 순서와 실제로 테스트 기능을 포함하는 함수의 이름을 제어하는 ​​데이터 소스 (예 : CSV 파일 또는 데이터베이스 테이블)를 정의합니다. 그런 다음 해당 데이터 소스를 데이터 기반 테스트에 연결하고 순차 읽기 옵션을 사용하고 원하는 순서대로 개별 테스트로 함수를 실행합니다.

[TestClass]
public class OrderedTests
{
    public TestContext TestContext { get; set; }

    private const string _OrderedTestFilename = "TestList.csv";

    [TestMethod]
    [DeploymentItem(_OrderedTestFilename)]
    [DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", _OrderedTestFilename, _OrderedTestFilename, DataAccessMethod.Sequential)]
    public void OrderedTests()
    {
        var methodName = (string)TestContext.DataRow[0];
        var method = GetType().GetMethod(methodName);
        method.Invoke(this, new object[] { });
    }

    public void Method_01()
    {
        Assert.IsTrue(true);
    }

    public void Method_02()
    {
        Assert.IsTrue(false);
    }

    public void Method_03()
    {
        Assert.IsTrue(true);
    }
}

내 예에서는 출력에 복사되는 TestList.csv라는 지원 파일이 있습니다. 다음과 같이 보입니다.

TestName
Method_01
Method_02
Method_03

테스트는 지정한 순서대로 일반적인 테스트 격리 상태로 실행됩니다 (즉, 하나가 실패해도 나머지는 여전히 실행되지만 정적 클래스를 공유 함).

위의 내용은 실제로 기본 아이디어 일뿐입니다. 프로덕션에서 사용한다면 테스트가 실행되기 전에 동적으로 테스트 함수 이름과 순서를 생성합니다. 찾은 PriorityAttribute와 간단한 리플렉션 코드를 활용하여 클래스에서 테스트 메서드를 추출하고 적절하게 정렬 한 다음 해당 순서를 데이터 소스에 기록 할 수 있습니다.


지금 쯤 알고 계시 겠지만, 순수 주의자들은 주문 테스트를 실행하는 것이 금지되어 있다고 말합니다. 단위 테스트의 경우에 해당 할 수 있습니다. MSTest 및 기타 단위 테스트 프레임 워크는 순수 단위 테스트를 실행하는 데 사용되며 UI 테스트, 전체 통합 테스트도 실행합니다. 단위 테스트 프레임 워크라고 부르면 안되거나 필요에 따라 사용해야 할 수도 있습니다. 어쨌든 대부분의 사람들이하는 일입니다.

저는 VS2015를 실행 중이며 UI 테스트 (Selenium)를 실행하고 있기 때문에 지정된 순서대로 테스트를 실행해야합니다.

우선 순위 -아무것도하지 않음 이 속성은 테스트 시스템에서 사용되지 않습니다. 사용자 정의 목적으로 사용자에게 제공됩니다.

orderedtest- 작동하지만 다음과 같은 이유로 권장하지 않습니다.

  1. orderedtest 가 실행되어야 위해 테스트를 나열하는 텍스트 파일. 메소드 이름을 변경하는 경우 파일을 수정해야합니다.
  2. 테스트 실행 순서는 클래스 내에서 존중됩니다. 어떤 클래스가 먼저 테스트를 실행하는지 주문할 수 없습니다.
  3. orderedtest의 파일은 디버그 또는 릴리스 중, 구성에 바인딩
  4. 여러 개의 정렬 된 테스트 파일을 가질 수 있지만 지정된 방법을 다른 정렬 된 테스트 파일 에서 반복 할 수 없습니다 . 따라서 디버그 용과 릴리스 용으로 orderedtest 파일 하나를 가질 수 없습니다 .

이 스레드의 다른 제안은 흥미롭지 만 테스트 탐색기에서 테스트 진행 상황을 추적 할 수있는 기능이 없습니다.

당신은 순수 주의자가 조언 할 해결책을 남겼지 만 실제로 작동하는 해결책은 선언 순서로 정렬합니다 .

MSTest 실행자는 선언 순서를 관리하는 interop을 사용하며이 트릭은 Microsoft가 테스트 실행기 코드를 변경할 때까지 작동합니다.

즉, 처음에 선언 된 테스트 메서드가 두 번째 위치에 선언 된 메서드보다 먼저 실행됩니다.

당신의 삶을 더 쉽게 만들기 위해 선언 순서는 테스트 탐색기에 표시된 알파벳 순서와 일치해야합니다.

  • A010_FirstTest
  • A020_SecondTest
  • 기타
  • A100_TenthTest

오래되고 테스트 된 규칙을 강력히 제안합니다.

  • 나중에 테스트 방법을 삽입해야하므로 10 단계를 사용하십시오.
  • 테스트 번호 사이에 넉넉한 단계를 사용하여 테스트 번호를 다시 매길 필요가 없습니다.
  • 10 개 이상의 테스트를 실행하는 경우 3 자리 숫자를 사용하여 테스트 번호를 매 깁니다.
  • 100 개 이상의 테스트를 실행하는 경우 4 자리 숫자를 사용하여 테스트 번호를 매 깁니다.

매우 중요

선언 순서대로 테스트를 실행 하려면 테스트 탐색기에서 모두 실행 을 사용해야합니다 .

3 개의 테스트 클래스가 있다고 가정합니다 (제 경우에는 Chrome, Firefox 및 Edge에 대한 테스트). 주어진 클래스를 선택하고 Run Selected Tests를 마우스 오른쪽 버튼으로 클릭 하면 일반적으로 마지막 위치에 선언 된 메서드를 실행하여 시작됩니다.

다시 말하지만, 선언 된 순서상장 된 순서 가 일치해야합니다. 그렇지 않으면 곧 큰 문제가 발생할 것입니다.


다음은 어떤 이유로 든 MS Ordered Tests 프레임 워크와 독립적으로 정렬 된 테스트를 설정하고 실행하는 데 사용할 수있는 클래스입니다 (예 : 빌드 시스템에서 mstest.exe 인수를 조정할 필요가 없거나 클래스에서 정렬되지 않은 것과 혼합 됨).

원래 테스트 프레임 워크는 정렬 된 테스트 목록 만 단일 테스트로 간주하므로 [TestInitalize ()] Init ()와 같은 모든 초기화 / 정리는 전체 세트 전후에만 호출됩니다.

용법:

        [TestMethod] // place only on the list--not the individuals
        public void OrderedStepsTest()
        {
            OrderedTest.Run(TestContext, new List<OrderedTest>
            {
                new OrderedTest ( T10_Reset_Database, false ),
                new OrderedTest ( T20_LoginUser1, false ),
                new OrderedTest ( T30_DoLoginUser1Task1, true ), // continue on failure
                new OrderedTest ( T40_DoLoginUser1Task2, true ), // continue on failure
                // ...
            });                
        }

이행:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace UnitTests.Utility
{    
    /// <summary>
    /// Define and Run a list of ordered tests. 
    /// 2016/08/25: Posted to SO by crokusek 
    /// </summary>    
    public class OrderedTest
    {
        /// <summary>Test Method to run</summary>
        public Action TestMethod { get; private set; }

        /// <summary>Flag indicating whether testing should continue with the next test if the current one fails</summary>
        public bool ContinueOnFailure { get; private set; }

        /// <summary>Any Exception thrown by the test</summary>
        public Exception ExceptionResult;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="testMethod"></param>
        /// <param name="continueOnFailure">True to continue with the next test if this test fails</param>
        public OrderedTest(Action testMethod, bool continueOnFailure = false)
        {
            TestMethod = testMethod;
            ContinueOnFailure = continueOnFailure;
        }

        /// <summary>
        /// Run the test saving any exception within ExceptionResult
        /// Throw to the caller only if ContinueOnFailure == false
        /// </summary>
        /// <param name="testContextOpt"></param>
        public void Run()
        {
            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                ExceptionResult = ex;
                throw;
            }
        }

        /// <summary>
        /// Run a list of OrderedTest's
        /// </summary>
        static public void Run(TestContext testContext, List<OrderedTest> tests)
        {
            Stopwatch overallStopWatch = new Stopwatch();
            overallStopWatch.Start();

            List<Exception> exceptions = new List<Exception>();

            int testsAttempted = 0;
            for (int i = 0; i < tests.Count; i++)
            {
                OrderedTest test = tests[i];

                Stopwatch stopWatch = new Stopwatch();
                stopWatch.Start();

                testContext.WriteLine("Starting ordered test step ({0} of {1}) '{2}' at {3}...\n",
                    i + 1,
                    tests.Count,
                    test.TestMethod.Method,
                    DateTime.Now.ToString("G"));

                try
                {
                    testsAttempted++;
                    test.Run();
                }
                catch
                {
                    if (!test.ContinueOnFailure)
                        break;
                }
                finally
                {
                    Exception testEx = test.ExceptionResult;

                    if (testEx != null)  // capture any "continue on fail" exception
                        exceptions.Add(testEx);

                    testContext.WriteLine("\n{0} ordered test step {1} of {2} '{3}' in {4} at {5}{6}\n",
                        testEx != null ? "Error:  Failed" : "Successfully completed",
                        i + 1,
                        tests.Count,
                        test.TestMethod.Method,
                        stopWatch.ElapsedMilliseconds > 1000
                            ? (stopWatch.ElapsedMilliseconds * .001) + "s"
                            : stopWatch.ElapsedMilliseconds + "ms",
                        DateTime.Now.ToString("G"),
                        testEx != null
                            ? "\nException:  " + testEx.Message +
                                "\nStackTrace:  " + testEx.StackTrace +
                                "\nContinueOnFailure:  " + test.ContinueOnFailure
                            : "");
                }
            }

            testContext.WriteLine("Completed running {0} of {1} ordered tests with a total of {2} error(s) at {3} in {4}",
                testsAttempted,
                tests.Count,
                exceptions.Count,
                DateTime.Now.ToString("G"),
                overallStopWatch.ElapsedMilliseconds > 1000
                    ? (overallStopWatch.ElapsedMilliseconds * .001) + "s"
                    : overallStopWatch.ElapsedMilliseconds + "ms");

            if (exceptions.Any())
            {
                // Test Explorer prints better msgs with this hierarchy rather than using 1 AggregateException().
                throw new Exception(String.Join("; ", exceptions.Select(e => e.Message), new AggregateException(exceptions)));
            }
        }
    }
}

I'll not address the order of tests, sorry. Others already did it. Also, if you know about "ordered tests" - well, this is MS VS's response to the problem. I know that those ordered-tests are no fun. But they thought it will be "it" and there's really nothing more in MSTest about that.

I write about one of your assumptions:

as there is no way to tear down the static class.

Unless your static class represents some process-wide external state external to your code (like ie. the state of an unmanaged native DLL library thats P/Invoked by the rest of your code), your assumption that there is no way is not true.

If your static class refers to this, then sorry, you are perfectly right, the rest of this anwer is irrelevant. Still, as you didn't say that, I assume your code is "managed".

Think and check the AppDomain thingy. Rarely it is needed, but this is exactly the case when you'd probably like to use them.

You can create a new AppDomain, and instantiate the test there, and run the test method there. Static data used by managed code will isolated there and upon completion, you will be able to unload the AppDomain and all the data, statics included, will evaporate. Then, next test would initialize another appdomain, and so on.

This will work unless you have external state that you must track. AppDomains only isolate the managed memory. Any native DLL will still be load per-process and their state will be shared by all AppDomains.

Also, creating/tearing down the appdomains will, well, slow down the tests. Also, you may have problems with assembly resolution in the child appdomain, but they are solvable with reasonable amount of reusable code.

Also, you may have small problems with passing test data to - and back from - the child AppDomain. Objects passed will either have to be serializable in some way, or be MarshalByRef or etc. Talking cross-domain is almost like IPC.

However, take care here, it will be 100% managed talking. If you take some extra care and add a little work to the AppDomain setup, you will be able to even pass delegates and run them in the target domain. Then, instead of making some hairy cross-domain setup, you can wrap your tests with to something like:

void testmethod()
{
    TestAppDomainHelper.Run( () =>
    {
        // your test code
    });
}

or even

[IsolatedAppDomain]
void testmethod()
{
    // your test code
}

if your test framework supports creating such wrappers/extensions. After some initial research and work, using them is almost trivial.


I see that this topic is almost 6 years old, and we now have new version of Visual studio but I will reply anyway. I had that order problem in Visual Studio 19 and I figured it out by adding capital letter (you can also add small letter) in front of your method name and in alphabetical order like this:

[TestMethod]
        public void AName1()
        {}
[TestMethod]
        public void BName2()
        {}

And so on. I know that this doesn't look appealing, but it looks like Visual is sorting your tests in test explorer in alphabetical order, doesn't matter how you write it in your code. Playlist didn't work for me in this case.

Hope that this will help.


they just can't be run together in a random order as there is no way to tear down the static class

알파벳순으로 네임 스페이스와 클래스의 이름을 지정할 수 있습니다. 예 :

  • MyApp.Test. Stage01 _ 설정. Step01 _BuildDB
  • MyApp.Test. Stage01 _ 설정. Step02 _UpgradeDB
  • MyApp.Test. Stage02_ 도메인. Step01 _TestMyStaff
  • MyApp.Test. Stage03 _ 통합. Step01 _TestMyStaff

어디는 MyApp.Test.Stage01_Setup네임 스페이스이며, Step01_BuildDB클래스 이름입니다.

참고 URL : https://stackoverflow.com/questions/20711300/controlling-execution-order-of-unit-tests-in-visual-studio

반응형