-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* ci: add continuous integration * test: add more tests * fix: dbcontext instance lifetime
- Loading branch information
Showing
34 changed files
with
500 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* text eol=crlf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Contributing guidelines | ||
|
||
This project welcomes contributions: | ||
|
||
**Request for support:** | ||
TBD | ||
|
||
**Disclose vulnerability:** | ||
Please [create a new security advisory on GitHub](https://github.com/ArwynFr/dotnet-integration-testing/security/advisories) | ||
|
||
**Report malfunctions:** | ||
[Please create a new issue on GitHub](https://github.com/ArwynFr/dotnet-integration-testing/issues/new/choose) | ||
|
||
**Suggest a feature:** | ||
[Please create a new issue on GitHub](https://github.com/ArwynFr/dotnet-integration-testing/issues/new/choose) | ||
|
||
**Offer some code:** | ||
Please [fork the repository](https://github.com/ArwynFr/dotnet-integration-testing/fork) | ||
and [submit a pull-request](https://github.com/ArwynFr/dotnet-integration-testing/compare) | ||
|
||
## Definition of Done | ||
|
||
Merging a pull request requires: | ||
|
||
- dotnet format passes | ||
- dotnet roslynator analyze passes | ||
- dotnet oudated passes | ||
- markdownlint passes | ||
- SonarCloud QualityGate passes | ||
- Documentation updated |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# ArwynFr.IntegrationTesting | ||
|
||
This library provides utility classes for writing integration tests in | ||
dotnet using `XUnit` and `WebApplicationFactory`. | ||
|
||
![Nuget.org](https://img.shields.io/nuget/v/ArwynFr.IntegrationTesting?style=for-the-badge) | ||
![Nuget.org](https://img.shields.io/nuget/dt/ArwynFr.IntegrationTesting?style=for-the-badge) | ||
![GitHub | ||
License](https://img.shields.io/github/license/ArwynFr/dotnet-integration-testing?style=for-the-badge) | ||
|
||
## Installation | ||
|
||
dotnet add package ArwynFr.IntegrationTesting | ||
|
||
## Usage | ||
|
||
Read [advanced usage | ||
documentation](https://github.com/ArwynFr/dotnet-integration-testing/blob/main/.github/USAGE.adoc) | ||
for further details. | ||
|
||
By default, the lib redirects the tested application logs to XUnit | ||
output, so you get them in the test output in case of failure. It also | ||
overwrites the application configuration with values from user secrets | ||
and environement variables. | ||
|
||
public class MyTest : IntegrationTestBase<Program> | ||
{ | ||
public MyTest(ITestOutputHelper output) : base(output) { } | ||
|
||
[Fact] | ||
public async Task OnTest() | ||
{ | ||
// Call system under test | ||
var response = await Client.GetFromJsonAsync<OrderDto>($"/order"); | ||
|
||
response.Should().HaveValue(); | ||
} | ||
|
||
// Override a service with fake implementation in the tested app | ||
protected override void ConfigureAppServices(IServiceCollection services) | ||
=> services.AddSingleton<IMyService, FakeService>(); | ||
} | ||
|
||
### EntityFrameworkCore integration | ||
|
||
public class TestBaseDb : IntegrationTestBase<Program, MyDbContext> | ||
{ | ||
public TestBaseDb(ITestOutputHelper output) : base(output) { } | ||
|
||
[Fact] | ||
public async Task OnTest() | ||
{ | ||
// Access the injected dbcontext | ||
var value = await Database.Values | ||
.Where(val => val.Id == 1) | ||
.Select(val => val.Result) | ||
.FirstOrDefaultAsync(); | ||
|
||
// Call system under test | ||
var result = await Client.GetFromJsonAsync<int>("/api/value/1"); | ||
|
||
result.Should().Be(value + 1); | ||
} | ||
|
||
// Create and drop a database for every test execution | ||
protected override IDatabaseTestStrategy<Context> DatabaseTestStrategy | ||
=> IDatabaseTestStrategy<MyDbContext>.DatabasePerTest; | ||
|
||
// Configure EFcore with a random database name | ||
protected override void ConfigureDbContext(DbContextOptionsBuilder builder) | ||
=> builder.UseSqlite($"Data Source={Guid.NewGuid()}.sqlite"); | ||
} | ||
|
||
## Contributing | ||
|
||
This project welcomes contributions: | ||
|
||
**Request for support:** | ||
TBD | ||
|
||
**Disclose vulnerability:** | ||
Please [create a new security advisory on GitHub](https://github.com/ArwynFr/dotnet-integration-testing/security/advisories) | ||
|
||
**Report malfunctions:** | ||
[Please create a new issue on GitHub](https://github.com/ArwynFr/dotnet-integration-testing/issues/new/choose) | ||
|
||
**Suggest a feature:** | ||
[Please create a new issue on GitHub](https://github.com/ArwynFr/dotnet-integration-testing/issues/new/choose) | ||
|
||
**Offer some code:** | ||
Please [fork the repository](https://github.com/ArwynFr/dotnet-integration-testing/fork) | ||
and [submit a pull-request](https://github.com/ArwynFr/dotnet-integration-testing/compare) | ||
\ | ||
[Read our definition of done in contributing guidelines](https://github.com/ArwynFr/dotnet-integration-testing/blob/main/.github/CONTRIBUTING.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
# Advanced usage documentation | ||
|
||
This library uses `WebApplicationFactory` for integration testing. | ||
Please read [Microsoft’s paper on integration | ||
testing](https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-8.0) | ||
for details on how this library works. | ||
|
||
## Definitions | ||
|
||
**Tested application** | ||
A dotnet application that you want to test. Also known as the *system | ||
under test* (SUT). | ||
|
||
**Entrypoint** | ||
A specific class of the tested application that is run when the | ||
application start. For top-level statement applications this is the | ||
`Program` class. | ||
|
||
**Test project** | ||
A XUnit test project that implements tests and ensures that the tested | ||
application behaves as expected. | ||
|
||
## Basic integration test | ||
|
||
Simply extend the `IntegrationTestBase<TEntryPoint>` class and provide | ||
the entrypoint class you want to test: | ||
|
||
public class MyTest : IntegrationTestBase<Program> | ||
{ | ||
public MyTest(ITestOutputHelper output) : base(output) { } | ||
} | ||
|
||
## Arranging the tested application | ||
|
||
You can override the following methods to customize the tested | ||
application: | ||
|
||
ConfigureAppConfiguration | ||
Change the sources for the app configuration. By default, overrides the | ||
application configuration sources with the following (lower overrides | ||
higher): | ||
|
||
- `appsettings.json` file | ||
- User secrets associated with the assembly that contains the entrypoint | ||
- User secrets associated with the assembly that contains the test class | ||
- Environment variables | ||
|
||
ConfigureAppLogging | ||
Change the app logging configuration. By default, redirects all logs | ||
(all namespaces and all levels) to the XUnit output helper. If the test | ||
fails, you get all the tested application logs in the test output. | ||
|
||
ConfigureAppServices | ||
Override dependency injection services of the tested application. | ||
|
||
If your test code and the tested application need to access the same | ||
instance of a service, you need to inject this instance using a | ||
Singleton lifetime. Accessing the DI container outside of the client | ||
call will return an unscoped provider as scopes are created and disposed | ||
by the framework for each request. | ||
|
||
// Override a service with custom implementation in the tested app | ||
protected override void ConfigureAppServices(IServiceCollection services) | ||
=> services.AddSingleton<IMyService, FakeService>(); | ||
|
||
[Fact] | ||
public async Task OnTest() | ||
{ | ||
var expected = 3; | ||
|
||
// Access the injected service from the test code | ||
var service = Services.GetRequiredService<IMyService>(); | ||
service.SetValue(expected); | ||
|
||
var response = await Client.GetFromJsonAsync<int>($"/value"); | ||
|
||
response.Should().Be(expected); | ||
} | ||
|
||
## Accessing the tested application | ||
|
||
Client | ||
You can access the tested application using the `Client` property which | ||
returns a pseudo `HttpClient` created by `WebApplicationFactory`. You | ||
access your application like a client application would: | ||
|
||
<!-- --> | ||
|
||
[Fact] | ||
public async Task OnTest() | ||
{ | ||
var response = await Client.GetFromJsonAsync<OrderDto>($"/order"); | ||
response.Should().HaveValue(); | ||
} | ||
|
||
Configuration | ||
This property grants you acces to the `IConfiguration` values that are | ||
currently available to the tested application. | ||
|
||
Services | ||
This property grants you access to the DI service provider of the tested | ||
application. | ||
|
||
If your test code and the tested application need to access the same | ||
instance of a service, you need to inject this instance using a | ||
Singleton lifetime. Accessing the DI container outside of the client | ||
call will return an unscoped provider as scopes are created and disposed | ||
by the framework for each request. | ||
|
||
## Extending the behavior of the test class | ||
|
||
You can override the following methods to run code before and after each | ||
test: | ||
|
||
InitializeAsync | ||
Code executed before the execution of each test of the class. | ||
|
||
DisposeAsync | ||
Code executed after the execution of each test of the class. | ||
|
||
## EntityFrameworkCore integration | ||
|
||
The library provides specific support for efcore. You can achieve this | ||
integration by extending the | ||
`IntegrationTestBase<TEntryPoint, TContext>` class instead of the one | ||
that only uses the entrypoint. | ||
|
||
You will need to override the abstract `ConfigureDbContext` method to | ||
tell the dependency injection library how to configure your context. A | ||
context instance will be generated per test and injected in your target | ||
app as a singleton. You can access the same context instance in your | ||
test through the `Database` property. | ||
|
||
protected override void ConfigureDbContext(DbContextOptionsBuilder builder) | ||
=> builder.UseSqlite($"Data Source=test.sqlite"); | ||
|
||
The base class also exposes a `DatabaseTestStrategy` property that | ||
allows you to customize the test behavior regarding the database. You | ||
can write your own implementation which requires you to write specific | ||
code that will run before and after each test to set your database. | ||
|
||
The library comes with 3 standard behaviors: | ||
|
||
`IDatabaseTestStrategy<TContext>.Default` | ||
By default the library simply instantates the context and disposes the | ||
instance after the test execution. | ||
|
||
`IDatabaseTestStrategy<TContext>.Transaction` | ||
This behavior will execute each test in a separate transaction. This can | ||
be used to prevent write operations to change the contents of the | ||
database. Obviously requires a database engine that supports | ||
transactions. | ||
|
||
`IDatabaseTestStrategy<TContext>.DatabasePerTest` | ||
This behavior creates a fresh database before test execution and drops | ||
it afterwards. It also applies migrations if any are found, otherwise it | ||
will use `EnsureCreated` (read [Create and Drop | ||
APIs](https://learn.microsoft.com/en-us/ef/core/managing-schemas/ensure-created) | ||
to understand how this might affect your test results). This allows test | ||
parallelization when transaction isolation is not sufficient or | ||
unavailable. You must combine this behavior with a random name | ||
interpolation in the connection string to run each test on it’s own | ||
database in parallel. Otherwise the tests will try to access the same | ||
database concurrently and will fail to drop it while other tests are | ||
running: | ||
|
||
<!-- --> | ||
|
||
protected override IDatabaseTestStrategy<Context> DatabaseTestStrategy | ||
=> IDatabaseTestStrategy<Context>.DatabasePerTest; | ||
|
||
protected override void ConfigureDbContext(DbContextOptionsBuilder builder) | ||
=> builder.UseSqlite($"Data Source={Guid.NewGuid()}.sqlite"); | ||
|
||
This beahvior WILL drop your database after each test ! |
Oops, something went wrong.