This post is part of the series 'MSTest v2'. Be sure to check out the rest of the blog posts of the series!
MSTest v2 is extensible. In the previous posts, we saw how to extend DataTest and create new assert methods. In this post, we'll see how to customize how tests are executed.
#Customizing execution at test method level
By default, the runner executes methods decorated with [TestMethod]. Looking more closely at the TestMethod attribute, you'll see that the class contains the test execution logic. The Execute method (GitHub) calls the test method and returns the result.
C#
public class TestMethodAttribute : Attribute
{
public virtual TestResult[] Execute(ITestMethod testMethod)
{
return new TestResult[] { testMethod.Invoke(null) };
}
}
The method is virtual, so you can extend this class to customize how the method is called. For instance, if you need to execute the method on an STA thread, you can create a custom attribute and override the Execute method.
C#
public class STATestMethodAttribute : TestMethodAttribute
{
public override TestResult[] Execute(ITestMethod testMethod)
{
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
return Invoke(testMethod);
TestResult[] result = null;
var thread = new Thread(() => result = Invoke(testMethod));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
return result;
}
private TestResult[] Invoke(ITestMethod testMethod)
{
return new[] { testMethod.Invoke(null) };
}
}
You can use this attribute instead of TestMethod:
C#
[TestClass]
public class TestClass1
{
[STATestMethod]
public void Test_STA()
{
Assert.AreEqual(ApartmentState.STA, Thread.CurrentThread.GetApartmentState());
}
}
#Customizing execution at test class level
You might wonder why the TestClass attribute is needed when each test is already decorated with [TestMethod]. The TestClass attribute lets you customize how tests are discovered and executed within a class. For instance, if you want all tests in a class to run on an STA thread, you can create a class that inherits from TestClassAttribute.
C#
public class STATestClassAttribute : TestClassAttribute
{
public override TestMethodAttribute GetTestMethodAttribute(TestMethodAttribute testMethodAttribute)
{
if (testMethodAttribute is STATestMethodAttribute)
return testMethodAttribute;
return new STATestMethodAttribute(base.GetTestMethodAttribute(testMethodAttribute));
}
}
public class STATestMethodAttribute : TestMethodAttribute
{
private readonly TestMethodAttribute _testMethodAttribute;
public STATestMethodAttribute()
{
}
public STATestMethodAttribute(TestMethodAttribute testMethodAttribute)
{
_testMethodAttribute = testMethodAttribute;
}
public override TestResult[] Execute(ITestMethod testMethod)
{
// code omitted for brevity (same as above)
}
private TestResult[] Invoke(ITestMethod testMethod)
{
if (_testMethodAttribute != null)
return _testMethodAttribute.Execute(testMethod);
return new[] { testMethod.Invoke(null) };
}
}
You can use this attribute instead of TestClass:
C#
[STATestClass]
public class TestClass1
{
[TestMethod]
public void Test1()
{
Assert.AreEqual(ApartmentState.STA, Thread.CurrentThread.GetApartmentState());
}
[STATestMethod]
public void Test2()
{
Assert.AreEqual(ApartmentState.STA, Thread.CurrentThread.GetApartmentState());
}
[DataTestMethod]
[DataRow(1)]
[DataRow(2)]
public void Test3(int i)
{
Assert.AreEqual(ApartmentState.STA, Thread.CurrentThread.GetApartmentState());
}
}
All methods of the class are executed in an STA thread.
#Conclusion
MSTest v2 is easily extensible. In this post, I've shown how to run tests on an STA thread. There are many other scenarios, such as changing the culture of the current thread or repeating tests multiple times to verify stability. For example, you could create a Culture attribute to set the culture, or a RepeatTestMethod attribute to run the test N times or for a set duration. These extension points help consolidate shared behavior across your tests!
Do you have a question or a suggestion about this post? Contact me!