MsTest framework

What is MsTest framework?

MsTest framework is a unit testing framework for the ASP .NET framework that allows us to write automated tests without the help of any third-party tools or libraries. It’s one of the most useful test frameworks out there and it comes along with Visual Studio IDE by default. MsTest framework has various options like different test attributes, data-driven tests, assertion library ability to execute the tests in parallel, etc. It can be used for client-side or server-side components. MsTest can be integrated with tools like Selenium for automated UI tests as well.

In this post, I will go through some of the most important characteristics MSTest frameworks and how you can successfully utilize them in a test script.

List of attributes

Let’s start by explaining all the attributes MsTest has. The attributes are special keywords that allow us to structure and run the tests as well as group the tests by specific group names. The keyword itself should be written inside the squared brackets [ ] so that MsTest automatically knows what kind of test attributes is and how to treat the method with that attribute.

The list of all the available attributes MsTest framework has are:

  • [TestMethod] – Assigns a test method
  • [TestClass] – Assigns a test class
  • [TestInitialize] – Method with this attribute will be executed before each test
  • [TestCleanup] – Method with this attribute will be executed after each test
  • [ClassInitialize] – Method with this attribute will be executed only once before executing the tests within the same test class
  • [ClassCleanup] – Method with this attribute will be executed only once after executing the tests within the same test class
  • [AssemblyInitialize] – Method with this attribute will be executed only once before executing all the tests in the whole test repo
  • [AssemblyCleanup] – Method with this attribute will be executed only once after executing all the tests in the whole test repo
  • [TestProperty] – Set explanatory data to a test, for example, the kind of test or the test name, etc.
  • [DataRow] – Configures a data-driven test by providing a specified set of input
  • [Ignore] – Ignore a test so that when the execution starts, MsTest automatically ignores that one during execution
  • [TestCategory(“”)] – Group test by specific name

Test workflow

Let’s try putting all of the above-mentioned attributes in a test. For this example, we will use the MsTest framework in integration with Selenium and will try automating some scenarios for www.qamind.com.

Open Visual Studio IDE and create a unit test project. Inside the project, we will create a Base Class folder and class which will contain the two assembly methods that will be executed only once for all of the test cases.

Let’s create a method for setting up the ChromeDriver. This will initialize the driver for Chrome once called.

private static IWebDriver GetChromeDriver()
{
    IWebDriver driver = new ChromeDriver();
    return driver        
}

We will write another method that maximizes the browser window.

public static void Maximize()
{
    ObjectRepository.Driver.Manage().Window.Maximize();
}

Another method for navigating to a specific URL passed as an argument.

public static void NavigateToURL(string url)
{
    ObjectRepository.Driver.Navigate().GoToUrl(url);
}

Now, let’s create the [AssemblyInitialize] method called InitializeDriver() .

[AssemblyInitialize]
public static void InitializeDriver(TestContext tc)
{
    GetChromeDriver();
    Maximize();
}

This method will run first before every single test in our test repo. It will initialize the chrome driver, open a Chrome browser and it will maximize the window.

The last method that will be included in the BaseClass will be the TearDown() method which will quit the driver that is running in the background. (The driver object is created by using the property value IWebDriver Driver {get; set;})

[AssemblyCleanup]
public static void TearDown()
{
    if(Driver != null)
    {
        Driver.Quit();
    }
}

Now, let’s create our test class. We will try to click on a button and validate some text once navigated to the particular page.

[TestClass]
public class ClickOnButtonAndVerifyTextClass
{
    [ClassInitialize]
    public static void NavigateToWebsite(TestContext tc)
    {
        NavigateToURL("https://www.qamind.com");
    }

    [ClassCleanup]
    public static void CloseBrowser()
    {
        Driver.Close()
    }

    [TestMethod]
    public void ClickOnButtonAndVerifyText()
    {
        LinkHelper.ClickOnHyperlink(By.CssSelector("[id='menu-item-2325']"));
        string actualAboutMeTitle = GetElementText(By.CssSelector(".post-   content h3"));
        AreEqual(actualAboutMeTitle, EXPECTED_ABOUT_ME_TITLE);
    }
}

We assign the class as a test class and inside that class, we are creating a test method by using the above-mentioned test attributes. Before all of the tests inside this class are executed the NavigateToWebsite() method will be executed before them but after the InitializeDriver() assembly method. After navigating to the website, the test is getting executed and after the test has finished executing, the CloseBrowser() method will close the browser window, and the TearDown() assembly method will quit the driver.

The ClickOnHyperlink() method comes from the LinkHelper class and GetElementText() is a static one that comes from different class. That allows us to click on an element passed as an argument and get the text of an element. (I will not go through all of the wrapped methods for element manipulation, therefore you can find what you need to know about Selenium at the official selenium website)

