In the last module we learned how to implement the repository pattern and created a Vehicle Repository for the application. Now that the Vehicle controller is using an interface to access the Vehicle data we can create some unit tests for its HTTP GET method.
Create a Vehicles Controller Test: CanReturnDefaultPage
Create a new folder called Controllers on the root of the FredsCarsAPI.Tests project. In the new folder create a class file called VehiclesControllerTests.cs and fill it with the contents below.
FredsCarsAPI.Tests/Controllers/VehiclesControllerTests.cs
using Moq;
using MockQueryable.Moq;
using FredsCarsAPI.Controllers;
using FredsCarsAPI.Repositories;
using FredsCarsAPI.Models;
using FredsCarsAPI.Models.DTOs;
using FredsCarsAPI.Data;
namespace FredsCarsAPI.Tests.Controllers
{
public class VehiclesControllerTests
{
private List<Vehicle> _testData =
new List<Vehicle>
{
new Vehicle { Id = 1,
Status = Status.New,
Year = "2022",
Make = "M1",
Model = "M1",
Color = "C1",
Price = 64000,
VIN = "123",
VehicleTypeId = 1,
VehicleType = new VehicleType { Id = 1, Name = "Car" }
},
new Vehicle { Id = 2,
Status = Status.New,
Year = "2022",
Make = "M2",
Model = "M2",
Color = "C2",
Price = 64000,
VIN = "456",
VehicleTypeId = 2,
VehicleType = new VehicleType { Id = 2, Name = "Truck" }
},
new Vehicle { Id = 3,
Status = Status.New,
Year = "2022",
Make = "M3",
Model = "M3",
Color = "C3",
Price = 64000,
VIN = "789",
VehicleTypeId = 3,
VehicleType = new VehicleType { Id = 3, Name = "Jeep" }
},
new Vehicle { Id = 4,
Status = Status.New,
Year = "2022",
Make = "M4",
Model = "M4",
Color = "C4",
Price = 64000,
VIN = "012",
VehicleTypeId = 1,
VehicleType = new VehicleType { Id = 1, Name = "Car" }
},
new Vehicle { Id = 5,
Status = Status.New,
Year = "2022",
Make = "M5",
Model = "M5",
Color = "C5",
Price = 64000,
VIN = "345",
VehicleTypeId = 2,
VehicleType = new VehicleType { Id = 2, Name = "Truck" }
},
new Vehicle { Id = 6,
Status = Status.New,
Year = "2022",
Make = "M6",
Model = "M6",
Color = "C6",
Price = 64000,
VIN = "678",
VehicleTypeId = 3,
VehicleType = new VehicleType { Id = 3, Name = "Jeep" }
},
new Vehicle { Id = 7,
Status = Status.New,
Year = "2022",
Make = "M7",
Model = "M7",
Color = "C7",
Price = 64000,
VIN = "901",
VehicleTypeId = 1,
VehicleType = new VehicleType { Id = 1, Name = "Car" }
},
new Vehicle { Id = 8,
Status = Status.New,
Year = "2022",
Make = "M8",
Model = "M8",
Color = "C8",
Price = 64000,
VIN = "234",
VehicleTypeId = 2,
VehicleType = new VehicleType { Id = 2, Name = "Truck" }
},
new Vehicle { Id = 9,
Status = Status.New,
Year = "2022",
Make = "M9",
Model = "M9",
Color = "C9",
Price = 64000,
VIN = "567",
VehicleTypeId = 3,
VehicleType = new VehicleType { Id = 3, Name = "Jeep" }
},
new Vehicle { Id = 10,
Status = Status.New,
Year = "2022",
Make = "M10",
Model = "M10",
Color = "C10",
Price = 64000,
VIN = "890",
VehicleTypeId = 1,
VehicleType = new VehicleType { Id = 1, Name = "Car" }
},
new Vehicle { Id = 11,
Status = Status.New,
Year = "2022",
Make = "M11",
Model = "M11",
Color = "C11",
Price = 64000,
VIN = "abc",
VehicleTypeId = 2,
VehicleType = new VehicleType { Id = 2, Name = "Truck" }
},
new Vehicle { Id = 12,
Status = Status.New,
Year = "2022",
Make = "M12",
Model = "M12",
Color = "C12",
Price = 64000,
VIN = "def",
VehicleTypeId = 3,
VehicleType = new VehicleType { Id = 3, Name = "Jeep" }
},
new Vehicle { Id = 13,
Status = Status.New,
Year = "2022",
Make = "M13",
Model = "M13",
Color = "C13",
Price = 64000,
VIN = "ghi",
VehicleTypeId = 1,
VehicleType = new VehicleType { Id = 1, Name = "Car" }
},
new Vehicle { Id = 14,
Status = Status.New,
Year = "2022",
Make = "M14",
Model = "M14",
Color = "C14",
Price = 64000,
VIN = "jkl",
VehicleTypeId = 2,
VehicleType = new VehicleType { Id = 2, Name = "Truck" }
},
};
[Fact]
public async Task CanReturnDefaultPage()
{
// Arrange
// Create the mock from the test data
Mock<IVehicleRepository> mockVehicleRepo =
new Mock<IVehicleRepository>();
var mockVehicleIQueryable =
_testData.AsQueryable().BuildMock();
mockVehicleRepo.Setup(m => m.Vehicles).Returns(mockVehicleIQueryable);
var controller = new VehiclesController(mockVehicleRepo.Object);
// Act
ApiResult<VehicleDTO> result = await controller.GetVehicles();
// Assert
Assert.True(result.Data.Count == 10);
Assert.True(result.Data[0].Id == 1
&& result.Data[9].Id == 10);
}
}
}
Run the CanReturnDefaultPage test in Test Explorer
Right click on the CanReturnDefaultPage test in Test Explorer and select Run.

