【ASP.NET Core】【xUnit】【Moq】Add unit tests 1
Masui Masanori
Posted on September 13, 2020
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();
});
}
}
}
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; }
}
}
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();
}
}
}
IProductService.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using Models;
namespace Services
{
public interface IProductService
{
Task<List<Product>> GetProductsAsync();
}
}
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" }
});
}
}
}
_Layout.cshtml
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>@ViewData["Title"]</title>
</head>
<body>
@RenderBody()
</body>
</html>
Index.cshtml
<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.
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
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
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>
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());
}
}
}
I can get test results by "dotnet test".
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.
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
- Home > xUnit.net
- Getting started: .NET Core with command line > xUnit.net
- Moq : Mocking Framework for .NET - Qiita
- GitHub - moq/moq4: Repo for managing Moq 4.x
- Unit testing C# code in .NET Core using dotnet test and xUnit - .NET Core | Microsoft Docs
- Test controller logic in ASP.NET Core | Microsoft Docs
- c# - What's the idiomatic way to verify collection size in xUnit? - Stack Overflow
Posted on September 13, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.