diff --git a/docs/en/Testing.md b/docs/en/Testing.md index 5dafef3daf..e0ba6e2f34 100644 --- a/docs/en/Testing.md +++ b/docs/en/Testing.md @@ -1,3 +1,222 @@ -# Testing +# Automated Testing -TODO! \ No newline at end of file +ABP Framework has been designed with testability in mind. There are some different levels of automated testing; + +* **Unit Tests**: You typically test a single class (or a very few classes together). These tests will be fast. However, you generally need to deal with mocking for the dependencies of your service(s). +* **Integration Tests**: You typically test a service, but this time you don't mock the fundamental infrastructure and services to see if they properly working together. +* **UI Tests**: You test the UI of the application, just like the users interact with your application. + +## The Application Startup Template + +The [Application Startup Template](Startup-Templates/Application.md) comes with the test infrastructure properly installed and configured for you. + +### The Test Projects + +See the following solution structure in the Visual Studio: + +![solution-test-projects](images/solution-test-projects.png) + +There are more than one test project, organized by the layers; + +* `Domain.Tests` is used to test your Domain Layer objects (like [Domain Services](Domain-Services.md) and [Entities](Entities.md)). +* `Application.Tests` is used to test your Application Layer (like [Application Services](Application-Services.md)). +* `EntityFrameworkCore.Tests` is used to test your custom repository implementations or EF Core mappings (this project will be different if you use another [Database Provider](Data-Access.md)). +* `Web.Tests` is used to test the UI Layer (like Pages, Controllers and View Components) +* `TestBase` contains some classes those are shared/used by the other projects. + +> `HttpApi.Client.ConsoleTestApp` is not an automated test application. It is an example Console Application that shows how to consume your HTTP APIs from a .NET Console Application. + +The following sections will introduce the base classes and other infrastructure included in these projects. + +### The Test Infrastructure + +The startup solution has the following libraries already installed; + +* [xUnit](https://xunit.net/) as the test framework. +* [NSubstitute](https://nsubstitute.github.io/) as the mocking library. +* [Shouldly](https://github.com/shouldly/shouldly) as the assertion library. + +While you are free to replace them with your favorite tools, this document and examples will be base on these tooling. + +## The Test Explorer + +You can use the Test Explorer to view and run the tests in Visual Studio. For other IDEs, see their own documentation. + +### Open the Test Explorer + +Open the *Test Explorer*, under the *Tests* menu, if it is not already open: + +![vs-test-explorer](images/vs-test-explorer.png) + +### Run the Tests + +Then you can click to the Run All or Run buttons to run the tests. The initial startup template has some sample tests for you: + +![vs-startup-template-tests](images/vs-startup-template-tests.png) + +### Run Tests In Parallel + +The test infrastructure is compatible to run the tests in parallel. It is **strongly suggested** to run all the tests in parallel, which is pretty faster then running them one by one. + +To enable it, click to the caret icon near to the settings (gear) button and select the *Run Tests In Parallel*. + +![vs-run-tests-in-parallel](images/vs-run-tests-in-parallel.png) + +## Unit Tests + +For Unit Tests, you don't need to much infrastructure. You typically instantiate your class and provide some pre-configured mocked objects to prepare your object to test. + +### Classes Without Dependencies + +In this simplest case, the class you want to test has no dependencies. In this case, you can directly instantiate your class, call its methods and make your assertions. + +#### Example: Testing an Entity + +Assume that you've an `Issue` [entity](Entities.md) as shown below: + +````csharp +using System; +using Volo.Abp.Domain.Entities; + +namespace MyProject.Issues +{ + public class Issue : AggregateRoot + { + public string Title { get; set; } + public string Description { get; set; } + public bool IsLocked { get; set; } + public bool IsClosed { get; private set; } + public DateTime? CloseDate { get; private set; } + + public void Close() + { + IsClosed = true; + CloseDate = DateTime.UtcNow; + } + + public void Open() + { + if (!IsClosed) + { + return; + } + + if (IsLocked) + { + throw new IssueStateException("You can not open a locked issue!"); + } + + IsClosed = true; + CloseDate = null; + } + } +} + +```` + +Notice that the `IsClosed` and `CloseDate` properties have private setters to force some business rules by using the `Open()` and `Close()` methods; + +* Whenever you close an issue, the `CloseDate` should be set to the [current time](Timing.md). +* An issue can not be re-opened if it is locked. And if it is re-opened, the `CloseDate` should be set to `null`. + +Since the `Issue` entity is a part of the Domain Layer, we should test it in the `Domain.Tests` project. Create an `Issue_Tests` class inside the `Domain.Tests` project: + +````csharp +using Shouldly; +using Xunit; + +namespace MyProject.Issues +{ + public class Issue_Tests + { + [Fact] + public void Should_Set_The_CloseDate_Whenever_Close_An_Issue() + { + // Arrange + + var issue = new Issue(); + issue.CloseDate.ShouldBeNull(); // null at the beginning + + // Act + + issue.Close(); + + // Assert + + issue.IsClosed.ShouldBeTrue(); + issue.CloseDate.ShouldNotBeNull(); + } + } +} +```` + +This test follows the AAA (Arrange-Act-Assert) pattern; + +* **Arrange** part creates an `Issue` entity and ensures the `CloseDate` is `null` at the beginning. +* **Act** part executes the method we want to test for this case. +* **Assert** part checks if the `Issue` properties are same as we expect to be. + +`[Fact]` attribute is defined by the [xUnit](https://xunit.net/) library and marks a method as a test method. `Should...` extension methods are provided by the [Shouldly](https://github.com/shouldly/shouldly) library. You can directly use the `Assert` class of the xUnit, but Shouldly makes it much comfortable and straightforward. + +When you execute the tests, you will see that is passes successfully: + +![issue-first-test](images/issue-first-test.png) + +Let's add two more test methods: + +````csharp +[Fact] +public void Should_Allow_To_ReOpen_An_Issue() +{ + // Arrange + + var issue = new Issue(); + issue.Close(); + + // Act + + issue.Open(); + + // Assert + + issue.IsClosed.ShouldBeFalse(); + issue.CloseDate.ShouldBeNull(); +} + +[Fact] +public void Should_Not_Allow_To_ReOpen_A_Locked_Issue() +{ + // Arrange + + var issue = new Issue(); + issue.Close(); + issue.IsLocked = true; + + // Act & Assert + + Assert.Throws(() => + { + issue.Open(); + }); +} +```` + +`Assert.Throws` checks if the executed code throws a matching exception. + +> See the xUnit & Shoudly documentations to learn more about these libraries. + +### Classes With Dependencies + +TODO + +## Integration Tests + +TODO + +## UI Tests + +TODO + +## Best Practices + +TODO \ No newline at end of file diff --git a/docs/en/images/issue-first-test.png b/docs/en/images/issue-first-test.png new file mode 100644 index 0000000000..b2be1aa8eb Binary files /dev/null and b/docs/en/images/issue-first-test.png differ diff --git a/docs/en/images/solution-test-projects.png b/docs/en/images/solution-test-projects.png new file mode 100644 index 0000000000..e51c368e9f Binary files /dev/null and b/docs/en/images/solution-test-projects.png differ diff --git a/docs/en/images/vs-run-tests-in-parallel.png b/docs/en/images/vs-run-tests-in-parallel.png new file mode 100644 index 0000000000..765cba5100 Binary files /dev/null and b/docs/en/images/vs-run-tests-in-parallel.png differ diff --git a/docs/en/images/vs-startup-template-tests.png b/docs/en/images/vs-startup-template-tests.png new file mode 100644 index 0000000000..f16d79a4c7 Binary files /dev/null and b/docs/en/images/vs-startup-template-tests.png differ diff --git a/docs/en/images/vs-test-explorer.png b/docs/en/images/vs-test-explorer.png new file mode 100644 index 0000000000..e225ee5e92 Binary files /dev/null and b/docs/en/images/vs-test-explorer.png differ diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/ValueObject_Tests.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/ValueObject_Tests.cs index 9142dfd45e..b19c722042 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/ValueObject_Tests.cs +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/ValueObject_Tests.cs @@ -22,7 +22,7 @@ namespace Volo.Abp.Domain.Values var cityId = Guid.NewGuid(); var address1 = new Address(cityId, "Baris Manco", 42); - var address2 = new Address(cityId, "Baris Manco", 42); + var address2 = new Address(cityId, "Baris Manco", 43); address1.ValueEquals(address2).ShouldBeFalse(); }