Unit testing in .Net Core has been made simple. It has been designed with testability in mind. TDD approach has paid rich dividends for the developers who follow them religiously.
Unit tests gives you the confidence and faith in your code, catches errors early in the cycle and overall, improves productivity levels across the team. There is a upfront cost with unit testing but, it's worth the cost and effort.
This code sample unit tests a typical CRUD web api controller. This code sample is compiled on .Net Core 2.0 and .Net Core Standard 2.0. We used xUnit as testing framework and test runner, Moq for mocking dependencies.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
| using AutoMapper;
using Moq;
using VisualStudio99.Data.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using VisualStudio99.Api.Controllers;
using Xunit;
using VisualStudio99.Common.Dto;
using VisualStudio99.Tests.Data;
using VisualStudio99.Data;
using VisualStudio99.Data.Mappers;
namespace VisualStudio99.Tests
{
public class EmployeeControllerTests
{
private readonly Mock<IEmployeeRepository> mockRepo;
private readonly Mock<EmployeeMapper> mockMapper;
private readonly EmployeeController controller;
public EmployeeControllerTests()
{
mockRepo = new Mock<IEmployeeRepository>();
mockMapper = new Mock<EmployeeMapper>();
controller = new EmployeeController(mockRepo.Object, mockMapper.Object);
}
[Fact]
public async Task Post_ReturnsOkResult_WhenCreatingValidEmployee()
{
// Arrange & Act
mockRepo.Setup(repo => repo.Add(It.IsAny<Employee>())).Verifiable();
mockRepo.Setup(repo => repo.SaveAsync()).ReturnsAsync(1);
mockRepo.Setup(repo => repo.AnyAsync(It.IsAny<Expression<Func<Employee, bool>>>())).Returns(Task.FromResult(false));
// Act
var result = await controller.Post(EmployeeData.GetNewEmployee());
//Assert
mockRepo.Verify(repo => repo.Add(It.IsAny<Employee>()), Times.Once);
mockRepo.Verify(repo => repo.SaveAsync(), Times.Once);
var createdAtRouteResult = Assert.IsType<CreatedAtActionResult>(result);
var employee = Assert.IsType<EmployeeDto>(createdAtRouteResult.Value);
Assert.Equal("TestEmployee", employee.Name);
}
[Fact]
public async Task Post_ReturnsBadRequest_WhenUpdatingInvalidNullEmployee()
{
// Act
var result = await controller.Post(null);
//Assert
mockRepo.Verify(repo => repo.Add(It.IsAny<Employee>()), Times.Never);
Assert.IsType<BadRequestObjectResult>(result);
}
[Fact]
public async Task Post_ReturnsBadRequest_WhenAddingExistingEmployee()
{
mockRepo.Setup(repo => repo.AnyAsync(It.IsAny<Expression<Func<Employee, bool>>>())).Returns(Task.FromResult(true));
// Act
var result = await controller.Post(EmployeeData.GetNewEmployee());
//Assert
mockRepo.Verify(repo => repo.Add(It.IsAny<Employee>()), Times.Never);
Assert.IsType<BadRequestObjectResult>(result);
}
[Fact]
public async Task Get_Returns_All_Employees()
{
// Arrange
mockRepo.Setup(repo => repo.GetAsync()).Returns(Task.FromResult(EmployeeData.GetEmployees()));
// Act
var result = await controller.GetAllAsync();
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
Assert.IsType<List<EmployeeDto>>(okResult.Value);
mockRepo.Verify(repo => repo.GetAsync(), Times.AtMostOnce);
}
[Theory]
[InlineData(1)]
public async Task Get_ReturnsHttpOkResult_ForValidEmployeeId(int empId)
{
// Arrange
string employeeName = "testEmployee";
mockRepo.Setup(repo => repo.GetAsync(It.IsAny<Expression<Func<Employee, bool>>>())).Returns(Task.FromResult(EmployeeData.GetEmployees().Where(t => t.EmpId == empId).ToList()));
// Act
var result = await controller.Get(empId);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var emp = Assert.IsType<List<EmployeeDto>>(okResult.Value).FirstOrDefault();
mockRepo.Verify(repo => repo.GetAsync(It.IsAny<Expression<Func<Employee, bool>>>()), Times.Once);
Assert.True(emp.EmpName == employeeName);
}
[Fact]
public async Task Get_ReturnsBadRequestResult_ForInValidEmpId()
{
// Arrange
var empId = -1;
mockRepo.Setup(repo => repo.GetAsync(It.IsAny<Expression<Func<Employee, bool>>>())).Returns(Task.FromResult(EmployeeData.GetEmployees().Where(t => t.EmpId == empId).ToList()));
// Act
var result = await controller.Get(empId);
// Assert
Assert.IsType<BadRequestObjectResult>(result);
mockRepo.Verify(repo => repo.GetAsync(), Times.Never);
}
[Fact]
public async Task Put_ReturnsBadRequestResult_WhenUpdatingInvalidNullEmployee()
{
// Arrange & Act
// Act
var result = await controller.Put(null);
//Assert
mockRepo.Verify(repo => repo.Update(It.IsAny<Employee>()), Times.Never);
Assert.IsType<BadRequestObjectResult>(result);
}
[Fact]
public async Task Put_ReturnsNotFoundRequest_WhenUpdatingInvalidEmployee()
{
// Arrange & Act
mockRepo.Setup(repo => repo.AnyAsync(It.IsAny<Expression<Func<Employee, bool>>>())).Returns(Task.FromResult(false));
// Act
var result = await controller.Put(new EmployeeDto()
{
EmpId = 99
});
//Assert
mockRepo.Verify(repo => repo.Update(It.IsAny<Employee>()), Times.Never);
Assert.IsType<NotFoundResult>(result);
}
[Fact]
public async Task Put_ReturnsOkResult_WhenUpdatingValidEmployee()
{
// Arrange & Act
mockRepo.Setup(repo => repo.Update(It.IsAny<Employee>())).Verifiable();
mockRepo.Setup(repo => repo.SaveAsync()).ReturnsAsync(1);
mockRepo.Setup(repo => repo.AnyAsync(It.IsAny<Expression<Func<Employee, bool>>>())).Returns(Task.FromResult(true));
// Act
var result = await controller.Put(new EmployeeDto()
{
EmpId = 1
});
//Assert
mockRepo.Verify(repo => repo.Update(It.IsAny<Employee>()), Times.Once);
mockRepo.Verify(repo => repo.SaveAsync(), Times.Once);
Assert.IsType<NoContentResult>(result);
}
[Fact]
public async Task Delete_ReturnsHttpBadRequest_ForInvalidEmployee()
{
// Arrange
int empId = 99;
mockRepo.Setup(repo => repo.AnyAsync(It.IsAny<Expression<Func<Employee, bool>>>())).Returns(Task.FromResult(false));
// Act
var result = await controller.Delete(empId);
// Assert
Assert.IsType<BadRequestResult>(result);
}
[Fact]
public async Task Delete_ReturnsNoContentResult_ForValidEmployee()
{
// Arrange
int empId = 99;
mockRepo.Setup(repo => repo.AnyAsync(It.IsAny<Expression<Func<Employee, bool>>>())).Returns(Task.FromResult(true));
mockRepo.Setup(repo => repo.SaveAsync()).ReturnsAsync(1);
// Act
var result = await controller.Delete(empId);
// Assert
mockRepo.Verify(repo => repo.Delete(It.IsAny<int>()), Times.Once);
mockRepo.Verify(repo => repo.SaveAsync(), Times.Once);
Assert.IsType<NoContentResult>(result);
}
}
}
|