Skip to content
  • iImagine
  • Register
  • Log In

Web Development School

Learning made easy.

  • Books
    • Beginning Web Development with ASP.Net Core & Client-Side Technologies
      • TOC
      • Part 1
        • Chapter 1: Static HTML – Designing the landing page
      • Part 2
        • Chapter 2: ASP.Net Core – Let’s talk Dynamic
        • Chapter 3: Introduction to ASP.Net Core MVC
          [ASP.Net Core v9]
      • Part 4
        • Chapter 7: Using Server Side & Client Side technologies together
          [ASP.Net Core v7 & Angular 15]
  • Environment Setup
    • Installing Angular
    • Installing Visual Studio 2022
    • Installing SQL Server 2022 Express
    • Installing Postman
    • Installing Git for Windows
  • Blog
  • iImagine WebSolutions
  • Events
  • Learning Videos
  • Toggle search form

Unit Testing: Custom Server-Side Paging

We put in a lot of time and effort in the last module to make our Vehicle controller’s HTTP Get method clean, DRY, and testable. So in this module we are going to start unit testing the controller. We learn all about unit testing in Part 2 of this book. But, this will be a good review and a good place to put unit testing into use for the back-end of our full-stack implementation of FredsCars.

Note: The pathway I am using for development in this module is: C:\Development\FredsCars\FullStack\Module24.
Table Of Contents
  1. Choosing a Unit Testing Framework
  2. Create the Test Project
  3. Add a reference to the API project
  4. Identify components to test
  5. Unit Test the extension method
  6. Install the MockQueryable.Moq package
  7. Run the CanConvertVehiclesToDtos Test
    • Run the test with Test Explorer
    • Run the test from the command line.
    • Fix the Assert.IsType() error
  8. Unit Test the ApiResult class
  9. Unit Test the Vehicles Controller HTTP GET method
  10. What's Next

Choosing a Unit Testing Framework

.Net provides three unit testing Frameworks.

  • mstest
  • nUnit
  • xUnit

All of the .Net included test frameworks are very similar.
We are going to continue to use xUnit as we did in Part 2.

Create the Test Project

Right click the FredsCars solution folder in Solution Explorer and select Add --> New Project.