The test should pass and and display a green checkmark.

Note: When I introduced this test, CanReturnDefaultPage, into the project, I was surprised to run into a little trouble. I could right click a test and run it in Test Explorer with no problem. But if I tried to run all of the test it would hang and I would have to kill the testhost.exe in the Task Manager. But it seemed to be ok if I ran all tests. If you ever run into trouble in Test Explorer you can use the command line as an alternative. You can run all tests using the dotnet test
command like we learned in module 24, or you can filter tests and run a specific one as I show in the next section using the dotnet test
command’s --filter
switch.
However, what worked seemed to work for me in getting Test Explorer running smoothly again was updatating the xunit.runner.visualstudio package to version 2.5.4. You can do this in the Nuget Package Manager or through the command line with the following command in a command prompt pointed to the FredsCarsAPI.Tests project.dotnet add package xunit.runner.visualstudio --version 2.5.4
Run the CanReturnDefaultPage test with DotNet CLI: dotnet test –filter switch
We can also run the single test from the command line with the dotnet test
CLI command we saw in the module 24. Open a command line and navigate to the FredsCarsAPI.Tests project and run the following command.
dotnet test --filter "FullyQualifiedName=FredsCarsAPI.Tests.Controllers.VehiclesControllerTests.CanReturnDefaultPage"
The results should look similar to the following.

