Initializing the testing class

Let's proceed by creating a new testing class in our Catalog.Infrastructure.Tests project called ItemRepositoryTests:

using Xunit;

namespace Catalog.Infrastructure.Tests
{
public class ItemRepositoryTests
{
[Fact]
public void should_get_data()
{
Assert.True(true);
}
}
}

The Xunit framework identifies test classes using the Fact attribute. Every class that contains a method that has the Fact attribute or, as we'll see later in this section, the Theory attribute, will be considered as a test by the unit test runner. 

Let's continue by adding our first test method. This checks the GetAsync method of the ItemRepository class:

using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Shouldly;
using Catalog.Infrastructure.Repositories;
using Xunit;

namespace Catalog.Infrastructure.Tests
{
public class ItemRepositoryTests
{
[Fact]
public async Task should_get_data()
{
var options = new DbContextOptionsBuilder<CatalogContext>()
.UseInMemoryDatabase(databaseName: "should_get_data")
.Options;

await using var context = new TestCatalogContext(options);
context.Database.EnsureCreated();

var sut = new ItemRepository(context);
var result = await sut.GetAsync();

result.ShouldNotBeNull();
}
}
}

This code initializes a new Options object using DbContextOptionsBuilder<T>, which is of the CatalogContext type. It also uses the UseInMemoryDatabase extension method to create a new in-memory database instance with a given name. Since DbContext is extended by the CatalogContext class, which implements the IAsyncDisposable type, it is possible to use the await using var keywords. This approach avoids any type of nesting and provides a cleaner way of reading code, by avoiding the any use of nesting:

...
using (var context = new TestCatalogContext(options))
{
context.Database.EnsureCreated();
var sut = new ItemRepository(context);

var result = await sut.GetAsync();

result.ShouldNotBeNull();
}
...

To build the code, it is necessary to add the following package to the Catalog.Infrastructure.Tests project:

dotnet add package Microsoft.EntityFrameworkCore.InMemory
The UseInMemoryDatabase extension method is useful for configuring a new in-memory database instance. It is important to note that it is not designed to be a relational database. Furthermore, it doesn't perform any database integrity checks or constraint checks. For more appropriate testing, we should use the in-memory version of SQLite. You can find more information about the SQLite provider in the following documentation: https://docs.microsoft.com/en-us/ef/core/miscellaneous/testing/sqlite.

After the creation of a new Options object, the should_get_data method creates a new instance of TestCatalogContext, and calls the EnsureCreated() method, which ensures that the context exists in the in-memory database. The EnsureCreate method also implicitly calls the OnModelCreating method. After that, the test initializes a new ItemRepository by using the context and executes the GetAsync method. Finally, it checks the result using result.ShouldNotBeNull().

Note that all test examples in this book use Shouldly as an assertion framework. Shouldly focuses on giving error messages that are concise and straightforward when an assertion fails. It is possible to avoid the use of Shouldly by using the default assertion framework built-in to .NET Core. You can find more information about Shouldly from the following link: https://github.com/shouldly/shouldly. It is possible to add the Shouldly package executing the following CLI instruction in the Catalog.Infrastructure.Tests project: dotnet add package Shouldly.

Let's continue by implementing tests for all the methods implemented in the ItemRepository class:

using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Shouldly;
using Catalog.Infrastructure.Repositories;
using Xunit;