In the “Add a new project” dialogue, search for xunit.” Select the xUnit Test Project Type from the results and click Next. (Again make sure you pick the project type for C# and not VB or F#. Also make sure to select xUnit rather than nUnit.)

In the “Configure our new project” dialogue, name the project FredsCarsAPI.Tests and click the Next button. This is the convention for naming ASP.Net Core test projects.

[ProjectToBeTested].Tests

In the “Additional information” dialogue, select .Net 7.0 for the Framework and click Create.

At this point you should be able to see the new FredsCarsAPI.Tests project under the main solution in Solution Explorer

The test project creates a file called UnitTest1.cs and sets up a sample skeleton for your first test. We can go ahead and delete this file.

Add a reference to the API project

Within the new xUnit test project, we need to add a reference to the FredsCarsAPI project (the project to be tested) so we can mock or instantiate components and services we want to test.

Right click on the FredsCarsAPI.Tests project and select Add --> Project Reference.

In the Reference Manager dialogue, select the FredsCarsAPI project and click OK.

Within Solution Explorer you should now see the reference under Projects to the FredsCarsAPI project under the Dependencies section in the FredsCarsAPI.Tests project.


Identify components to test

At this point in the FredsCarsAPI project we really have three pieces of code we need to test.

  • The Vehicle controller itself.
  • The ApiResult class
  • The extension method to convert simple Vehicle objects to VehicleDTO objects.

The Vehicle Controller uses the ConvertVehiclesToDTOs extension method to prepare the query to send to the ApiResult class. It seems like our custom extension method is the bottom most dependency. So let’s start with that.

Unit Test the extension method

Create a new folder on the root of the FredsCarsAPI.Tests project called ExtensionMethods. In the new folder create a class file called ExtensionMethodTests.cs and fill it with the contents below.

FredsCarsAPI.Tests/ExtensionMethods/ExtensionMethodTests.cs

namespace FredsCarsAPI.Tests.ExtensionMethods
{
    public class ExtensionMethodTests
    {
        #region ConvertVehiclesToDTOs Tests
        [Fact]
        public void CanConvertVehiclesToDtos()
        {
            

        }
        #endregion
    }
}

I used the region preprocessor directive to mark the area where we will conduct tests specific to the ConvertVehiclesToDTOs() extension method.

Next we have set up a method called CanConvertVehiclesToDtos which describes the fact that we are testing the extension method to see if it in fact, “Can Convert Vehicles To Dtos”. We have applied the Fact attribute to the test method to mark it as an xUnit test.

The extension method we are testing, ConvertVehiclesToDTOs() takes in an IQueryable<Vehicle> as a parameter. This parameter can be thought of as a dependency of the method we are testing. In the real application, IQueryable<Vehicle> is built up by calling the Vehicles DbSet from the DbContext which returns the IQueryable. But we don’t want to work with a real DbContext and DbSet or the real database. We need to control the dependancy to ensure it is not causing any potential bugs of the actual component we are testing, in this case, the extension method.

We need a way to “fake” or “mock” the dependency. In part two we learned how to use a mocking tool called moq. Here, we are going to use a mocking package called MockQueryable.Moq which extends moq and makes it a little easier to work with IQueryables and DbSets called MockQueryable.

Install the MockQueryable.Moq package

Open a command prompt, navigate to the FredsCarsAPI.Tests directory and install the MockQueryable.Moq package by running the following commands.

cd C:\Development\FredsCars\FullStack\Module24\FredsCarsAPI.Tests
dotnet add package MockQueryable.Moq --version 7.0.0

Next, return to the ExtensionMethodTests.cs class file and modify its contents with the following.

FredsCarsAPI.Tests/ExtensionMethods/ExtensionMethodTests.cs

using FredsCarsAPI.Models;
using FredsCarsAPI.Models.DTOs;
using Microsoft.EntityFrameworkCore;
using MockQueryable.Moq;

namespace FredsCarsAPI.Tests.ExtensionMethods
{
    public class ExtensionMethodTests
    {
        #region ConvertVehiclesToDTOs Tests
        [Fact]
        public void CanConvertVehiclesToDtos()
        {
            // Arrange
            // 1 - create a List<T> with test items
            var Vehicles = 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" } }
            };
            
            // 2- Create the mock DbSet from the test data
            var mockVehiclesDbSet = Vehicles.AsQueryable().BuildMockDbSet();
            // 3- Convert the DbSet to an IQueryable interface
            var vehiclesIQueryable = mockVehiclesDbSet.Object.AsQueryable();

            // Act
            var vehicleDTOs = vehiclesIQueryable
                .Include(v => v.VehicleType)
                .ConvertVehiclesToDTOs();

            // Assert
            Assert.IsType<IQueryable<VehicleDTO>>(vehicleDTOs);
        }
        #endregion
    }
}

Run the CanConvertVehiclesToDtos Test

Run the test with Test Explorer

From the top menu in Visual Studio select View --> Test Explorer.

On the left side of the Test Explorer Window, expand the Test tree all the way down to the CanConvertVehiclesToDtos Test, right click on the test and select run.

At this point the test fails as shown in the following screen shot.

But that’s ok. That’s why we run tests. Right? To uncover any hidden or potential bugs in our code. However, we don’t always want to assume a test fails because of the application code. The error may lay in the test code itself. We might have to step back and re-evaluate the test. So let’s do that. Let’s inspect the test code above.

First of all note we are using the A/A/A method:

  • Arrange: set up objects and conditions we need for the test such as a list of test data.
  • Act: perform actions for the test
  • Assert: verify the expected outcome based on the result of Assert, Verify statements.

So, in the arrange section, the first thing we do is set up some test data as a List of Vehicles or a List<Vehicle> and assign it to a variable called Vehicles.

Next, we create a mock DbSet of Vehicles using List<T>.AsQueryable() method which converts a list to an IQueryable and then chain on the MockQueryable.Moq() method provided by the new MockQueryable.Moq package.

var mockVehiclesDbSet = Vehicles.AsQueryable().BuildMockDbSet();

Then we convert the mocked DbSet back to a mocked IQueryable of Vehicles and assign the result to a variable called vehiclesIQueryable.

In the act section we use the mocked vehiclesIQueryable variable to include VehicleType objects for each Vehicle object just as before in the actual application code so we can try and convert Vehicles to VehicleDTOs. Then we chain on with LINQ fluid API the actual ConvertVehiclesToDTOs extension method we are actually trying to test and assign the result to a variable called vehicleDTOs.

var vehicleDTOs = vehiclesIQueryable
    .Include(v => v.VehicleType)
    .ConvertVehiclesToDTOs();

Finally, in the assert section, we assert that vehicleDTOs is of type IQueryable<VehicleDTO> which would mean that the ConvertVehiclesToDTOs extension method worked.

