Search code examples
c#.netunit-testingtestingmstest

MSTest Extending (Obtaining test class instance)


I have tests that use a socket connection to test calls to a server. The socket object is a global variable in the test class itself, which I set before every test using a method marked with [TestInitialize]. We'll call this TestInit().

I wanted to extend MSTest to run each test twice, swapping the socket out for the second run, allowing me to effectively run the tests against two backends/servers. I did this by extending TestMethodAttribute's Execute() function and calling Invoke() twice.

From TestMethodAttribute's Execute(), there is no instance variables. I cannot get the test class to swap out the socket. My solution was to use static variables to signal if we are on the second execution, and if so, TestInit() uses the second socket instead of first.

Problem: I can use locks on the extended TestMethodAttribute to ensure my static variable which marks the secondary execution isn't prone to a race condition with another test running in parallel. The issue is, normal test method attributes won't have the thread locking code, and so mixing the original TestMethodAttribute with the extended one will cause race conditions. Locking tests sequentially by thread-locking on TestInit() and unlocking on TestCleanup() won't work either, since other tests can enter between the first and second test, after we set the "secondary execution" test. (Race condition).

Is there any way to get the test class instance in TestMethodAttribute's Execute()? Is there anything I could do, short of forcing usage of custom TestMethodAttributes/TestClassAttributes everywhere? Please help.

(Do not suggest I manually write the swapping into tests, the whole point of my question is to have the test framework abstract this away from you. Also do not suggest disabling parallelization, as I need it still).

Thank you.


Solution

  • You can mark your static variable that is being used to indicate which run with the [ThreadStatic] attribute.

    [TestClass]
    public class MyTest
    {
        [ThreadStatic]
        public static int Run = 1;
    
        [TestInitialize]
        public void TestInit()
        {
            if (Run == 1) 
            {
                //...
            }
            else if (Run == 2) 
            { 
                //...
            }
        }
    
        [MyTestMethod]
        public void MyTestMethod() 
        {
            //...
        }
    }
    
    public class MyTestMethodAttribute : TestMethodAttribute
    {
        public override TestResult[] Execute(ITestMethod testMethod)
        {
            MyTest.Run = 1;
            var result1 = testMethod.Invoke(null);
            MyTest.Run = 2;
            var result2 = testMethod.Invoke(null);
    
            return new TestResult[] { result1, result2 };
        }
    }