Unit Testing - mcbride-clint/DeveloperCurriculum GitHub Wiki

Overview

Unit testing can be a very important tool in longevity and complexity of an application. Verifying that code behaves as planned is a very important part of programming but testing your entire application whenever you make a change can be very taxing and error prone without an automated testing system.

Unit Testing is more than just "testing" your application's code. It is a way of organizing your code that will allow it to be testable by breaking your logic down into discrete units of code.

Visual Studio makes it easy to write, track, run, and debug tests. It has a window called the Test Explorer that lists all found tests in the solution. It allows you to one, many, or all tests and it will track the last result of each test.

There are several Unit Testing frameworks compatible with Visual Studio. The default framework is MSTest. To add unit tests to a solution, add a MSTest Test Project and any tests in the MSTest project will show in the Test Explorer.

Basics

Creating a Test

In order for test methods to be found and run each class and method must be marked with TestClass and TestMethod Attributes.


[TestClass]
public class TestGroup {
  [TestMethod]
  public void Test1 () {
  }
  [TestMethod]
  public void Test2 () {
  }
}

Naming

When naming a test method is is good practice to summarize what method you are testing, the inputs or situation you are testing, and the expected outcome . For example, if you are testing a ValidatePrice(decimal price) function to test what it does when provided with a price that is zero, then you could name the function Test_ValidatePrice_Given_Zero_Expect_ArgumentOutOfRangeException. You can always leave comments to further clarify the intent of the test but providing some context in the method name will help to differentiate it from similarly named functions to avoid a ValidatePriceTest1, ValidatePriceTest2, ValidatePriceTest3, etc. situation.

Designing Test Method Contents

When designing a unit test it can be useful, for consistency, to follow the AAA pattern. Arrange, Act, Assert.

Arrange

First you should try to setup the scenario that is in test. Here you should declare any variables that you need to prepare your test. Some tests can be as simple as creating an input and expected output variables while other situations could call for a more complex setup such as user roles and objects being passed in.

If you are experiencing difficulty in setting up a scenario to test, then that could be a strong hint that the method that you want to test could be doing too much and could be refactored to limit the extent of your tests.

Act

Next you should call the method that you are actually testing. This should be just a single line of code to execute the target method given the inputs that you already set up and receive the output.

If the test requires multiple calls to several methods then that could be a strong hint that your test is too large in scope and you are actually testing several connected methods.

Assert

Finally you verify that the output of the method you tested matches what you expect given the inputs you provided. This can be accomplished via a set of methods that from the Assert static class. Most of these methods have an expected and an actual parameter input that it will test and You can have any number of assertion statements and if any one of them fail then the test fails.

Examples


[TestMethod]
public void Test_ValidatePrice_Given_Valid_Price_Expect_True()
{
  // Arrange 
  decimal testPrice = 12.11;
  bool expected = true;

  // Act 
  bool actual = ValidatePrice(testPrice);

  // Assert
  Assert.AreEqual(expected, actual);
}

[TestMethod]
public void Test_ValidatePrice_Given_Zero_Expect_ArgumentOutOfRangeException()
{
  // Arrange 
  decimal testPrice = 0;

  // Act / Assert (In this case the Act and Assert are done on the same line)
  // Ensures the ValidatePrice function will throw the ArgumentOutOfRangeException as expected.
  // Any other exceptions or results will cause the test to fail.
  Assert.ThrowsException<ArgumentOutOfRangeException>(() => ValidatePrice(testPrice));
}

Executing Tests

Visual Studio

When using Visual Studio, there is a Test Explorer tool window that will automatically catalog and track all tests that are present in the solution. Using this window you can see a list of all tests and the most recent result.

Command Line

From a command line within the root solution directory, you can call dotnet test and it will automatically locate, run, and report back the results of all tests in the solution. This can be useful when creating an automatic Continuous Integration and Continuous Deployment Pipeline and want all tests to execute before and code is deployed.

See Also