Assert.IsType<IQueryable<VehicleDTO>>(vehicleDTOs);

Unfortunately at this point our test fails in the last screen shot shown above with a message of “Assert.IsType()” failure.

The reason given is that although the expected and actual object types are the same (FredsCarsAPI.Models.DTOs.VehicleDTO), the type of IEnumerable for that object are different. The expected IEnumerable type is System.Linq.IQueryable. And the actual IEnumerable type is MockQueryable.EntityFrameworkCore.TestAsyncEnumerableEfCore.

Run the test from the command line.

If you prefer, you can run the test from the command line to see the errors there with the following command. Just make sure your command prompt is pointing to the FredsCarsAPI.Tests project first.

dotnet test

Fix the Assert.IsType() error

Return once again to the ExtensionMethodTests.cs file and make the following modifications.

FredsCarsAPI.Tests/ExtensionMethods/ExtensionMethodTests.cs

/*** existing code ***/            

            // Act
            var vehicleDTOs = vehiclesIQueryable
                .Include(v => v.VehicleType)
                .ConvertVehiclesToDTOs();

            string typeName =
                vehicleDTOs.GetType().GetGenericArguments()[0].FullName;

            // Assert
            //Assert.IsType<IQueryable<VehicleDTO>>(vehicleDTOs);
            Assert.Equal(typeName, "FredsCarsAPI.Models.DTOs.VehicleDTO");
        }
        #endregion
    }
}

In the code above we have added a string variable called typeName and assigned to it only the FullName of the object we want to get back an IEnumerable for.

Then we comment out the Assert.IsType() statement and replace it with a regular Assert.Equal() statement this time just comparing the typeName of we just fetched to what we know it should be; FredsCarsAPI.Models.DTOs.VehicleDTO.

Now if you run the test again it should pass.

Unit Test the ApiResult class

If we look back at the ApiResult class, its factory method takes in an IQueryable<T> (where T should be VehicleDTO objects), a pageIndex, and a pageSize. The factory method then calls the ApiResult constructor which returns the five properties; Data, PageIndex, PageSize, TotalCount, and TotalPages. Let’s think of the Data property as a page (of data). And think of the other four properties as Paging Info.
Having reviewed the ApiResult class functionality, let’s ask ourselves, “What do we need to test for this class? Here is what I can come up with. Maybe you can come with some more at the end of this section and create some tests of your own.

  1. Can select a page.
  2. Can return paging info.

Create a new folder in the FredsCarsAPI.Tests project root called Data and in the new folder create a class file called ApiResultTests.cs and fill it with the contents below.

FredsCarsAPI.Tests/Data/ApiResultTests.cs

using FredsCarsAPI.Data;
using MockQueryable.Moq;

namespace FredsCarsAPI.Tests.Data
{
    public class TestPageData
    {
        public int Id { get; set; }
        public string Name { get; set; } = string.Empty;
    }

    public class ApiResultTests
    {
        private List<TestPageData> _data = new List<TestPageData>()
        {
            new TestPageData { Id = 1, Name = "a" },
            new TestPageData { Id = 2, Name = "b" },
            new TestPageData { Id = 3, Name = "c" },
            new TestPageData { Id =4, Name = "d" },
            new TestPageData { Id =5, Name = "e" },
            new TestPageData { Id =6, Name = "f" },
            new TestPageData { Id =7, Name = "g" },
            new TestPageData { Id =8, Name = "h" },
            new TestPageData { Id =9, Name = "i" },
            new TestPageData { Id =10, Name = "j" },
            new TestPageData { Id = 11, Name = "k" },
            new TestPageData { Id = 12, Name = "l" },
            new TestPageData { Id = 13, Name = "m" },
            new TestPageData { Id = 14, Name = "n" }
        };
                
        [Fact]
        public async Task CanSelectPage()
        {
            // Arrange
            // 1- Create the mock DbSet from the test data
            var dataDbSet = _data.AsQueryable().BuildMockDbSet();
            // 3- Convert the DbSet to an IQueryable interface
            var dataIQueryable = dataDbSet.Object.AsQueryable();

            // Act
            var response = await ApiResult<TestPageData>.CreatAsync(
                dataIQueryable, 2, 4);
                    
            // Assert
            // if returns 4 records; correct pagesize.
            Assert.Equal(4, response.Data.Count);
            // 1st and last data ids of page 3 (pageIndex of 2 + 1)
            //  should be 9 and 12 from testPageData.
            Assert.True(response.Data[0].Id == 9
                && response.Data[3].Id == 12);
        }

