...
...
...
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.
...