After we store that element text in a variable, we are using some of the built-in MsTest assertions to check the value. I will go into detail about assertion later in this post.

If for example instead of [ClassInitialize] and [ClassCleanup] we put [TestInitialize] and [TestCleanup], those methods will execute after each and every test method within that test class. So, if you work with only one set of data it’s a good practice to use the class initialization and cleanup approach. But if you need different sets of data for different types of validations, I would suggest using the test initialization and cleanup approach for sure. (Note: The test execution will last longer though, just have that in mind)

The output from the execution:

MsTest framework
Test Output

Let’s write 4 simple tests that only prints something in the console.

[TestMethod]
[TestProperty("First Test", "Test1")]
public void Test1()
{
    Console.WriteLine("Test1");
}

[TestMethod]
[TestCategory("Group 1")]
public void Test2()
{
    Console.WriteLine("Test2");
}

[TestMethod]
[TestCategory("Group 1")]
public void Test3()
{
    Console.WriteLine("Test3");
}

[TestMethod]
[Ignore]
public void Test4()
{
    Console.WriteLine("Test4");
}

The output from running all of the tests:

MsTest framework
Test Output

The last test “Test4” has the [Ignore] attribute and therefore, it is ignored during execution. The first test “Test1” has the [TestCategory] attribute which includes some additional metadata for the tests.

The second and the third test are group by “Group 1”. If we group the tests by Traits in the Test Explorer we would see:

MsTest framework
Grouped tests by traits

This is nice when we have a large number of tests that can be grouped by page or functionality and can be run as a separate regression suite.

Data Driven tests

Let’s say that we need to perform the same set of steps but with different input data. Instead of having a large number of tests, we can use the so-called parameterization approach where we can have one test that is executed N times depending on how many sets of inputs we have. Consider this approach similar to the TestNG parameterization approach for data-driven tests.

There can be many approaches regarding data-driven testing. We can fetch data from CSV, XML, or XLSX files or we can dynamically fetch the data by creating a so-called data Provider.

[TestMethod]
[DataSource(
"Microsoft.VisualStudio.TestTools.DataSource.CSV",
         @"PATH_TO_THE_CSV_FILE",
"FILE_NAME#csv", 
DataAccessMethod.Sequential)]
public void DataDriveTestCsv()
{
    By locator = By.Id(TestContext.DataRow["TabMenu"].ToString());
    IWebElement element = GenericHelper.GetElement(locator);
    string elementText = element.Text;
    AreEqual(elementText, TestContext.DataRow["MenuText"].ToString());
}
[DataTestMethod]
[DynamicData(nameof(GetValues), DynamicDataSourceType.Method)]
public void DynamicDataTest(int a, int b, int expected)
{
 var actualValue = Calc.Add(a, b);
 AreEqual(expected, actual);
}
public static IEnumerable<object[]> GetValues()
{
 yield return new object[] { 12, 2, 14 };
 yield return new object[] { 21, 19, 40 };
}

Both approaches are valid approaches and can be used depending on your test automation strategy.

Assertions

The list of all assertions that MsTest framework provides is pretty long so I’ll just name a few of them that are often used.

  • Assert.AreEqual(expectedValue, actualValue); – Values should be equal
  • Assert.AreNotEqual(expectedValue, actualValue); – Values should not be equal
  • Assert.IsTrue(actualValue); – Condition is true
  • Assert.IsTrue(actualValue); – Condition is false
  • Assert.IsNull(actualValue); – Value is null
  • Assert.IsNotNull(actualValue); – Value is not null
  • StringAssert.Contains(expectedValue, “text”); – Expected string value contains a sub string value
  • StringAssert.StartsWith(expectedValue, “text”); – Expected string value starts with a sub string value
  • CollectionAssert.AreEqual(expectedCollectionValues, actualCollectionValues); – Both collections are equal (same elements, total num of elements and ordering)
  • CollectionAssert.AreNotEqual(expectedCollectionValues, actualCollectionValues); – Collections are not equal (Does not have the same elements | total num of elements | ordering)
  • CollectionAssert.AreEquivalent(expectedCollectionValues, actualCollectionValues); – Both collections have the same elements
  • CollectionAssert.AreNotEquivalent(expectedCollectionValues, actualCollectionValues); – Both collections does not have the same elements
  • CollectionAssert.Contains(expectedCollectionValues, “text”); – Expected collection contains an actual element
  • CollectionAssert.DoesNotContain(expectedCollectionValues, “text”); – Expected collection does not contains an actual element

Conclusion

Consider MsTest as TestNG framework but for C# .NET. Supported by Microsoft, MsTest is a powerful tool for automating unit tests but it can be used for another type of automatization especially UI automation integrated with Selenium for E2E coverage.

Share This Post

Latest Posts