xUnit Write ASP. NET Core Unit Test Method

  • 2021-11-13 07:13:03
  • OfStack

Remember ASP. NET WebForm. NET Framework? In those days, it was a disaster to do unit testing at Web layer. . NET Core learns lessons and is designed with testability in mind. Even ASP. NET Core applications such as Web or API are convenient for unit testing. Among them, interface-oriented and dependency injection play a very important role in this respect.

This article will teach you how to use xUnit to unit test ASP. NET Core application. . NET Core commonly used test tools are NUnit and MSTest. I am used to using xUnit as a test tool, so this article uses xUnit.

Create a sample project

First, build an application with ASP. NET Core API template.

The template automatically created an ValuesController for us, and for the convenience of demonstration, we only left one Get method:


public class ValuesController : ControllerBase
{
 // GET api/values/5
 [HttpGet("{id}")]
 public ActionResult<string> Get(int id)
 {
  return "value";
 }
}

Then add another xUnit unit test project:

The template automatically adds an xUnit reference for us:


<ItemGroup>
 <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
 <PackageReference Include="xunit" Version="2.4.0" />
 <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
</ItemGroup>

But to test the ASP. NET Core application, you need to add two more NuGet packages:

Install-Package Microsoft.AspNetCore.App

Install-Package Microsoft.AspNetCore.TestHost

Of course, target projects should be introduced. The last quote goes like this:


<ItemGroup>
 <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.5" />
 <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.1" />
 <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
 <PackageReference Include="xunit" Version="2.4.0" />
 <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
</ItemGroup>

<ItemGroup>
 <ProjectReference Include="..\WebApplication1\WebApplication1.csproj" />
</ItemGroup>

After adding the reference, compile 1 and make sure there is no problem with the reference.

Writing unit tests

There are generally three steps to writing unit test 1: Arrange, Act, and Assert.

Arrange is the preparation stage, which is the preparation work, such as simulating data and initializing objects;

Act is the behavior stage, which uses the prepared data to call the method to be tested;

Assert is the assertion phase, which compares the value returned by the calling target method with the expected value. If it is 1 with the expected value, the test will pass, otherwise it will fail.

Following this step, we write a unit test method, with Get method in ValuesController as the target to be tested. A unit test method is a test case.

We add an ValuesTests unit test class to the test project, and then write a unit test method with the following code:


public class ValuesTests
{
 public ValuesTests()
 {
  var server = new TestServer(WebHost.CreateDefaultBuilder()
   .UseStartup<Startup>());
  Client = server.CreateClient();
 }

 public HttpClient Client { get; }

 [Fact]
 public async Task GetById_ShouldBe_Ok()
 {
  // Arrange
  var id = 1;

  // Act
  var response = await Client.GetAsync($"/api/values/{id}");

  // Assert
  Assert.Equal(HttpStatusCode.OK, response.StatusCode);
 }
}

Here we get an HttpClient object through TestServer, with which we can simulate an Http request. We wrote a very simple test case that fully demonstrated the three steps of unit testing Arrange, Act, and Assert.

It is recommended that the method name of unit test use the pattern of "what should be what". For example, GetById_ShouldBe_Ok above means that the result returned by calling GetById, API, should be OK, so that you can know what your test case is for without too many comments.

Run unit tests

After writing the unit test case, open "Test Explore" (Chinese version of VS sees Chinese), right-click on the test method and select "Run Seleted Tests", or right-click in the method code block and select "Run Tests":

Pay attention to the color of the icon in front of the test method, which is blue at present, indicating that the test case has not been run yet.

After the test case is executed, if the result and expectation are 1, it will be a green icon:

If the result is not the same as expected, it will be a red icon, and then you need to modify the code until the green icon appears. You can see the execution time under "Test Explore", and you can also see the execution details in the Output window.

The above icon color change process is: blue, red, and then green. It is possible that blue will directly become green after one run, and it is also possible that red will become green after many times. This is how the term BRG (blue, red and green) comes from test-driven development.

Debug unit tests

You can debug in unit tests by adding breakpoints. The method is very simple. Right-click on the method to be debugged and select "Debug Seleted Tests". The debugging in peacetime is 1.

If we want to see exactly what API returns, we can see the variable string value of the returned result by debugging with breakpoints, but this is not the best choice. For example, for the same API, I want to see what the results returned by 10 parameters are, which is very troublesome to check through breakpoint debugging every time.

In addition to adding breakpoints for debugging, there is also a way to print logs for quick debugging, which xUnit can easily do. To this end, we modify 1ValuesTests:


public ValuesTests(ITestOutputHelper outputHelper)
{
 var server = new TestServer(WebHost.CreateDefaultBuilder()
  .UseStartup<Startup>());
 Client = server.CreateClient();
 Output = outputHelper;
}

public ITestOutputHelper Output{ get; }

// ... ( Omit other codes )

Here we add the ITestOutputHelper parameter to the constructor, and xUnit injects an instance that implements this interface. Once we get this instance, we can use it to output logs:


[Fact]
public async Task GetById_ShouldBe_Ok()
{
 // Arrange
 var id = 1;

 // Act
 var response = await Client.GetAsync($"/api/values/{id}");

 // Output
 var responseText = await response.Content.ReadAsStringAsync();
 Output.WriteLine(responseText);

 // Assert
 Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

Run (note that it is not Debug) this method. After running, you can see the word "Output" under "Test Explore". Click it to see the output result, as shown in the figure:

In this way, we can easily view the output results every time we run the test.

Others

Above, we call API test by simulating Http request, and another one is new. One Controller directly calls its Action method to test. For example:


// Arrange
var id = 1;
var controller = new ValuesController();
// Act
var result = controller.Get(id);

If Controller has no other dependencies, this method is of course the most convenient. However, Controller usually has one or more dependencies, such as this:


public class ValuesController : Controller
{
 private readonly ISessionRepository _sessionRepository;

 public ValuesController(ISessionRepository sessionRepository)
 {
  _sessionRepository = sessionRepository;
 }

 // ...
}

We have to simulate and instantiate all the dependencies of this Controller. Of course, it is unrealistic to simulate manually, because a dependency class may also depend on other classes or interfaces, and the dependency chain may be very long, so you can't instantiate every dependency manually. There is a tool called Moq that automatically simulates instantiation dependencies. Its usage is as follows:


// ..
// Arrange
var mockRepo = new Mock<ISessionRepository>();
mockRepo.Setup(...);
var controller = new HomeController(mockRepo.Object);

// Act
var result = await controller.Index();

I don't recommend this method, because regardless of the learning cost of Moq, it is important that it is not as close to the real calling scenario as simulating Http requests, so this article won't introduce it too much, as long as everyone knows that there is such a thing.

That's how xUnit writes ASP. NET Core unit tests. For more information about xUnit writing ASP. NET Core unit tests, please pay attention to other related articles on this site!


Related articles: