...
Cada teste executado no cenário acima será de forma reproduzido de forma isolada.
Quando estamos trabalhando com repositórios sem o uso de um ORM por exemplo, precisamos fazer o mock da interface do repositório simulando um cenário afim de explorar todo o funcionamento do sistema.
Para isso o TNF possui uma extensão onde é possível realizar a substituição de uma injeção e usar o NSubstitute para criar objeto de mock para substitui-lo no injetor de dependência.
Para o seguinte cenário de testes que iremos construir considere a seguinte entidade e Dto:
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
[AutoMap(typeof(PresidentDto))]
public class President : Entity<string>
{
public const int MaxNameLength = 256;
public string Name { get; internal set; }
public ZipCode ZipCode { get; internal set; }
public enum Error
{
Unexpected = 0,
InvalidId = 1,
PresidentNameMustHaveValue = 2,
PresidentZipCodeMustHaveValue = 3,
}
}
public class PresidentDto
{
public PresidentDto()
{
}
public PresidentDto(string id, string name, string zipCode)
{
Id = id;
Name = name;
ZipCode = new ZipCode(zipCode);
}
public string Id { get; set; }
public string Name { get; set; }
public ZipCode ZipCode { get; set; }
} |
Para realizar teste em cenários com o uso mocks o TNF prove alguns facilitadores.
Vamos começar com a criação do modulo que fará o carregamento da estrutura de testes:
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
[DependsOn(
typeof(AppModule),
typeof(TnfTestBaseModule))]
public class AppTestModule : TnfModule
{
public override void PreInitialize()
{
// Mock repositories
Configuration.ReplaceService<IWhiteHouseRepository>(() =>
{
var instance = Substitute.For<IWhiteHouseRepository>();
var presidentsToInsert = new List<PresidentDto>()
{
new PresidentDto("1", "New President", "55833479")
};
instance.InsertPresidentsAsync(presidentsToInsert)
.Returns(Task.FromResult(presidentsToInsert));
IocManager.IocContainer.Register(
Component
.For<IWhiteHouseRepository>()
.Instance(instance)
.LifestyleTransient()
);
});
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
} |
O modulo "AppTestModule" foi criado no projeto de testes sendo responsável por carregar toda a estrutura de sua aplicação para o teste integrado.
Note que o atributo do modulo "DependsOn" tem como referencia outro modulo chamado "AppModule". Este modulo é um modulo da camada de aplicação de nossa aplicação concreta a ser testada. Esse modulo também contém dependências de outras camadas como domínio e infraestrutura onde estão definidos nossos repositórios de dados.
O código exemplificado acima, carrega o modulo da camada a ser testada, configurando no método "PreInitialize" os objetos de mock. No nosso exemplo estamos mocando
Através da função ReplaceService do TNF podemos usar o framework NSubstitute para então criar um objeto de mock para esse repositório e registrá-lo no container de injeção de dependência.
Para criação do Setup de cada teste o TNF disponibiliza uma classe abstrata chamada "TnfIntegratedTestBase<Module>" que recebe um TnfModule ("AppTestModule" vide exemplo anterior):
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
public class AppTestBase : TnfIntegratedTestBase<AppTestModule>
{
} |
A classe TnfIntegratedTestBase está contida no pacote Tnf.TestBase em nosso feed: "https://www.myget.org/F/tnf/api/v3/index.json"
Definida a classe que fará o setup de cada teste criado, podemos começar a escrita de nossos testes sobre os serviços de aplicação:
| Bloco de código | ||||||
|---|---|---|---|---|---|---|
| ||||||
public interface IWhiteHouseAppService : IApplicationService
{
Task<DtoResponseBase<PresidentDto>> InsertPresidentAsync(PresidentDto dto);
} |
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
public class WhiteHouseAppService : ApplicationService, IWhiteHouseAppService
{
private readonly IWhiteHouseService _whiteHouserService;
public WhiteHouseAppService(IWhiteHouseService whiteHouserService)
{
_whiteHouserService = whiteHouserService;
}
public async Task<DtoResponseBase<PresidentDto>> InsertPresidentAsync(PresidentDto dto)
{
var response = await _whiteHouserService.InsertPresidentAsync(dto);
return response;
}
} |
Os serviços de aplicação consomem via injeção de dependência os serviços de domínio executando as validações presentes no builder da entidade:
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
public interface IWhiteHouseService : IDomainService
{
Task<DtoResponseBase<PresidentDto>> InsertPresidentAsync(PresidentDto president);
} |
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
public class WhiteHouseService : DomainService<IWhiteHouseRepository>, IWhiteHouseService
{
public WhiteHouseService(IWhiteHouseRepository repository)
: base(repository)
{
}
public async Task<DtoResponseBase<PresidentDto>> InsertPresidentAsync(PresidentDto president)
{
var response = new DtoResponseBase<PresidentDto>();
var builder = new PresidentBuilder()
.WithId(item.Id)
.WithName(item.Name)
.WithZipCode(item.ZipCode);
var build = builder.Build();
if (!build.Success)
response.AddNotifications(build.Notifications);
if (response.Success)
{
president = await Repository.InsertPresidentsAsync(president);
response.Data = president;
}
return response;
}
} |
Caso as regras de negocio sejam satisfeitas, o repositório é consumido.
Em nossos casos de teste, fazemos o mock no ultimo nível de infra estrutura, usando o NSubstitute para realizar o mock da interface IWhiteHouseRepository.
Cada teste executado no cenário acima será de forma reproduzido de forma isolada.