Wednesday, November 15, 2017

.Net Core Web Api CRUD Controller unit testing code sample

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);
        }
       
    }
}