namespace Catalog.Infrastructure.Tests
{
public class ItemRepositoryTests
{
[Fact]
public async Task should_get_data()
{
var options = new DbContextOptionsBuilder<CatalogContext>()
.UseInMemoryDatabase("should_get_data")
.Options;

await using var context = new TestCatalogContext(options);
context.Database.EnsureCreated();

var sut = new ItemRepository(context);
var result = await sut.GetAsync();

result.ShouldNotBeNull();
}

[Fact]
public async Task should_returns_null_with_id_not_present()
{
var options = new DbContextOptionsBuilder<CatalogContext>()
.UseInMemoryDatabase(databaseName:
"should_returns_null_with_id_not_present")
.Options;

await using var context = new TestCatalogContext(options);
context.Database.EnsureCreated();

var sut = new ItemRepository(context);
var result = await sut.GetAsync(Guid.NewGuid());

result.ShouldBeNull();
}

[Theory]
[InlineData("b5b05534-9263-448c-a69e-0bbd8b3eb90e")]
public async Task should_return_record_by_id(string guid)
{
var options = new DbContextOptionsBuilder<CatalogContext>()
.UseInMemoryDatabase(databaseName:
"should_return_record_by_id")
.Options;

await using var context = new TestCatalogContext(options);
context.Database.EnsureCreated();

var sut = new ItemRepository(context);
var result = await sut.GetAsync(new Guid(guid));

result.Id.ShouldBe(new Guid(guid));
}
...

The preceding snippet defines tests that cover the GetAsync methods. The first method, should_get_data, tests the GetAsync() overload with no parameters, while the second method tests the GetAsync(guid id) overload. In both cases, we use InMemoryDatabase to emulate the underlying data source. In the same ItemRepositoryTests class, it is also possible to define test cases related to create/update actions:

...
[Fact]
public async Task should_add_new_item()
{
var testItem = new Item
{
Name = "Test album",
Description = "Description",
LabelName = "Label name",
Price = new Price { Amount = 13, Currency = "EUR" },
PictureUri = "https://mycdn.com/pictures/32423423",
ReleaseDate = DateTimeOffset.Now,
AvailableStock = 6,
GenreId = new Guid("c04f05c0-f6ad-44d1-a400-3375bfb5dfd6"),
ArtistId = new Guid("f08a333d-30db-4dd1-b8ba-3b0473c7cdab")
};

var options = new DbContextOptionsBuilder<CatalogContext>()
.UseInMemoryDatabase("should_add_new_items")
.Options;

await using var context = new TestCatalogContext(options);
context.Database.EnsureCreated();

var sut = new ItemRepository(context);

sut.Add(testItem);
await sut.UnitOfWork.SaveEntitiesAsync();

context.Items
.FirstOrDefault(_ => _.Id == testItem.Id)
.ShouldNotBeNull();
}

[Fact]
public async Task should_update_item()
{
var testItem = new Item
{
Id = new Guid("b5b05534-9263-448c-a69e-0bbd8b3eb90e"),
Name = "Test album",
Description = "Description updated",
LabelName = "Label name",
Price = new Price { Amount = 50, Currency = "EUR" },
PictureUri = "https://mycdn.com/pictures/32423423",
ReleaseDate = DateTimeOffset.Now,
AvailableStock = 6,
GenreId = new Guid("c04f05c0-f6ad-44d1-a400-3375bfb5dfd6"),
ArtistId = new Guid("f08a333d-30db-4dd1-b8ba-3b0473c7cdab")
};

var options = new DbContextOptionsBuilder<CatalogContext>()
.UseInMemoryDatabase("should_update_item")
.Options;

await using var context = new TestCatalogContext(options);
context.Database.EnsureCreated();

var sut = new ItemRepository(context);
sut.Update(testItem);

await sut.UnitOfWork.SaveEntitiesAsync();

context.Items
.FirstOrDefault(x => x.Id == testItem.Id)
?.Description.ShouldBe("Description updated");
}
...
}

Finally, the ItemRepositoryTests class provides test coverage for all CRUD methods implemented by the ItemRepository class. The should_get_data, should_returns_null_with_id_not_present, and should_return_record_by_id methods execute the GetAsync method and check whether the result is what we expect. The should_add_new_item and should_update_item test cases provide test coverage for the ItemRepository.Add and ItemRepository.Update methods. Both the tests initialize a new record of type Item and they update the database through the methods exposed by the ItemRepository type.

As a result, we can run our tests by executing the following command in the Catalog.Infrastructure.Tests folder:

 dotnet test

The preceding command executes tests implemented in the project. Therefore, the result will be a report with a list of tests that have succeeded. As an alternative, we can also choose to run tests using the tests runner provided by the IDE. Now that we have completed the data access part using EF Core combined with the code-first approach, we can also take a quick look at Dapper, and how it can be useful by providing a more lighter way to access data. 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.179.186