There are multiple versions of this document. Pick the options that suit you best.

UI
Database

Web Application Development Tutorial - Part 4: Integration Tests

About This Tutorial

In this tutorial series, you will build an ABP based web application named Acme.BookStore. This application is used to manage a list of books and their authors. It is developed using the following technologies:

  • Entity Framework Core as the database provider.
  • Blazor Server as the UI Framework.

This tutorial is organized as the following parts;

Download the Source Code

This tutorial has multiple versions based on your UI and Database preferences. We've prepared a few combinations of the source code to be downloaded:

If you encounter the "filename too long" or "unzip" error on Windows, please see this guide.

Test Projects in the Solution

This part covers the server side tests. There are several test projects in the solution:

bookstore-test-projects-v2

Test projects slightly differs based on your UI and Database selection. For example, if you select MongoDB, then the Acme.BookStore.EntityFrameworkCore.Tests will be Acme.BookStore.MongoDB.Tests.

Each project is used to test the related project. Test projects use the following libraries for testing:

The test projects are configured to use SQLite in-memory as the database. A separate database instance is created and seeded (with the data seed system) to prepare a fresh database for every test.

Adding Test Data

If you had created a data seed contributor as described in the first part, the same data will be available in your tests. So, you can skip this section. If you haven't created the seed contributor, you can use the BookStoreTestDataSeedContributor to seed the same data to be used in the tests below.

Testing the BookAppService

Create a test class named BookAppService_Tests in the Books folder of the Acme.BookStore.Application.Tests project:

using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Modularity;
using Xunit;
using System;
using Volo.Abp.Validation;
using System.Linq;

namespace Acme.BookStore.Books;

public abstract class BookAppService_Tests<TStartupModule> : BookStoreApplicationTestBase<TStartupModule>
    where TStartupModule : IAbpModule
{
    private readonly IBookAppService _bookAppService;

    protected BookAppService_Tests()
    {
        _bookAppService = GetRequiredService<IBookAppService>();
    }

    [Fact]
    public async Task Should_Get_List_Of_Books()
    {
        //Act
        var result = await _bookAppService.GetListAsync(
            new PagedAndSortedResultRequestDto()
        );

        //Assert
        result.TotalCount.ShouldBeGreaterThan(0);
        result.Items.ShouldContain(b => b.Name == "1984");
    }
}

Add a new implementation class of BookAppService_Tests class, named EfCoreBookAppService_Tests in the EntityFrameworkCore\Applications\Books namespace (folder) of the Acme.BookStore.EntityFrameworkCore.Tests project:

using Acme.BookStore.Books;
using Xunit;

namespace Acme.BookStore.EntityFrameworkCore.Applications.Books;

[Collection(BookStoreTestConsts.CollectionDefinitionName)]
public class EfCoreBookAppService_Tests : BookAppService_Tests<BookStoreEntityFrameworkCoreTestModule>
{

}
  • Should_Get_List_Of_Books test simply uses BookAppService.GetListAsync method to get and check the list of books.
  • We can safely check the book "1984" by its name, because we know that this books is available in the database since we've added it in the seed data.

Add a new test method to the BookAppService_Tests class that creates a new valid book:

[Fact]
public async Task Should_Create_A_Valid_Book()
{
    //Act
    var result = await _bookAppService.CreateAsync(
        new CreateUpdateBookDto
        {
            Name = "New test book 42",
            Price = 10,
            PublishDate = DateTime.Now,
            Type = BookType.ScienceFiction
        }
    );

    //Assert
    result.Id.ShouldNotBe(Guid.Empty);
    result.Name.ShouldBe("New test book 42");
}

Add a new test that tries to create an invalid book and fails:

[Fact]
public async Task Should_Not_Create_A_Book_Without_Name()
{
    var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
    {
        await _bookAppService.CreateAsync(
            new CreateUpdateBookDto
            {
                Name = "",
                Price = 10,
                PublishDate = DateTime.Now,
                Type = BookType.ScienceFiction
            }
        );
    });

    exception.ValidationErrors
        .ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
}
  • Since the Name is empty, ABP will throw an AbpValidationException.

The final test class should be as shown below:

using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Modularity;
using Xunit;
using System;
using Volo.Abp.Validation;
using System.Linq;

namespace Acme.BookStore.Books;

public abstract class BookAppService_Tests<TStartupModule> : BookStoreApplicationTestBase<TStartupModule>
    where TStartupModule : IAbpModule
{
    private readonly IBookAppService _bookAppService;

    protected BookAppService_Tests()
    {
        _bookAppService = GetRequiredService<IBookAppService>();
    }

    [Fact]
    public async Task Should_Get_List_Of_Books()
    {
        //Act
        var result = await _bookAppService.GetListAsync(
            new PagedAndSortedResultRequestDto()
        );

        //Assert
        result.TotalCount.ShouldBeGreaterThan(0);
        result.Items.ShouldContain(b => b.Name == "1984");
    }

    [Fact]
    public async Task Should_Create_A_Valid_Book()
    {
        //Act
        var result = await _bookAppService.CreateAsync(
            new CreateUpdateBookDto
            {
                Name = "New test book 42",
                Price = 10,
                PublishDate = DateTime.Now,
                Type = BookType.ScienceFiction
            }
        );

        //Assert
        result.Id.ShouldNotBe(Guid.Empty);
        result.Name.ShouldBe("New test book 42");
    }

    [Fact]
    public async Task Should_Not_Create_A_Book_Without_Name()
    {
        var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
        {
            await _bookAppService.CreateAsync(
                new CreateUpdateBookDto
                {
                    Name = "",
                    Price = 10,
                    PublishDate = DateTime.Now,
                    Type = BookType.ScienceFiction
                }
            );
        });

        exception.ValidationErrors
            .ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
    }
}

Open the Test Explorer Window (use Test -> Windows -> Test Explorer menu if it is not visible) and Run All tests:

bookstore-appservice-tests

Congratulations, the green icons indicates that the tests have been successfully passed!


Was this page helpful?

Please make a selection.

To help us improve, please share your reason for the negative feedback in the field below.

Please enter a note.

Thank you for your valuable feedback!

Please note that although we cannot respond to feedback, our team will use your comments to improve the experience.

In this document
Community Talks

Building Modular Monolith Applications Using .NET and ABP Framework

17 Oct, 17:00
Online
Watch the Event
Mastering ABP Framework Book
Mastering ABP Framework

This book will help you gain a complete understanding of the framework and modern web application development techniques.

Learn More