From 788a62ab33093d56e8d7b674e6ac06526da04085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 5 Nov 2020 17:34:19 +0300 Subject: [PATCH] Update Testing.md --- docs/en/Testing.md | 141 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 133 insertions(+), 8 deletions(-) diff --git a/docs/en/Testing.md b/docs/en/Testing.md index 3612d4b604..e5c8611379 100644 --- a/docs/en/Testing.md +++ b/docs/en/Testing.md @@ -429,38 +429,163 @@ ABP Provides a complete infrastructure to write integration tests. All the ABP i #### The Database -The startup template is configured to use in-memory SQL database for the EF Core (for MongoDB, it uses [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) library). So, all the configuration and queries are performed against a real database and you can even test database transactions. +The startup template is configured to use **in-memory SQLite** database for the EF Core (for MongoDB, it uses [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) library). So, all the configuration and queries are performed against a real database and you can even test database transactions. -Using in-memory SQL database has two main advantages; +Using in-memory SQLite database has two main advantages; * It is faster compared to an external DBMS. * It create a **new fresh database** for each test case, so tests doesn't affect each other. -#### The Seed Data +> **Tip**: Do not use EF Core's In-Memory database for advanced integration tests. It is not a real DBMS and has many differences in details. For example, it doesn't support transaction and rollback scenarios, so you can't truly test the failing scenarios. On the other hand, In-Memory SQLite is a real DBMS and supports the fundamental SQL database features. + +### The Seed Data Writing tests against an empty database is not practical. In most cases, you need to some initial data in the database. For example, if you write a test class that query, update and delete the Products, it would be helpful to have a few products in the database before executing the test case. ABP's [Data Seeding](Data-Seeding.md) system is a powerful way to seed the initial data. The application startup template has a *YourProject*TestDataSeedContributor class in the `.TestBase` project. You can fill it to have an initial data that you can use for each test method. -#### AbpIntegratedTest Class +**Example: Create some Issues as the seed data** + +````csharp +using System.Threading.Tasks; +using MyProject.Issues; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; + +namespace MyProject +{ + public class MyProjectTestDataSeedContributor + : IDataSeedContributor, ITransientDependency + { + private readonly IIssueRepository _issueRepository; + + public MyProjectTestDataSeedContributor(IIssueRepository issueRepository) + { + _issueRepository = issueRepository; + } + + public async Task SeedAsync(DataSeedContext context) + { + await _issueRepository.InsertAsync( + new Issue + { + Title = "Test issue one", + Description = "Test issue one description", + AssignedUserId = TestData.User1Id + }); + + await _issueRepository.InsertAsync( + new Issue + { + Title = "Test issue two", + Description = "Test issue two description", + AssignedUserId = TestData.User1Id + }); + + await _issueRepository.InsertAsync( + new Issue + { + Title = "Test issue three", + Description = "Test issue three description", + AssignedUserId = TestData.User1Id + }); + + await _issueRepository.InsertAsync( + new Issue + { + Title = "Test issue four", + Description = "Test issue four description", + AssignedUserId = TestData.User2Id + }); + } + } +} +```` + +Also created a static class to store the User `Ids`: + +````csharp +using System; + +namespace MyProject +{ + public static class TestData + { + public static Guid User1Id = Guid.Parse("41951813-5CF9-4204-8B18-CD765DBCBC9B"); + public static Guid User2Id = Guid.Parse("41951813-5CF9-4204-8B18-CD765DBCBC9B"); + } +} +```` + +In this way, we can use these known Issues and the User `Id`s to perform the tests. + +#### Example: Testing a Domain Service `AbpIntegratedTest` class (defined in the [Volo.Abp.TestBase](https://www.nuget.org/packages/Volo.Abp.TestBase) package) is used to write tests integrated to the ABP Framework. `T` is the Type of the root module to setup and initialize the application. -**Example: Create a test class derived from the `AbpIntegratedTest`** +The application startup template has base classes in each test project, so you can derive from these base classes to make it easier. + +See the `IssueManager` tests are re-written as integration tests ````csharp -using Volo.Abp.Testing; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp; +using Xunit; namespace MyProject.Issues { - public class IssueManager_Integration_Tests - : AbpIntegratedTest + public class IssueManager_Integration_Tests : MyProjectDomainTestBase { + private readonly IssueManager _issueManager; + private readonly Issue _issue; + + public IssueManager_Integration_Tests() + { + _issueManager = GetRequiredService(); + _issue = new Issue + { + Title = "Test title", + Description = "Test description" + }; + } + + [Fact] + public async Task Should_Not_Allow_To_Assign_Issues_Over_The_Limit() + { + // Act & Assert + await Assert.ThrowsAsync(async () => + { + await _issueManager.AssignToUserAsync(_issue, TestData.User1Id); + }); + + _issue.AssignedUserId.ShouldBeNull(); + } + + [Fact] + public async Task Should_Assign_An_Issue_To_A_User() + { + // Act + await _issueManager.AssignToUserAsync(_issue, TestData.User2Id); + //Assert + _issue.AssignedUserId.ShouldBe(TestData.User2Id); + } } } ```` +* First test method assigns the issue to the User 1, which has already assigned to 3 issues in the Data Seed code. So, it throws a `BusinessException`. +* Second test method assigns the issue to User 2, which has only 1 issue assigned. So, the method succeeds. + +This class typically locates in the `.Domain.Tests` project since it tests a class located in the `.Domain` project. It is derived from the `MyProjectDomainTestBase` which is already configured to properly run the tests. + +Writing such an integration test class is very straightforward. Another benefit is that you won't need to change the test class later when you add another dependency to the `IssueManager` class. + +### Example: Testing an Application Service + + + TODO ## UI Tests