.NET Core and integration tests
Tags: integration, tests, clean
Published: 2018-11-28 01:56:35

---
title: .NET Core and integration tests
published: true
description: A fast guide to write clean and maintainable tests
tags: integration,tests,clean,
cover_image: https://thepracticaldev.s3.amazonaws.com/i/krx7kzzkrtoesx2eqahn.jpg
---

# Integration test for .NET Core - Clean architecture

This project has an example of how to write clean and maintainable test with [xUnit](https://xunit.github.io/), [TestServer](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.testhost.testserver?view=aspnetcore-2.1), [FluentAssertions](https://github.com/JeremySkinner/FluentValidation) and more!

### The production code

First lets look at all the production code... (Database schema configuration, Hashing, JWT Configuration, Token Generation, Validation).

This is the folder structure: 
```
CleanVidly/
    src/
        CleanVidly/
            Controllers/        <-- All the controllers with their DTOS and validators
            Extensions/         <-- Extension methods for pagination and other things
            Core/               <-- Abstractions and Entities
            Mapping/            <-- AutoMapper configuration 
            Persistance/        <-- All repositories and database schema configuration (Migrations here too)
            Infraestructure/    <-- Helpers classes for generate JWT and password hashing
    tests/
        CleanVidly.IntegrationsTest/
            Controllers/        <-- Endpoints tests
            Extensions/         <-- Extensions for testing
            Helpers/            <-- Helpers for testing
```

Everything will make sense! Just be patient! Before testing I want to share with you the parts of the production code. 

Is a simple app with a clean and simple structure, have all REST endpoints for 'Categories' and 'Roles', besides an endpoint for JWT login.

### Why this solution is not splitted into multiple projects? (View/BusinessLogic/Data)

Please see [this post of Mosh Hamedani](https://programmingwithmosh.com/csharp/should-you-split-your-asp-net-mvc-project-into-multiple-projects/) about this.

The Inversion dependency principle is in practice even with just one project The **Controller** layer depend upon abstractions on **Core** layer, and **Persistance** depend on **Core** too. All depent on the dependency direction, no folder, no projects.



##### JWT Token generation and validation with roles verification.

Setup the token services and authorization middleware on **Startup.cs** with: 
```csharp
private void GetAuthenticationOptions(AuthenticationOptions options)
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}

private void GetJwtBearerOptions(JwtBearerOptions options)
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,

        ValidIssuer = Configuration["Jwt:ValidIssuer"],
        ValidAudience = Configuration["Jwt:ValidAudience"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:SecurityKey"]))
    };
}
```

Use them inside **ConfigureServices** method with:
```csharp
services
    .AddAuthentication(GetAuthenticationOptions)
    .AddJwtBearer(GetJwtBearerOptions);
```
And add this in the **Configure** method
```csharp
app.UseAuthentication();
app.UseMvc();
```

Generate the token on each valid login with:
```csharp
public string GenerateToken(User user)
{

    var claims = new List<Claim>() {
        new Claim(JwtRegisteredClaimNames.NameId, user.Id.ToString()),
        new Claim(JwtRegisteredClaimNames.UniqueName, user.Email),
        new Claim("name", user.Name),
        new Claim("lastname", user.Lastname),
        new Claim("joinDate", user.JoinDate.ToString("dd/MM/yyyy H:mm")),
    };

    var roles = user.UserRoles.Select(ur => new Claim("roles", ur.Role.Description));
    claims.AddRange(roles);

    return GetJwtToken(claims);
}

private string GetJwtToken(IEnumerable<Claim> claims)
{
    var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey));
    var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha512);

    var tokeOptions = new JwtSecurityToken(
        issuer: validIssuer,
        audience: validAudience,
        claims: claims,
        expires: DateTime.UtcNow.AddHours(24),
        signingCredentials: signinCredentials
    );

    return new JwtSecurityTokenHandler().WriteToken(tokeOptions);
}
```


##### Validation

I prefer to use [FluentValidation](https://github.com/JeremySkinner/FluentValidation) to avoid Data anotations and because is so much powerful
```csharp
public class UserValidator : AbstractValidator<SaveUserResource>
{
    public UserValidator()
    {
        RuleFor(u => u.Name).NotEmpty().MinimumLength(4).MaximumLength(32);
        RuleFor(u => u.Lastname).NotEmpty().MinimumLength(4).MaximumLength(32);
        RuleFor(u => u.Email).NotEmpty().MinimumLength(4).MaximumLength(128).EmailAddress();
        RuleFor(u => u.Roles).NotEmpty();
    }
}
```

It is cleaner and respects the **Single responsability principle!** Supports regular expressions, async validation and more.

To validate all Dtos automaticly on each request, add a line of code on our Startup class

```csharp
services
    .AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .AddFluentValidation(fvc => fvc.RegisterValidatorsFromAssemblyContaining<Startup>());
```
This line tells to search all the validators on the current assembly.

On each request, if add a dto as a parameter of an enpoint, it will check for a validator, validate it and if any errors, returns a 400 BadRequest with all the errors, cool right?




##### Database Schema

To configure all columns and tables I don't use data anotations, use pure FluentAPI but that **OnModelCreating method** was messy with so many code... So I use **IEntityTypeConfiguration** to create a configuration class for each model:

```csharp
public class UserConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.Property(u => u.Email).HasMaxLength(128).IsRequired();
        builder.Property(u => u.Name).HasMaxLength(32).IsRequired();
        builder.Property(u => u.Lastname).HasMaxLength(32).IsRequired();
        builder.Property(u => u.Salt).HasMaxLength(128).IsRequired();
        builder.Property(u => u.Password).HasMaxLength(64).IsRequired();
        builder.Property(u => u.JoinDate).HasDefaultValueSql("GETDATE()").IsRequired();
    }
}
```

And in **OnModelCreating** call this to read all the configuration classes on the current assembly:

```csharp
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
```



##### Password Hash

To password hashing use these funcions:

```csharp
public static byte[] CreateHash(byte[] salt, string valueToHash)
{
    using (var hmac = new HMACSHA512(salt))
    {
        return hmac.ComputeHash(Encoding.UTF8.GetBytes(valueToHash));
    }
}

public static byte[] GenerateSalt()
{
    using (var hmac = new HMACSHA512())
    {
        return hmac.Key;
    }
}

public static bool VerifyHash(string password, byte[] salt, byte[] actualPassword)
{
    using (var hmac = new HMACSHA512(salt))
    {
        var computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(password));
        return computedHash.SequenceEqual(actualPassword);
    }
}
```
  



### How setup integration test?
I prepare a configuration singleton instance to read the **appSettings.Test.json** on the test project, and no the normal **appSettings** from the source project.

```csharp
private static IConfigurationRoot configuration;
private ConfigurationSingleton() { }
public static IConfigurationRoot GetConfiguration()
{
    if (configuration is null)
        configuration = new ConfigurationBuilder()
        .SetBasePath(Path.Combine(Path.GetFullPath("../../../")))
        .AddJsonFile("appsettings.Test.json")
        .AddEnvironmentVariables()
        .Build();


    return configuration;
}
```

We need a DbContext to validate that each endpoint did what it is supposed to, so create a **DbContextFactory:**
```csharp
public DbContextFactory()
{
    var dbBuilder = GetContextBuilderOptions<CleanVidlyDbContext>("vidly_db");

    Context = new CleanVidlyDbContext(dbBuilder.Options);
    Context.Database.Migrate(); //Execute migrations
}
```

Sometimes, this context is not aware of the changes the API did, so we need to refresh the context:

```csharp
public CleanVidlyDbContext GetRefreshContext()
{
    var dbBuilder = GetContextBuilderOptions<CleanVidlyDbContext>("vidly_db");
    Context = new CleanVidlyDbContext(dbBuilder.Options);

    return Context;
}
```

The client created by TestServer sometimes is a little hard to work with, so the **Request** class help to make it a little easier.

```csharp
public JwtAuthentication Jwt => new JwtAuthentication(ConfigurationSingleton.GetConfiguration());

//Returns this to chain with the REST endpoint ex: `request.AddAuth(token).Get("api/sensitiveData");`
public Request<TStartup> AddAuth(string token)
{
    this.client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    return this;
}

/**
 * Also Get, post, put and delete methods
 */
```

Also created a extension method to read the body of the response with more cleaner code
```csharp
public static async Task<T> BodyAs<T>(this HttpResponseMessage httpResponseMessage)
{
    var bodyString = await httpResponseMessage.Content.ReadAsStringAsync();
    return JsonConvert.DeserializeObject<T>(bodyString);
}
```
I just call
```csharp
res.BodyAs<Employees[]>();
```
And that's it! Now the tests...

### How to create integration test correctly?

I always set my test to run on 'happy path', and for each test change what I need to change!

First on each class, implement these interfaces

```csharp
public class CategoriesControllerPostTests : IClassFixture<Request<Startup>>, IClassFixture<DbContextFactory>, IDisposable
```

The constructor will run **BEFORE** each test of the class and the IDisposable is to run the Dispose method **AFTER** each test, 

The **IClassFixture** is to have a single context between tests, so we use to create the Request and DbcontextFactory instances, and create a constructor for them.

```csharp
private readonly Request<Startup> request;
private readonly CleanVidlyDbContext context;
public CategoriesControllerPostTests(Request<Startup> request, DbContextFactory contextFactory)
{
    this.request = request;
    this.context = contextFactory.Context;
}
```

We need to run each test on a clean state... The Dispose method cleans the database after each test.

```csharp
public void Dispose()
{
    context.Categories.RemoveRange(context.Categories);
    context.SaveChanges();
}
```

Now let's write our first integration test!

```csharp
[Fact]
public async Task ShouldSave_Category_IfInputValid()
{
    //Send a request to /api/cateogires with that body
    await request.Post("/api/categories", new { Description = "My description" });
    
    //Select directly to the Db;
    var categoryInDb = await context.Categories.FirstOrDefaultAsync(c => c.Description == "My description");
    
    //Validate that is not null (Thanks FluentAssertions!)
    categoryInDb.Should().NotBeNull();
}
```

But there is a problem with this implementation, the request will fail with status 401 Unauthorized... So we need to make a change...

```csharp
[Fact]
public async Task ShouldSave_Category_IfInputValid()
{
    //Create user to generate JWT token
    var user = new User()
    {
        Email = "",
        Name = "",
        Lastname = "",
        Id = 1
    };

    //Generate token
    var Token = request.Jwt.GenerateToken(user);

    //Send a request to /api/cateogires with that body and token
    await request.AddAuth(Token)Post("/api/categories", new { Description = "My description" });
    
    //Select directly to the Db;
    var categoryInDb = await context.Categories.FirstOrDefaultAsync(c => c.Description == "My description");
    
    //Validate that is not null (Thanks FluentAssertions!)
    categoryInDb.Should().NotBeNull();
}
```

But our test is getting fat and dirty... I prefer a diferent aproach... Let´s make some changes! Extract the request into another method and extract the **Description and Token as a variable**  to change them depending on the test.
We got this:

```csharp
private readonly Request<Startup> request;
private readonly CleanVidlyDbContext context;

private string Description;
private string Token;

public CategoriesControllerPostTests(Request<Startup> request, DbContextFactory contextFactory)
{
    this.request = request;
    this.context = contextFactory.Context;

    Description = "Valid Category"; //Initalize with some valid value!

    var user = new User()
    {
        Email = "",
        Name = "",
        Lastname = "",
        Id = 1
    };

    Token = request.Jwt.GenerateToken(user);
}

public void Dispose()
{
    context.Categories.RemoveRange(context.Categories);
    context.SaveChanges();
}

public Task<HttpResponseMessage> Exec() => request.AddAuth(Token).Post("/api/categories", new { Description = Description });

[Fact]
public async Task ShouldSave_Category_IfInputValid()
{
    //Don't change anything, this is the test of the happy path!
    await Exec(); 
    
    //Should be equal to the variable without change
    var categoryInDb = context.Categories.FirstOrDefault(c => c.Description == Description); 
    
    //Should found the category
    categoryInDb.Should().NotBeNull();
}
```

Everything is good, but how to test other paths? What if no Description is provided? Will it respond with 400 BadRequest?
```csharp
[Fact]
public async Task ShouldReturn_BadRequest400_IfDescription_LessThanFourCharacters()
{
    Description = "a"; //Just change what the test should supose to test and call Exec(); It is cleaner!

    var res = await Exec();
    var body = await res.BodyAs<ValidationErrorResource>(); //It supose to return a error model with the errors!

    res.StatusCode.Should().Be(HttpStatusCode.BadRequest); 
    body.Errors.Should().ContainKey("Description"); //Should return an error for Description (From FluentValidation!)
}
```

It´s simple, clean, self explanatory and the test do exactly what it says, nothing less, nothing more!
The full class is:
```csharp
private readonly Request<Startup> request;
private readonly ITestOutputHelper output;
private readonly CleanVidlyDbContext context;
private string Description;
private string Token;
public CategoriesControllerPostTests(Request<Startup> request, DbContextFactory contextFactory)
{
    this.request = request;
    this.context = contextFactory.Context;

    //Set before each test, remember... The constructor runs before each test
    Description = "Valid Category";  

    var user = new User()
    {
        Email = "",
        Name = "",
        Lastname = "",
        Id = 1
    };

    Token = request.Jwt.GenerateToken(user);

}

public void Dispose()
{
    context.Categories.RemoveRange(context.Categories);
    context.SaveChanges();
}

public Task<HttpResponseMessage> Exec() => request.AddAuth(Token).Post("/api/categories", new { Description = Description });

[Fact]
public async Task ShouldSave_Category_IfInputValid()
{
    await Exec();
    var categoryInDb = context.Categories.FirstOrDefault(c => c.Description == Description);
    categoryInDb.Should().NotBeNull();
}

[Fact]
public async Task ShouldRetuns_Category_IfInputValid()
{
    var res = await Exec();
    var body = await res.BodyAs<KeyValuePairResource>();

    body.Id.Should().BeGreaterThan(0, "Id should by set by EF");
    body.Description.Should().Be(Description, "Is the same description sended on Exec();");
}

[Fact]
public async Task ShouldReturn_BadRequest400_IfDescription_LessThanFourCharacters()
{
    Description = "a";

    var res = await Exec();
    var body = await res.BodyAs<ValidationErrorResource>();

    res.StatusCode.Should().Be(HttpStatusCode.BadRequest);
    body.Errors.Should().ContainKey("Description");
}

[Fact]
public async Task ShouldReturn_BadRequest400_IfDescription_GreaterThanSixtyFourCharacters()
{
    //Create a string of 65 characters long 
    Description = string.Join("a", new char[66]);

    var res = await Exec();
    var body = await res.BodyAs<ValidationErrorResource>();

    res.StatusCode.Should().Be(HttpStatusCode.BadRequest);
    body.Errors.Should().ContainKey("Description");
}

[Fact]
public async Task ShouldReturn_Unauthorized401_IfNoTokenProvided()
{
    Token = "";

    var res = await Exec();

    res.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
}
```


This test all execution paths of our POST endpoint of Categories! Great job!

Setup and mantain integration tests is hard, I hope I've helped!


### Requirements

If you clone the repository you need:

  - .NET Core 2.2 Preview (You can try change the versions on .csproj if you don't want to install the preview)
  - An instance of SQL Server


Inside **src/CleanVidly/** Set user secret for SQL Server connection string with:
```sh
$ dotnet user-secrets set "ConnectionStrings:vidly_db" "Your connection string"
```

Then run
```sh
$ dotnet ef database update
```

To run test you have two options, set an environment variable with your test connection string, or hardcode in **appSettings.Test.json** on the test project, user secrets is just for **development environment.**

If you want to see the full project go to the the Github Repository

{% github https://github.com/lHersey/VidlyIntegrationTest %}

If you know a better aproach please open an issue! I want to learn about your experience! And if you need help please leave a comment!

Thanks!