【ASP.NET Core】【xUnit】【Moq】Add unit tests 1
Tags: aspnetcore, csharp, xunit
Published: 2020-09-13 00:34:58

## Intro
I try add unit tests in an ASP.NET Core project.
In this time, I use xUnit and Moq.

### Environments
* .NET Core ver.3.1.402
* xUnit ver.2.4.0
* Moq ver.4.14.5

### Base project
I created an ASP.NET Core project by "dotnet new empty -n XUnitSample" and added some classes.

#### Startup.cs
```csharp
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Services;

namespace XUnitSample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();
            services.AddControllers();
            services.AddScoped<IProductService, ProductService>();
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseRouting();
            app.UseStaticFiles();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}
```
#### Product.cs
```csharp
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Models
{
    [Table("Product")]
    public class Product
    {
        [Key]
        public int Id { get; set; }
        public string? ModelName { get; set; }
    }
}
```
#### HomeController.cs
```csharp
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Models;
using Services;

namespace Controllers
{
    public class HomeController: Controller
    {
        private readonly IProductService _product;
        public HomeController(IProductService product)
        {
            _product = product;
        }
        [Route("/")]
        public IActionResult Index()
        {
            ViewData["Title"] = "Home";
            return View("Views/Index.cshtml");
        }
        [Route("/Products/All")]
        public async Task<List<Product>> GetAllProducts()
        {
            return await _product.GetProductsAsync();
        }
    }
}
```
#### IProductService.cs
```csharp
using System.Collections.Generic;
using System.Threading.Tasks;
using Models;

namespace Services
{
    public interface IProductService
    {
        Task<List<Product>> GetProductsAsync();
    }
}
```
#### ProductService.cs
```csharp
using System.Collections.Generic;
using System.Threading.Tasks;
using Models;

namespace Services
{
    public class ProductService: IProductService
    {
        public async Task<List<Product>> GetProductsAsync()
        {
            return await Task.FromResult(new List<Product>{
                new Product { Id = 0, ModelName = "SampleModel" }
            });
        }
    }
}
```
#### _Layout.cshtml
```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8" />
    <title>@ViewData["Title"]</title>
</head>
<body>
    @RenderBody()
</body>
</html>
```
#### Index.cshtml
```html
<div>Hello World</div>
```

## Add a test project
First I add a test project.
But I can't add it into the exist project or I will get error.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lz0bc81ll8yssozfsdcv.png)

```
obj\Debug\netcoreapp3.1\.NETCoreApp,Version=v3.1.AssemblyAttributes.cs(4,12): error CS0579: Duplicate 'global::System.Runtime.Versioning.TargetFrameworkAttribute' attribute [C:\Users\example\OneDrive\Documents\workspace\Dotnet31\AspNetCore31Sample\AspNetCore31Sample.csproj]
obj\Debug\netcoreapp3.1\AspNetCore31Sample.AssemblyInfo.cs(13,12): error CS0579: Duplicate 'System.Reflection.AssemblyCompanyAttribute' attribute 
...
```

So I must create a solution and put these projects into it.

```
dotnet new sln -n XUnit
dotnet sln add XUnitSample
```

* [dotnet sln command - .NET Core CLI | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-sln)

### Add xUnit project
Now I can create a test project with "xunit" template.

```
dotnet new xunit -n XUnitSampleTest
dotnet sln add XUnitSampleTest
dotnet add reference ../XUnitSample/XUnitSample.csproj
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qv6xhwg7mgrn3lkvca3l.png)

I add Moq to use mock.

#### XUnitSampleTest.csproj
```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <IsPackable>false</IsPackable>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0"/>
    <PackageReference Include="xunit" Version="2.4.0"/>
    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0"/>
    <PackageReference Include="coverlet.collector" Version="1.2.0"/>
    <PackageReference Include="Moq" Version="4.14.5"/>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\XUnitSample\XUnitSample.csproj"/>
  </ItemGroup>
</Project>
```

### Create a test class
I add a test class for testing "HomeController".

#### HomeControllerTest.cs
```csharp
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Models;
using Moq;
using Services;
using Xunit;

namespace Controllers
{
    public class HomeControllerTest
    {
        private readonly HomeController _target;
        public HomeControllerTest()
        {
            var productMock = new Mock<IProductService>();
            productMock.Setup(p => p.GetProductsAsync())
                .ReturnsAsync(new List<Product>
                {
                    new Product
                    {
                        Id = 1,
                        ModelName = "ModelTest"
                    }
                });
            _target = new HomeController(productMock.Object);
        }
        [Fact]
        public async Task GetAllProductsReturnsOneItem()
        {
            Assert.True((await _target.GetAllProducts()).Count == 1);
        }
        [Fact]
        public void IndexReturnsView()
        {
            Assert.IsType<ViewResult>(_target.Index());
        }
        [Fact]
        public void PageTitleIsHome()
        {
            var page = _target.Index();
            var viewResult = Assert.IsType<ViewResult>(page);
            Assert.Equal("Home", viewResult.ViewData["Title"].ToString());
        }
    }
}
```

I can get test results by "dotnet test".
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/b1dwz7144f0zmj0lbqly.png)

## Executing tests with Visual Studio Code
Although I don't have any problems the result of "dotnet test" command, I still want UI like below.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ulpk3vlupu6iobe60khu.png)

So I add some extensions into Visual Studio Code.

* [Test Explorer UI - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer#:~:text=The%20Test%20Explorer%20UI%20%28which%20is%20the%20main,the%20current%20test%20suite%20in%20the%20Status%20Bar.)
* [.NET Core Test Explorer - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=formulahendry.dotnet-test-explorer)

There are two extensions what are named ".NET Core Test Explorer".
But I like [this](https://marketplace.visualstudio.com/items?itemName=formulahendry.dotnet-test-explorer), because I don't need build projects and update tests manually before I execute tests.

## Resources
* [Home > xUnit.net](https://xunit.net/)
* [Getting started: .NET Core with command line > xUnit.net](https://xunit.net/docs/getting-started/netcore/cmdline)
* [Moq : Mocking Framework for .NET - Qiita](https://qiita.com/usamik26/items/42959d8b95397d3a8ffb)
* [GitHub - moq/moq4: Repo for managing Moq 4.x](https://github.com/Moq/moq4)
* [Unit testing C# code in .NET Core using dotnet test and xUnit - .NET Core | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-dotnet-test)
* [Test controller logic in ASP.NET Core | Microsoft Docs](https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/testing?view=aspnetcore-3.1)
* [c# - What's the idiomatic way to verify collection size in xUnit? - Stack Overflow](https://stackoverflow.com/questions/46653557/whats-the-idiomatic-way-to-verify-collection-size-in-xunit)