Unit testing – Creating unit tests using the MSTestv2 framework in Visual Studio

Unit testing – Creating unit tests using the MSTestv2 framework in Visual Studio

Suppose you are working on a protocol and you are creating a precompile QAction that contains functionality that will be used by other QActions. To ensure that the functionality provided by this precompile QAction is correct, you want to create unit tests.

This blog post is part of a series on unit testing:

1. Introduction to unit testing
2. Creating unit tests using the MSTestv2 framework in Visual Studio
3. Using isolation frameworks
4. Writing testable code
5. FluentAssertions
6. Data-driven tests
7. Test life cycle attributes
8. Using files in unit tests

In the following example, a precompile QAction with ID 1 was created that, among other things, provides the class PathRewriter. This PathRewriter class defines the Rewrite method, for which a unit test is needed.

In Visual Studio, you can easily generate a unit test: put the cursor on the method, right-click, and select “Create Unit Tests” in the context menu.

This will open a window where you can configure things such as the test framework to use, the name format of the test class, etc. As all the default settings are OK for now, simply click “OK” to continue.

After clicking “OK”, you will see that a new project has been added to the solution; in this case, “QAction_1Tests”.

This project contains the file PathRewriterTests.cs that defines the test class PathRewriterTests:

[TestMethod()]
public void Rewrite_PathWithItemsToAbbreviate_ReturnsAbbreviatedPath()
{
// Arrange
PathRewriter pathRewriter = new PathRewriter();
string path = @"Visios\Customers\Skyline\Protocols\Test";
string expected = @"V\C\Skyline\P\Test";
// Act
string result = pathRewriter.Rewrite(path);
// Assert
Assert.AreEqual(expected, result);
}

The PathRewriterTests class has been annotated with a TestClass attribute. This attribute indicates that this class contains test methods.

The RewriteTest method has been annotated with the TestMethod attribute to indicate that it is a test method.

These attributes are defined by the MSTest framework (in the namespace Microsoft.VisualStudio.TestTools.UnitTesting). Other useful attributes are:

  • AssemblyInitializeAttribute and AssemblyCleanupAttribute: these identify the method that contains code to be executed before or after all tests in the assembly have run, respectively.
  • ClassInitializeAttribute and ClassCleanupAttribute: these identify the method that contains code to be executed before or after all tests in this test class have run, respectively.
  • TestInitializeAttribute and TestCleanupAttribute: these identify the method that contains code that must be executed before or after each test in this test class, respectively.
  • IgnoreAttribute: used to indicate that a specific test should not run. 
  • DescriptionAttribute: used to provide a description for the test.
  • TestCategoryAttribute: specifies a category for the unit test.
  • TimeoutAttribute: provides a timeout for the test.
  • DataTestMethodAttribute: used to specify a data-driven test where data can be specified inline.
  • DataRowAttribute: defines inline data for a test method.
  • ExpectedExceptionAttribute: specifies the type of exception this test is expected to throw.

For more information about these attributes and a complete overview of all attributes that can be used, visit the Microsoft Documentation platform.

Visual Studio also provides a Test Explorer, which gives an overview of all your tests in the solution. You can open the Test Explorer in Visual Studio from the menu bar: go to Test Windows Test Explorer.

From the Test Explorer, you can easily run a specific test, run all tests, create a playlist of tests, etc.

For more information about the Test Explorer, refer to:

At this point, Visual Studio has created the test class and method, but it is up to you to implement the test method.

To implement a test method, typically the triple-A (AAA) pattern is used. This means that the method body consists of three separate parts:

  • Arrange: the setup of everything you need to perform the tests.
  • Act: performing the action that you want to test.
  • Assert: verifying whether the result is what you expected.

In the current example, the “Arrange” step consists of creating an instance of the class, the “Act” step is invoking the Rewrite method on that instance, and the “Assert” step is verifying whether the actual output matches the expected output.

[TestMethod()]
public void Rewrite_PathWithItemsToAbbreviate_ReturnsAbbreviatedPath()
{
// Arrange
PathRewriter pathRewriter = new PathRewriter();
string path = @"Visios\Customers\Skyline\Protocols\Test";
string expected = @"V\C\Skyline\P\Test";
// Act
string result = pathRewriter.Rewrite(path);
// Assert
Assert.AreEqual(expected, result);
}

The Assert class defines multiple methods, such as AreEqualIsFalse, and IsTrue, that can be used to make assertions. For more information about the Assert class, visit the Microsoft Documentation platform.

Now you are ready to execute the test! This can be done in the Test Explorer by, for example, clicking “Run All”. After a successful execution, everything should be green.

In case a test fails, a red “X” will be displayed. Clicking on the line that represents the test in the Test Explorer will provide more information about what failed.

When writing unit tests, you typically want to provide tests for different scenarios. For example, you could also want to write an additional test to verify whether an ArgumentNullException is thrown in case the Rewrite method is invoked with a null reference as the parameter value.

This can be done as follows: The ExpectedException attribute can be used to indicate the type of exception that you expect.

[TestMethod()]
[ExpectedException(typeof(ArgumentNullException))]
public void Rewrite_NullPath_ThrowsArgumentNullException()
{
// Arrange
PathRewriter pathRewriter = new PathRewriter();
// Act
pathRewriter.Rewrite(null);
// Assert
Assert.Fail("No ArgumentNullException thrown.");
}

Note that the pattern used for the test method names consists of three parts separated by an underscore. The parts define the following:

  • The name of the method that is being tested
  • The condition or scenario you want to test
  • The expected result

This allows you to know what it tested, the tested scenario, and the expected result, just from the test method name.

In some cases, you may want to execute the same test but with different input. In such cases, the DataRowAttribute attribute can be used:

[DataTestMethod()]
[DataRow("pattern 1")]
[DataRow("pattern 2")]
public void ValidatePattern_ValidInput_ReturnsTrue(string input)
{
// Arrange
var myClass = new MyClass();
// Act
bool isValid = myClass.ValidatePattern(input);
// Assert
Assert.IsTrue(isValid);
}

Useful links


More blog posts on unit testing

Leave a Reply