Common fixture is a toolkit contains essential test fixtures for .net core and asp.net core test projects. CommonFixtures aims to reduce your preparing test suite effort for common testing concerns and supplying ready to use testing infrastructure. CommonFixtures can be used independently from testing framework. You can prefer any kind of dotNet testing framework like xUnit, nUnit etc.
Supported .Net Core Versions |
---|
.netocreapp3.1 |
.netocreapp3.0 |
CommonFixtures supplies some base classes to supply underlying fixtures for your test suite. These are as below;
- BaseTest
- WithIoC
- WithHost
- WithWebApp
- WithEfCore
- WithWebAppAndEfCore
- Selenium support for WithWebApp and WithWebAppAndEfCore fixtures
Contains basic mocking and stubbing methods to supply standard DSL for all tests. (It uses FakeItEasy library for faking and AutoFixture for data generation internally)
- Stubbing demo
public class ProductServiceTests : BaseTest
{
private readonly ProductService _sut;
private readonly IProductRepository _mockRepository;
public ProductServiceTests()
{
_mockRepository = Mock<IProductRepository>();
_sut = new ProductService(_mockRepository);
}
// with your favourite testing tool xUnit, nunit ... etc.
// [Fact]
// [Test]
public async Task Should_Created_Product()
{
// Given
var givenTitle = Random<string>();
var givenPrice = Random<decimal>();
var expectedId = Random<int>();
StubAsync(() => _mockRepository.CreateProduct(ArgMatches<Product>(x => x.Title == givenTitle && x.Price == givenPrice), ArgIgnore<CancellationToken>()), expectedId);
// When
var id = await _sut.CreateProduct(givenTitle, givenPrice);
// Then
Assert.Equal(expectedId, id);
}
}
- Verification demo
public async Task Should_Call_Repo_Once()
{
// Given
var givenTitle = Random<string>();
var givenPrice = Random<decimal>();
// When
await _sut.CreateProduct(givenTitle, givenPrice);
// Then
Verify(() => _mockRepository.CreateProduct(ArgMatches<Product>(x => x.Title == givenTitle && x.Price == givenPrice), ArgIgnore<CancellationToken>()), numberOfTimes: 1);
}
ServiceCollection fixture to keep your IoC logic afloat throughout the test.
public class ProductServiceTest : WithIoC
{
protected override void ConfigureServices(IServiceCollection services)
{
services.MockAndRegister<IProductRepository>();
services.Register<ProductService>();
}
[Fact]
public async Task Should_Created_Product()
{
// Given
var givenTitle = Random<string>();
var givenPrice = Random<decimal>();
var mockRepository = GetService<IProductRepository>();
var sut = GetService<ProductService>();
// When
await sut.CreateProduct(givenTitle, givenPrice);
// Then
Verify(() => mockRepository.CreateProduct(ArgMatches<Product>(x => x.Title == givenTitle && x.Price == givenPrice), ArgIgnore<CancellationToken>()), numberOfTimes: 1);
}
}
- Also allows you to mock some dependencies of the system under test while leaving others as they are
public class DependencyMockingTest : WithIoC
{
public class YourAwesomeService
{
public YourAwesomeService(IDependency1 dep1, IDependency2 dep2)
{
_dep1 = dep1;
_dep2 = dep2;
}
// ....
}
protected override void ConfigureServices(IServiceCollection services)
{
services.MockAndRegister<IDependency1>();
services.Register<YourAwesomeService>();
// so mocked just dependency 1 and leaving second one as it is
}
// ....
}
Supplies test host for testing .net core hosted services (see: .NET Core Hosted Services)
public class QueuedJobHostedServiceTests : WithHost
{
protected override void ConfigureServices(IServiceCollection services)
{
// adding your awesome hosted service
services.AddHostedService<QueuedJobHostedService>();
}
[Fact]
public async Task It_Should_Execute_Queued_Task()
{
// Arrange
var queueManager = GetService<QueueManager>();
var counter = 0;
// Act
queueManager.EnqueueJob(() =>
{
Interlocked.Increment(ref counter);
});
// Assert
await Task.Delay(300);
Assert.Equal(1, counter);
}
}
Aspnet core test server fixture for integration testing scenarios
public class MvcIntegrationTest : WithWebApp<Startup>
{
protected override void ConfigureServices(IServiceCollection services)
{
// You can override or mock services that registered from Startup.cs before
}
[Fact]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType()
{
// Arrange
var client = HttpClient;
// Act
var response = await client.GetAsync("/weatherforecast");
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal("application/json; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
⚠️ Ensure Microsoft.AspNetCore.Mvc.Testing package added to root of your test project before using this fixture.
- Supplies test fixture to test projects which use Entity Framework Core as persistence layer.
- CommonFixtures automatically replaces your existing ef core db context configuration with in memory sqlLite.
- Provides useful approach when you want to test your persistence logic like db model, entity validations, secend level cache, repository abstractions without mocking anything.
public class ProductRepositoryTest : WithEfCore<ApplicationDbContext>
{
[Fact]
public async Task Should_Persist_New_Created_Product()
{
// Arrange
Arrange(dbContext =>
{
// helper that pull the database to desired state before acting (optional)
});
var sut = new ProductRepository(DbContext);
// Act
var id = await sut.CreateProduct(Random<Product>(), CancellationToken.None);
// Assert
NewServiceScope(); // dispose all of services and recreate new one to simulate new scope like new http request
Assert.IsType<int>(id);
Assert.NotEqual(default, id);
var createdProduct = Get<Product>(id);
Assert.NotNull(createdProduct);
}
}
- Combination of WithWebApp and WithEfCore
- In addition to the WithEfCore, starts test server up and running then replace db context which registered in the Startup.ConfigureServices method
- It is useful when you want to make test more complex application logic without mocking persistence layer
// Automatically replaces your db context configuration specified in Startup.cs with in memory sqlLite
public class CreateProductCommandHandlerTest : WithWebAppAndEfCore<Startup, ApplicationDbContext>
{
[Fact]
public async Task Should_Created_New_Product()
{
// Arrange
Arrange(dbContext =>
{
// you can use pull the database to desired state before acting
// dbContext.Categories.Add(Random<Category>());
});
var mediator = GetService<IMediator>();
// Act
var id = await mediator.Send(Random<CreateProductCommand>());
// Assert
NewServiceScope(); // dispose all of services and recreate new one to simulate new scope like new http request
Assert.IsType<int>(id);
Assert.NotNull(Get<Product>(id));
}
}
- Make sure that following two dependencies are added to your test project.
<PackageReference Include="Selenium.WebDriver" Version="3.141.0" />
<PackageReference Include="Selenium.Chrome.WebDriver" Version="83.0.0" />
// see SampleWebApp Index.cshtml to see sut
public class MvcViewIntegrationTest : WithWebApp<Startup>
{
protected override bool SeleniumEnabled => true;
protected override bool SeleniumHeadless => true; // you can make false to see chrome browser
[Fact]
public void Counter_Test()
{
// Arrange
WebDriverWait waitForElement = new WebDriverWait(Selenium, TimeSpan.FromSeconds(10));
waitForElement.Until(ElementIsVisible(By.Id("counter")));
var button = Selenium.FindElement(By.Id("btn"));
var counterSpan = Selenium.FindElement(By.Id("counter"));
// Act
Assert.Equal(0, int.Parse(counterSpan.Text));
button.Click();
// Assert
counterSpan = Selenium.FindElement(By.Id("counter"));
Assert.Equal(1, int.Parse(counterSpan.Text));
}
}