        [Fact]
        public async Task CanReturnPagingInfo()
        {
            // Arrange
            // 1- Create the mock DbSet from the test data
            var dataDbSet = _data.AsQueryable().BuildMockDbSet();
            // 3- Convert the DbSet to an IQueryable interface
            var dataIQueryable = dataDbSet.Object.AsQueryable();

            // Act
            var response = await ApiResult<TestPageData>.CreatAsync(
                dataIQueryable, 2, 4);

            // Assert
            // all assertions to match testPageData.
            Assert.Equal(14, response.TotalCount);
            Assert.Equal(4, response.TotalPages);
            Assert.Equal(2, response.PageIndex);
            Assert.Equal(4, response.PageSize);
        }
    }
}

If you run the tests in Test Explorer these two new tests along with the others should all pass.

Let’s take a look at the test code above.

Since our ApiResult class was designed to be generic, we created a class at the top of the file called TestPageData with two properties; Id, and Name. This is so we can test the generic capability with something other than VehicleDTO objects.

Within the ApiTestResultTests class, we first set up a List<TestPagedata> as paging test data with 14 records and assign it to a private variable called _data. We do this outside of any test methods so we can use the same test data in both tests and not have to build it up twice.

Next comes the first test, CanSelectPage() with the Fact attribute applied marking the method as a test. We have also marked the test method as asynchronous with async keyword so we can call the asynchronous ApiResult factory method.

In the Arrange section we create convert the List<TestPageData> to a mock IQueryable<TestPageData> using the now familiar MockQueryable.Moq package and apply it to a variable called dataIQueryable.

In the Act section we call the ApiResult method asynchronously using the await keyword passing it the mock IQueryable of TestPageData, a pageIndex of 2 (which is really page 3 since collections are 0 base indexed), and a pageSize of 4. We then assign the result to a variable called response.

In the Assert section we use the response to test if four records were returned matching the pageSize we passed to the ApiResult factory method using the Data.Count property of the response returned from the ApiResult constructor.
We then compare the first and last Ids of the test page data set returned which should be 9 and 12 for the third page of our test data. If all these assert conditions are true the test should pass. Which as we saw it has.

In the CanReturnPagingInfo() method we build up the mock IQueryable<TestPageData> in the same way as the previous test method and again assign it’s results to a variable called response.

In the Assert section we check that the TotalCount property of the response is 14 (the total count of our test data), the TotalPages property is 4 (TotalCount / pageSize rounded up), the PageIndex property is 2 (or the third page of data), and the PageSize property is 4 (the pageSize we passed in to the ApiResult call).

Unit Test the Vehicles Controller HTTP GET method

If we look at the GetVehicles() method in the controller, it takes in pageIndex and pageSize as parameters, builds up the dataQuery as an IQueryable<Vehicle> from the Vehicles DbSet of the DbContext, includes VehicleType information in the query, and then uses the ConvertVehiclesToDTOs() extension method to convert the IQueryable<Vehicle> to IQueryable<VehicleDTO>.

It can then use the built up dataQuery and pass it to the ApiResult factory method along with the incoming pageIndex and pageSize parameters.

We have already created Unit Tests for ConvertVehiclesToDTOs() extension method and the ApiResult class, both of which can be thought of as dependencies of the Vehicles controller GetVehicles() method since the Vehicle controller, “Depends” on these two components to function.

So, what exactly do we want to test in the Vehicles controller? I think the tests will be pretty similar to the tests we created for the ApiResult class. We want to be able to pass in pageIndex and pageSize parameters to the HTTP GET method, and return an ApiResult with the same five properties as before.

But, how do we go about this? We already know how to mock for the two dependencies we need to create the test. We learned that in the previous two sections.

The problem I see here is that dataQuery is declared and defined using the Vehicles DbSet of the DbContext. And the whole DbContext is received in the controller constructor through DI (Dependency Injection).

How can we isolate the Vehicles DbSet from the controller so we are only testing the controller and not the real DbSet and be sure the data is not introducing any potential bugs other then from the controller itself. Not to mentions we really don’t want to hit real data for a test when there could be millions of records out there. We want to use mocked data similar to the last round of testing.

Well, it turns out one of the main ways of isolating components in C# is through the use of interfaces.

I think it’s time to take another pit stop and do another round of refactoring in the Vehicles controller.

What’s Next

In the next module we are going to implement a pattern known as the Repository pattern. We are going to create a, “Vehicles Repository” and create a dependency on it in the Vehicles controller. This way when we go to test the Vehicles controller, we can mock a Vehicles DbSet as an interface and pass it in during the test.

