mirror of https://github.com/abpframework/abp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.1 KiB
6.1 KiB
ASP.NET Core MVC Tutorial - Part III
About this Tutorial
This is the third part of the tutorial series. See all parts:
- Part I: Create the project and a book list page
- Part II: Create, Update and Delete books
- Part III: Integration Tests (this tutorial)
You can download the source code of the application from here.
Test Projects in the Solution
There are two test projects in the solution:
Acme.BookStore.Application.Tests
is for unit & integration tests. You can write tests for application service methods. It uses EF Core SQLite in-memory database.Acme.BookStore.Web.Tests
is for full stack integration tests including the web layer. So, you can write tests for UI pages too.
Test projects use the following libraries for testing:
- xunit as the main test framework.
- Shoudly as an assertion library.
- NSubstitute as a mocking library.
Adding Test Data
Startup template contains the BookStoreTestDataBuilder
class in the Acme.BookStore.Application.Tests
project that creates some data to run tests on. It's shown below:
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.Threading;
namespace Acme.BookStore
{
public class BookStoreTestDataBuilder : ITransientDependency
{
private readonly IIdentityDataSeeder _identityDataSeeder;
public BookStoreTestDataBuilder(IIdentityDataSeeder identityDataSeeder)
{
_identityDataSeeder = identityDataSeeder;
}
public void Build()
{
AsyncHelper.RunSync(BuildInternalAsync);
}
public async Task BuildInternalAsync()
{
await _identityDataSeeder.SeedAsync("1q2w3E*");
}
}
}
- It simply uses
IIdentityDataSeeder
which is implemented by the identity module and creates an admin role and admin user. You can use them in your tests. - You can add new test data in the
BuildInternalAsync
method.
Change the BookStoreTestDataBuilder
class as show below:
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Volo.Abp.Threading;
namespace Acme.BookStore
{
public class BookStoreTestDataBuilder : ITransientDependency
{
private readonly IIdentityDataSeeder _identityDataSeeder;
private readonly IRepository<Book, Guid> _bookRepository;
public BookStoreTestDataBuilder(
IIdentityDataSeeder identityDataSeeder,
IRepository<Book, Guid> bookRepository)
{
_identityDataSeeder = identityDataSeeder;
_bookRepository = bookRepository;
}
public void Build()
{
AsyncHelper.RunSync(BuildInternalAsync);
}
public async Task BuildInternalAsync()
{
await _identityDataSeeder.SeedAsync("1q2w3E*");
await _bookRepository.InsertAsync(
new Book
{
Id = Guid.NewGuid(),
Name = "Test book 1",
Type = BookType.Fantastic,
PublishDate = new DateTime(2015, 05, 24),
Price = 21
}
);
await _bookRepository.InsertAsync(
new Book
{
Id = Guid.NewGuid(),
Name = "Test book 2",
Type = BookType.Science,
PublishDate = new DateTime(2014, 02, 11),
Price = 15
}
);
}
}
}
- Injected
IRepository<Book, Guid>
and used it in theBuildInternalAsync
to create two book entities.
Testing the BookAppService
Create a test class named BookAppService_Tests
in the Acme.BookStore.Application.Tests
project:
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Xunit;
namespace Acme.BookStore
{
public class BookAppService_Tests : BookStoreApplicationTestBase
{
private readonly IBookAppService _bookAppService;
public 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 == "Test book 1");
}
}
}
Should_Get_List_Of_Books
test simply usesBookAppService.GetListAsync
method to get and check the list of users.
Add a new test that creates a valid new 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 throws anAbpValidationException
.
Testing Web Pages
TODO