...
...
...
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
[DependsOn(
typeof(AppModule),
typeof(TnfTestBaseModuleTnfAppTestBaseModule))]
public class EfCoreAppTestModule : TnfModule
{
public override void PreInitialize()
{
Configuration.Modules
.TnfEfCoreInMemory(IocManager.IocContainer)
.RegisterDbContextInMemory<ArchitectureDbContext>();
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
} |
...
Também temos como dependência o modulo "TnfTestBaseModuleTnfAppTestBaseModule" do TNF carregando toda a estrutura de testes integrados.
...
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
[DependsOn(
typeof(AppModule),
typeof(TnfTestBaseModuleTnfAppTestBaseModule))]
public class NSubstituteAppTestModule : TnfModule
{
public override void PreInitialize()
{
// Mock repositories
Configuration.ReplaceService<IWhiteHouseRepository>(() =>
{
var instance = Substitute.For<IWhiteHouseRepository>();
var presidentToInsert = new PresidentDto("1", "New President", "55833479");
instance.InsertPresidentsAsync(Arg.Any<List<PresidentDto>>)
.Returns(Task.FromResult(presidentToInsert));
IocManager.IocContainer.Register(
Component
.For<IWhiteHouseRepository>()
.Instance(instance)
.LifestyleTransient()
);
});
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
} |
O modulo "NSubstituteAppTestModule" foi criado no projeto de testes para 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 , carregando a camada de 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.
Também temos como dependência o modulo "TnfTestBaseModule" do TNF carregando toda TnfAppTestBaseModule" para carregar a estrutura de testes integrados.
O código exemplificado acima, carrega o modulo da camada a ser testada, configurando no método "PreInitialize" com os objetos de mock. No nosso exemplo estamos mocando através da função ReplaceService do TNF usando o framework NSubstitute para criar um objeto de mock para esse repositório e registrá-lo no container de injeção de dependência.
Após a definição de nosso do módulo vamos criar a classe de Setup para cada teste integrado.Para : para isso a classe abaixo nomeada de "NSubstituteAppTestBase" realiza a herança da classe "TnfEfCoreIntegratedTestBase<Module>TnfAppIntegratedTestBase<Module>" que recebe um TnfModule (implementado anteriormente):
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
public class NSubstituteAppTestBase : TnfIntegratedTestBase<NSubstituteAppTestModule>TnfAppIntegratedTestBase<NSubstituteAppTestModule> { } |
A classe TnfIntegratedTestBase está TnfAppIntegratedTestBase está contida no pacote Tnf.App.TestBase.
Definido nosso setup podemos implementar nossa classe e realizar os testes usando nosso repositório criado com NSubstitute.
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
public class WriteHouseAppServiceTests : NSubstituteAppTestBase
{
private readonly IWhiteHouseAppService _whiteHouseAppService;
public WriteHouseAppServiceTests()
{
_whiteHouseAppService = LocalIocManager.Resolve<IWhiteHouseAppService>();
}
[Fact]
public async Task Should_Insert_President_With_Success()
{
// Act
var response = await _whiteHouseAppService.InsertPresidentAsync(new PresidentDto("1", "New President", "12345678"));
// Assert
Assert.TrueFalse(responseLocalNotification.SuccessHasNotification());
}
} |
Podemos perceber que toda a estrutura de Injeção de dependência ainda é mantida inclusive nos testes.
O objeto LocalIocManager contém um wrapper que pode ser acessado para resolver toda a dependencia dependência das camadas carregadas em nosso teste através de nosso modulo que foi definido.
Com o NSubstitute criamos o objeto e então fazemos fazendo sua substituição dentro da estrutura de IoC do framework, possibilitando assim consumir nosso mock.
Assim temos o teste simulando toda a pilha de dependências do teste integrado. Cada teste executado no cenário acima será de forma reproduzido de forma isolada.
...
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
[DependsOn(
typeof(AppModule),
typeof(TnfAspNetCoreTestBaseModuleTnfAppAspNetCoreTestBaseModule))]
public class AppTestModule : TnfModule
{
public override void PreInitialize()
{
Configuration.Auditing.IsEnabledForAnonymousUsers = true;
// Mock repositories
Configuration.ReplaceService<IWhiteHouseRepository, WhiteHouseRepositoryMock>();
Configuration.Modules
.TnfEfCoreInMemory(IocManager.IocContainer, IocManager.Resolve<IServiceProvider>())
.RegisterDbContextInMemory<ArchitectureDbContext>();
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(AppTestModule).GetAssembly());
}
}
} |
...
Nosso modulo agora contem dependências diferente pois vamos testar uma camada de AspNetCore e por isso devemos deixar explicito em nosso atributo "DependsOn" o uso do modulo TnfAspNetCoreTestBaseModuleTnfAppAspNetCoreTestBaseModule.
Como estamos criando um teste para uma aplicação ASP .NET Core que expoe expõe nossas APIs, temos que criar uma classe de startup que fará a configuração de nosso pipeline:
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
public class StartupTest
{
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddApplicationPart(typeof(TnfAspNetCoreModule).GetAssembly())
.AddMvcCore() .AddApplicationPart(typeof(Tnf.Architecture.Web.Startup.WebModule).GetAssembly())
.AddControllersAsServices();
services.AddEntityFrameworkInMemoryDatabase();
// Configure Tnf and Dependency Injection
return services.AddTnf<AppTestModule>AddTnfApp<AppTestModule>(options =>
{
// Test setup
options.SetupTest();
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseTnf(); //Initializes Tnf framework.
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
} |
...
...
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
public class WhiteHouseControllerTests : AppTestBase
{
[Fact]
public void Should_Resolve_Controller()
{
ServiceProvider.GetService<WhiteHouseController>().ShouldNotBeNull();
}
[Fact]
public async Task GetAll_Presidents_With_Success()
{
// Act
var response = await GetResponseAsObjectAsync<AjaxResponse<PagingDtoResponse<PresidentDto>>>(
GetResponseAsObjectAsync<ListDto<PresidentDto>>(
"/api/white-house?offset=0&pageSize=10",
HttpStatusCode.OK
$"{RouteConsts.WhiteHouse}?pageSize=5",
HttpStatusCode.OK
);
// Assert
Assert.True(response.Success);
Assert.Equal(response.Result.DataItems.Count, 65);
response.Result.Notifications.ShouldBeEmpty();
}
[Fact]
public async Task GetAll_Presidents_With_Invalid_Parameters_Return_Bad_Request()
{
// Act
var response = await GetResponseAsObjectAsync<AjaxResponse<string>>(
"/api/white-house?offset=0&pageSize=0",
HttpStatusCode.BadRequest
);
response.Success.ShouldBeTrue();
response.Result.ShouldBe($"Invalid parameter: pageSize"GetResponseAsObjectAsync<ErrorResponse>(
$"{RouteConsts.WhiteHouse}",
HttpStatusCode.BadRequest
);
// Assert
response.Message.ShouldBe("GetAllPresident");
response.DetailedMessage.ShouldBe("GetAllPresident");
Assert.True(response.Details.Any(n => n.Message == Error.InvalidParameter.ToString()));
}
} |
Em nossa classe de teste temos acesso a alguns métodos pela herança da classe TnfAspNetCoreIntegratedTestBase contida dentro do pacote Tnf.App.AspNetCore.TestBase para criação de testes em serviços.
...