This time in the DotNet CLI we used the dotnet test
command but here we also used the --filter
switch to specify the test to run. After the filter switch we have in quotes FullyQualifiedName=[path to test].
This should be the namespace of the class followed by the class name followed by the test name all separated by dots.
namespaceOfTest.classNameOfTest.TesName
Now let’s return to the actual test code itself and inspect what we did there.
The first thing we did in the VehiclesControllerTests class is setup some Vehicle test data as a List<Vehicle>
and assign it to a private variable called _testData
.
Next we created a test method for our first Vehicles controller test called CanReturnDefaultPage
and made it asynchronous.
In the next arrange section, the first three lines use a combination of the original moq
package to mock an IVehicleRepository
in a variable called mockVehicleRepo
and the new MockQueryable.Moq
extension for moq
to convert our test data to a mock IQueryable
in a variable called mockVehicleIQueryable
. The third line sets up the Mock VehicleRepository
to have its Vehicles IQueryable
Property return the Mock Vehicle IQueryable
test data.
Finally at the end of the arrange section we instantiate a new Vehicles controller and pass to its constructor the Mock Vehicle Repository which now contains the Mock IQueryable
of test data and assign it to a variable called controller.
In the act section we simply call the GetVehicles
method of the controller and assign the result to a variable named result. The controller should now have everything it needs to return an ApiResult since we passed it the Vehicle Repository with test data.
We then make two assertions in the assert section.
Number one, we assert that the Count of VehicleDTO objects returned in the Data property of the ApiResult will be 10. This is because we did not specify any values for the optional parameters pageIndex and pageSize for the GetVehicles method in the Vehicles controller. The default values are 0 and 10 respectively. So we should expect a pageSize of 10 since that is the default value for pageSize.
Next we assert that the first (index 0) and tenth or last (index 9) VehicleDTO object Ids in the ApiResult Data property are 1 and 10. If we look back at the _testData data in the VehiclesControllerTests class, this would be the case using the default pageIndex and pageSize values where pageIndex of 0 means grab the first page of pageSize (10 in this case) records.
Create Vehicles Controller Test 2: CanReturnSelectedPage
In the VehiclesControllerTests.cs file, add the second test by modifying its contents with the following in bold blue font.
FredsCarsAPI.Tests/Controllers/VehiclesControllerTests.cs
/*** existing code ***/
[Fact]
public async Task CanReturnDefaultPage()
{
// Arrange
// Create the mock from the test data
Mock<IVehicleRepository> mockVehicleRepo =
new Mock<IVehicleRepository>();
var mockVehicleIQueryable =
_testData.AsQueryable().BuildMock();
mockVehicleRepo.Setup(m => m.Vehicles).Returns(mockVehicleIQueryable);
var controller = new VehiclesController(mockVehicleRepo.Object);
// Act
ApiResult<VehicleDTO> result = await controller.GetVehicles();
// Assert
Assert.True(result.Data.Count == 10);
Assert.True(result.Data[0].Id == 1
&& result.Data[9].Id == 10);
}
[Fact]
public async Task CanReturnSelectedPage()
{
// Arrange
// Create the mock from the test data
Mock<IVehicleRepository> mockVehicleRepo =
new Mock<IVehicleRepository>();
var mockVehicleIQueryable =
_testData.AsQueryable().BuildMock();
mockVehicleRepo.Setup(m => m.Vehicles).Returns(mockVehicleIQueryable);
var controller = new VehiclesController(mockVehicleRepo.Object);
// Act
ApiResult<VehicleDTO> result =
await controller.GetVehicles(3, 3);
// Assert
Assert.True(result.Data.Count == 3);
Assert.True(result.Data[0].Id == 10
&& result.Data[2].Id == 12);
}
}
}
In the test code above for our second test, where we are testing to see if we can return a specific page, the arrange section is the same as the first test. We build up our mocks for the Vehicle Repository and IQueryable<Vehicle>
that the repository returns.
The act section is also very similar except that it passes pageIndex and pageSize values to the controller’s GetVehicles
method.
In the assert section we are checking that the page we specified in the act section is indeed returned.
Our assertions here are also very similar to the first test.
We assert that the data count is 3 as we specify in the controller method call of the act section.
And then we assert that the first record (index 0) and the last record (index 2, the last of three records) have Ids of 10 and 12.
This should be true according to our _testData field contents as a pageIndex of 3 is the fourth page. So we skip 3 X 3 = 9 records and start on the Id of 10. And we take 3 records to end at an Id of 12.
Create Vehicles Controller Test 3: CanReturnPagingInfo
In the VehiclesControllerTests.cs file add the last test as shown below in bold blue font.
FredsCarsAPI.Tests/Controllers/VehiclesControllerTests.cs
/*** existing code ***/
[Fact]
public async Task CanReturnPagingInfo()
{
// Arrange
// Create the mock from the test data
Mock<IVehicleRepository> mockVehicleRepo =
new Mock<IVehicleRepository>();
var mockVehicleIQueryable =
_testData.AsQueryable().BuildMock();
mockVehicleRepo.Setup(m => m.Vehicles).Returns(mockVehicleIQueryable);
var controller = new VehiclesController(mockVehicleRepo.Object);
// Act
ApiResult<VehicleDTO> result =
await controller.GetVehicles(1, 5);
// Assert
Assert.True(result.Data.Count == 5);
Assert.True(result.PageIndex == 1);
Assert.True(result.PageSize == 5);
Assert.True(result.TotalCount == 14);
Assert.True(result.TotalPages == 3);
}
}
}
In the code above for our third test, where we are testing for the correct return of paging info, the arrange and act sections are again very similar to the first two tests. The only difference being that we are now passing different pageIndex and pageSize values; this time 1 and 5 respectively.
In the assert section we check the PageIndex and PageSize properties of the result are what we specified in the Vehicles controller GetVehicles
call, the total count of data in the repository is 14 which is indeed the number of Vehicles we have in our private _testData field, and that the total page count is 3 (total count / pageSize) = (14 / 5 rounded up).
Run the Vehicles controller tests
Now in Test Explorer, if you right click on the VehiclesControllerTests group and select Run, all three Vehicle controller tests will pass and show a green check mark.

What’s Next
We’ve been working very hard since module 23 to rework our paging to work on the server side and have refactored our Web API project to isolate all of our components to make testing easier and to try and give all of our components a single responsibility. Along the way we have implemented some popular software patterns namely Dependency Injection and the Repository pattern.
It took a little work but we have a solid architecture at this point for paging and should be able to incorporate server-side sorting relatively easy. And that is what we’ll turn to in the next module. We are going to implement server-side sorting and put back that functionality we temporarily dismantled so we could concentrate on paging.