【ASP.NET Core】【xUnit】【Moq】Add unit tests 1

masanori_msl

Masui Masanori

Posted on September 13, 2020

【ASP.NET Core】【xUnit】【Moq】Add unit tests 1

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

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();
            });
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Product.cs

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; }
    }
}
Enter fullscreen mode Exit fullscreen mode

HomeController.cs

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();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

IProductService.cs

using System.Collections.Generic;
using System.Threading.Tasks;
using Models;

namespace Services
{
    public interface IProductService
    {
        Task<List<Product>> GetProductsAsync();
    }
}
Enter fullscreen mode Exit fullscreen mode

ProductService.cs

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" }
            });
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

_Layout.cshtml

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8" />
    <title>@ViewData["Title"]</title>
</head>
<body>
    @RenderBody()
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Index.cshtml

<div>Hello World</div>
Enter fullscreen mode Exit fullscreen mode

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

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 
...
Enter fullscreen mode Exit fullscreen mode

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

dotnet new sln -n XUnit
dotnet sln add XUnitSample
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Alt Text

I add Moq to use mock.

XUnitSampleTest.csproj

<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>
Enter fullscreen mode Exit fullscreen mode

Create a test class

I add a test class for testing "HomeController".

HomeControllerTest.cs

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());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

I can get test results by "dotnet test".
Alt Text

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

So I add some extensions into Visual Studio Code.

There are two extensions what are named ".NET Core Test Explorer".
But I like this, because I don't need build projects and update tests manually before I execute tests.

Resources

💖 💪 🙅 🚩
masanori_msl
Masui Masanori

Posted on September 13, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related