< Prev
Next >

Leave a ReplyCancel reply

Chapter 1: Static HTML – Designing the landing page.

  • Static HTML – Designing the landing page.
  • Let’s get started!
  • Mock your site with HTML
  • Make CSS easy with Bootstrap
  • Mock your content
  • Introducing JavaScript
  • JavaScript Code Improvements
  • Results Data
  • Images and the HTML Image Element.
  • Revisiting Reusability for CSS and JavaScript
  • Reuse for HTML: PART 1
  • Reuse for HTML: PART 2
  • Details Page – Using a Bootstrap Component
  • Creating Links
  • Chapter One Conclusion

Chapter 2: ASP.Net Core – Let’s talk Dynamic

  • Introduction to ASP.Net Core
  • What is .Net?
  • What is ASP.Net
  • Introduction to Entity Framework Core

Chapter 3: ASP.Net MVC Core – Models, Views, and Controllers [ASP.Net Core v9]

  • Introduction to ASP.Net Core MVC
  • Create the project: ASP.Net Core MVC
  • Explore the ASP.Net Core Empty Web Project Template
  • Configure the Application for MVC
  • Create a Controller: Home Controller
  • Create a View: Index View for the Home Controller
  • Install Bootstrap using Libman
  • Create the Layout template
  • Create the Model
  • Install EF Core & Create the Database
  • Seed the Database: Loading test data
  • DI (Dependency Injection): Display a List of Vehicles
  • Repository Pattern: The Vehicles Repo
  • Unit Test 1: Home Controller Can Use Vehicle Repository
  • Unit Test 2: Vehicle Repository Can Return List
  • Add the ImagePath Migration and Thumbnail images to results
  • Pagination: Create a Custom Tag Helper
  • Sorting
  • Category Filter
  • Partial View: Break out the vehicle results
  • View Component: Create dynamic category buttons
  • Create the Details page
  • Create the Create Page

Chapter 7: Using Server Side & Client Side technologies together. [ASP.Net Core v7 & Angular v15]

  • Intro to Full Stack Development
  • Fred’s Cars – Full Stack Development
  • Prepare the environment
  • Create the Visual Studio Solution
  • Add the ASP.Net Core Web API project
  • Add the Angular Project
  • Wire it up!
  • WeatherForecast: Understanding the basics
  • Vehicles API Controller: Mock Data
  • Vehicles Angular Component: Consuming Data
  • Routing and Navigation
  • Using a Component Library: Angular Material
  • Our first Angular Material Component: MatToolbar
  • Configuring for Saas: CSS with superpowers
  • Create the Header & Footer components
  • Displaying Results with MatTable
  • Loading: Using a Progress Spinner
  • MatTable: Client-Side Paging and Sorting
  • MatSidenav: Create a Search Sidebar
  • MatCheckbox: Category Search UI
  • Adding an image to the welcome page
  • Create the database with Entity Framework Core migrations
  • MatPaginator & PageEvent: Custom Server-Side Paging
  • Unit Testing: Custom Server-Side Paging
  • Repository Pattern: VehicleRepository
  • Unit Test: Paging in the Vehicles controller
  • Server-Side Sorting
  • Unit Tests: Sorting
  • Filter (Quick Search)
  • Unit Tests: Filter feature
  • Advanced Search: Categories
  • Unit Tests: Search by category
  • Progress Spinner: Final Fix

TOC

  • What were WebForms?
  • Enter MVC
    • Understanding MVC
    • Advantages of MVC
  • ASP.Net Core MVC – A total rewrite
  • ASP.Net Core 2 MVC – Here come Razor Pages
    • Understanding Razor Pages
  • ASP.Net Core 3 – Dropping the MVC reference
    • Understanding Blazor
  • Dropping the MVC reference
  • Hello .Net 5!
  • What’s Next? – Here comes .Net 6.

Recent Posts

  • Angular Commands Cheat Sheet
  • Installing Git for Windows
  • Installing Postman
  • Installing SQL Server 2022 Express
  • Installing Visual Studio 2022

Recent Comments

No comments to show.

Archives

  • November 2023
  • October 2023
  • June 2023
  • October 2021

Categories

  • Angular
  • ASP.Net
  • Environment Setup
  • See All
  • SQL Server
  • Visual Studio
  • Web API & Rest Services

WordPress Theme Editor

Copyright © 2025 Web Development School.

Powered by PressBook Blog WordPress theme