Skip to content

Latest commit

 

History

History
221 lines (174 loc) · 4.74 KB

testing.md

File metadata and controls

221 lines (174 loc) · 4.74 KB

Init

https://github.com/bmeaut/WebApiLab/archive/refs/heads/net6-test-init.zip

Nuget

<PackageReference Include="Bogus" Version="34.0.2" />
<PackageReference Include="FluentAssertions" Version="6.6.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.4" />

Project reference

<ItemGroup>
  <ProjectReference Include="..\WebApiLab.Api\WebApiLab.Api.csproj" />
</ItemGroup>

CustomWebApplicationFactory

public class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
    protected override IHost CreateHost(IHostBuilder builder)
    {
        builder.UseEnvironment("Development");
        builder.ConfigureServices(services =>
        {
            services.AddScoped(sp =>
            {
                return new DbContextOptionsBuilder<AppDbContext>()
                    .UseSqlServer(@"connection string")
                    .UseApplicationServiceProvider(sp)
                    .Options;
            });
        });

        var host = base.CreateHost(builder);

        using var scope = host.Services.CreateScope();
        scope.ServiceProvider.GetRequiredService<AppDbContext>()
            .Database.EnsureCreated();

        return host;
    }
}

ProductControllerTest

public partial class ProductControllerTests : IClassFixture<CustomWebApplicationFactory>
{
    private readonly CustomWebApplicationFactory _appFactory;

    public ProductControllerTests(CustomWebApplicationFactory appFactory)
    {
        _appFactory = appFactory;
    }
}

DTO fake

private readonly Faker<Product> _dtoFaker;

public ProductControllerTests(CustomWebApplicationFactory appFactory)
{
    _dtoFaker = new Faker<Product>()
        .RuleFor(p => p.Id, 0)
        .RuleFor(p => p.Name, f => f.Commerce.Product())
        .RuleFor(p => p.UnitPrice, f => f.Random.Int(200, 20000))
        .RuleFor(p => p.ShipmentRegion, 
                 f => f.PickRandom<Dal.Entities.ShipmentRegion>())
        .RuleFor(p => p.CategoryId, 1)
        .RuleFor(p => p.RowVersion, f => f.Random.Bytes(5));
}

JsonSerializerOptions

private readonly JsonSerializerOptions _serializerOptions;

public ProductControllerTests(CustomWebApplicationFactory appFactory)
{
    // ...
    _serializerOptions = new JsonSerializerOptions()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
    };
    _serializerOptions.Converters.Add(new JsonStringEnumConverter());
}

ProductsControllerTest

public partial class ProductControllerTests
{
    public class Post : ProductControllerTests
    {
        public Post(CustomWebApplicationFactory appFactory)
            : base(appFactory)
        {
        }
    }
}

Test method #1

v0

[Fact]
public async Task Should_Succeded_With_Created()
{
    // Arrange

    // Act

    // Assert
}

Arrange

// Arrange
var client = _appFactory.CreateClient();
var dto = _dtoFaker.Generate();

Act

// Act
var response = await client.PostAsJsonAsync("/api/product", dto, _serializerOptions);
var p = await response.Content.ReadFromJsonAsync<Product>(_serializerOptions);

Assert

// Assert
response.StatusCode.Should().Be(HttpStatusCode.Created);
response.Headers.Location
    .Should().Be(
        new Uri(_appFactory.Server.BaseAddress, $"/api/Product/{p.Id}")
    );

p.Should().BeEquivalentTo(
    dto,
    opt => opt.Excluding(x => x.Category)
        .Excluding(x => x.Orders)
        .Excluding(x => x.Id)
        .Excluding(x => x.RowVersion));
p.Category.Should().NotBeNull();
p.Category.Id.Should().Be(dto.CategoryId);
p.Orders.Should().BeEmpty();
p.Id.Should().BeGreaterThan(0);
p.RowVersion.Should().NotBeEmpty();

prevent mutation

// Arrange
_appFactory.Server.PreserveExecutionContext = true;
using var tran = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);

Test method #2

v0

[Theory]
[InlineData("", "Product name is required.")]
[InlineData(null, "Product name is required.")]
public async Task Should_Fail_When_Name_Is_Invalid(string name, string expectedError)
{
    // Arrange

    // Act

    // Assert
}

Arrange

// Arrange
var client = _appFactory.CreateClient();
var dto = _dtoFaker.RuleFor(x => x.Name, name).Generate();

Act

var response = await client.PostAsJsonAsync("/api/product", dto, _serializerOptions);
var p = await response.Content
            .ReadFromJsonAsync<ValidationProblemDetails>(_serializerOptions);

Assert

// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);

p.Status.Should().Be(400);
p.Errors.Should().HaveCount(1);
p.Errors.Should().ContainKey(nameof(Product.Name));
p.Errors[nameof(Product.Name)].Should().ContainSingle(expectedError);