Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using MS Test with .NET Core API

0.00/5 (No votes)
23 Jul 2017 1  
This article describes how to test your .NET core API using MS Test.

Introduction

.NET Core is now the default standard for any web application development in the .NET world. However, with every application development, it is important to unit test your code.

Fortunately, .NET core has excellent support for MS Test. This article describes how you can use MS Test to write unit test cases for your API.

Why MS Test, Why Not xUnit?

xUnit is an excellent test framework but I found it lacks features which we are used to over the years while writing test cases with MS Test. One of the major limitations with xunit is that it does not do anything similar to AssemblyInitialize, which is where we initialize anything before any of the test cases are executed.

I use AssemblyInitialize to register my DI for other global initialization. If you are like me who likes to initialize test projects, MS Test is the default choice.

.NET core has excellent support for MS Test.

Using the Code

I am using Visual Studio 2017 for creating my API project and writing MS Test cases. Follow the below steps to write MS Test cases:

  1. Add a new project of type "Unit Test Project (.NET Core)" to your solution.

  2. Add reference to other projects in your solution.
  3. Add nuget package "Microsoft.AspNetCore.TestHost". Add "Moq" nuget package if you would like to use mocking.

  4. Create a copy of Startup.cs from your main API project into your test project. I have renamed it to "TestStartup.cs" just so that I don't accidentally touch the main Startup.cs.
  5. Add a TestInitializer.cs (rename the class to whatever you like) in your test project.
  6. Add the logic to start test server and register the mock repositories.
    [TestClass]
    public class TestInitializer
    {
        public static HttpClient TestHttpClient;
        public static Mock<IEmployeeRepository> MockEmployeeRepository;
    
        [AssemblyInitialize]
        public static void InitializeTestServer(TestContext testContext)
        {
            var testServer = new TestServer(new WebHostBuilder()
               .UseStartup<TestStartup>()
               // this would cause it to use StartupIntegrationTest class
               // or ConfigureServicesIntegrationTest / ConfigureIntegrationTest
               // methods (if existing)
               // rather than Startup, ConfigureServices and Configure
               .UseEnvironment("IntegrationTest"));
    
            TestHttpClient = testServer.CreateClient();
        }
    
        public static void RegisterMockRepositories(IServiceCollection services)
        {
            MockEmployeeRepository = (new Mock<IEmployeeRepository>());
            services.AddSingleton(MockEmployeeRepository.Object);
    
            //add more mock repositories below
        }
    }
    
  7. From "ConfigureServices" of TestStartup.cs, call method to initialize your repositories.
    public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            ....
            //Mock your repositories.
            TestInitializer.RegisterMockRepositories(services);
            ....
        }
    
  8. Write your first test case:
    [TestClass]
    public class TestEmployee
    {
        [TestMethod]
        public void TestGetAllEmployees()
        {
            var mockEmps = new List<Employee>();
            mockEmps.Add(new Employee
            { EmpId = 1, FirstName = "F1", LastName = "L1", Email = "F1.L1@tt.com" });
            mockEmps.Add(new Employee
            { EmpId = 2, FirstName = "F2", LastName = "L2", Email = "F2.L2@tt.com" });
    
            var employeeRepositoryMock = TestInitializer.MockEmployeeRepository;
            employeeRepositoryMock.Setup
                  (x => x.GetEmployees()).Returns(Task.FromResult(mockEmps));
    
            var response = TestInitializer.TestHttpClient.GetAsync("api/Employees").Result;
    
            var resp = response.Content.ReadAsStringAsync().Result;
            var responseData = JsonConvert.DeserializeObject<List<Employee>>(resp);
            Assert.AreEqual(3, responseData.Count);
            Assert.AreEqual(mockEmps[0].EmpId, responseData[0].EmpId);
        }
    }
    
  9. You can now debug/run your test case and view the results in Test Explorer.

Ponts of Interest

It is a good practice not to have any other code in controllers that make the call to your business logic layer. People tend to write test cases just for their business logic as they feel the controller doesn't have any code. I prefer writing test cases that call the controller as it traverses the full hierarchy.

You can download the sample code from below this github link.

One major limitiation is that Visual Studio doesn't support code coverage for .NET core projects, which is detailed at this link. It appears that Update 3 of Visual Studio 2017 does support code coverage but I didn't get a chance to download and play with Update 3. One option to get code coverage for .NET core projects is to use open source opencover.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here