Árvore de páginas

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.

...

  • Microsoft.NET.Test.Sdk
  • NSubstitute
  • Shouldly
  • Tnf.TestBase.AppTnf.App.EntityFrameworkCore.TestBase
  • Microsoft.EntityFrameworkCore.InMemory
  • xunit
  • xunit.runner.visualstudio

...

  • Microsoft.NET.Test.Sdk
  • Microsoft.AspNetCore.TestHost
  • NSubstitute
  • Shouldly
  • Tnf.App.AspNetCore.TestBase
  • Tnf.App.EntityFrameworkCore.TestBase
  • xunit
  • xunit.runner.visualstudio

...

Bloco de código
languagec#
firstline1
titleAppTestModule
linenumberstrue
[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
languagec#
firstline1
titleAppTestModule.cs
linenumberstrue
[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
languagec#
firstline1
titleAppTestBase.cs
linenumberstrue
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
languagec#
firstline1
titleWhiteHouseAppService
linenumberstrue
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.

Testes na camada de serviços

...

Bloco de código
languagec#
firstline1
titleAppTestModule.cs
linenumberstrue
[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
languagec#
firstline1
titleStartupTest.cs
linenumberstrue
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?}");
        });
	}
}

...

  • AddEntityFrameworkInMemoryDatabase: Neste ponto configuramos o uso do Entity Framework em memoria para o pipeline do AspNetCore;
  • AddTnfAddTnfApp: adicionamos o suporte ao TNF, deixando explicito que estamos criando uma aplicação AspNetCore para testes passando nossa configuração de modulo (AppTestModule definido anteriormente);

...

Bloco de código
languagec#
firstline1
titleWhiteHouseControllerTests.cs
linenumberstrue
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.

...