Árvore de páginas

Você está vendo a versão antiga da página. Ver a versão atual.

Comparar com o atual Ver Histórico da Página

« Anterior Versão 5 Próxima »

O TNF foi construído voltado para que a boas praticas e os princípios de um código limpo fossem respeitados.

Para que isso aconteça um dos principais pontos a serem abordados aqui nesta seção são os testes de unidade e testes integrados.

Nesse tópico será descrito como realizar os testes de unidade, validando regra a regra de uma aplicação construída com o TNF também como os testes integrados passando de camada a camada de sua aplicação.

Frameworks

O TNF prove algumas estruturas para realização de testes em sua aplicação. Não é uma regra, mas sugerimos os seguintes frameworks para utilização em seu projeto.

Xunit.net

É um framework free, voltado a comunidade open source, largamente utilizado para realizar testes na plataforma .NET. Ele suporte todas as tecnologias, atualmente fazendo parte do .NET Foundation.

Para sua instalação basta realizar a adição de 2 pacotes via nuget ao seu assembly: xunitxunit.runner.visualstudio.

Referencia: https://xunit.github.io/

NSubstitute

Consiste em um framework para realização de .NET mocking. Com ele é possível realizar mock de propriedades, classes, interfaces entre outras funcionalidades. 

Para sua utilização, basta realizar a instalação do pacote NSubstitute via interface nuget.

Referencia: http://nsubstitute.github.io/

Shouldly

Framework com estrutura de asserção. Junto com o Xunit fornece uma gama de metodos de extensão para realizar os assert e validações de regras, objetos e comportamentos.

Para sua utilização, basta realizar a instalação do pacote Shouldly via interface nuget.

Referencia: https://github.com/shouldly/shouldly

Cenário de testes

Para todos os testes descritos a seguir considere o seguinte cenário:

Todos os cenários descritos a seguir estão dispostos em nosso github no endereço: https://github.com/totvsnetcore/tnf-samples/tree/master/TnfSample-Architecture

Camada de infraestrutura

Nossa aplicação contem em sua camada de infraestrutura dois repositórios de dados: Entity Framework Core e Carol (SAS desenvolvido pela TOTVS).

Entidade no Entity Framework Core e Dto:

Country.cs
[AutoMap(typeof(CountryDto))]
[Table("Countries")]
public class Country : Entity
{
	public const int MaxNameLength = 256;
    [Required]
    [MaxLength(MaxNameLength)]
    public string Name { get; set; }
    public Country()
    {
    }
    public Country(int id, string name)
    {
    	Id = id;
        Name = name;
    }
}
 
public class CountryDto
{
	public string Name { get; set; }
}

Entidade no Carol e Dto:

PresidentEntity.cs
[JsonObject("president")]
public class PresidentEntity : CarolEntity
{
	public string Name { get; set; }
    public string ZipCode { get; set; }
    public override object GetStagingMapping()
    {
    	return new
        {
        	name = "string",
            zipCode = "string"
        };
	}
}
 
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; }
}

Camada de Domínio

Nessa camada temos um serviço chamado WhiteHouseService responsável pela persistência das entidade de presidentes no Carol.

Esse serviço realiza a validação da entidade de domínio President, utilizando o padrão de builder do TNF com o uso de specifications.

Abaixo temos a implementação do serviço WhiteHouseService usando um repositório chamado IWhiteHouseRepositoriy implementado pela camada de infraestrutura consumindo dados do Carol.

WhiteHouseService.cs
// Domain service interface
public interface IWhiteHouseService : IDomainService
{
    Task<DtoResponseBase<PresidentDto>> InsertPresidentAsync(PresidentDto request);
}
 
// Domain service
public class WhiteHouseService : DomainService<IWhiteHouseRepository>, IWhiteHouseService
{
    private readonly IEventBus _eventBus;
 
    public WhiteHouseService(IWhiteHouseRepository repository, IEventBus eventBus)
        : base(repository)
    {
        _eventBus = eventBus;
    }
 
    public Task<DtoResponseBase<PresidentDto>> InsertPresidentAsync(PresidentDto request)
    {
        var response = new DtoResponseBase<PresidentDto>();
        var builder = new PresidentBuilder()
            .WithId(item.Id)
            .WithName(item.Name);
 
        var build = builder.Build();
        if (!build.Success)
        {
            response.AddNotifications(build.Notifications);
            return response;
        }
 
        var presidentCreated = await Repository.InsertPresidentAsync(presidents);
        response.Data = presidentCreated;
 
        // Trigger event
        _eventBus.Trigger(new PresidentCreatedEvent(presidentCreated));
        return response;
    }
}

Como podemos observar no código acima quando é feita a inserção de um presidente através do serviço a estrutura de builder é consumido validando assim se a entidade pode ou não utilizar o repositório de dados no Carol.

Caso a entidade esteja apta a ser cadastrada os dados são persistidos no repositório e um evento de criação de um presidente é disparado.

PresidentBuilder.cs
public class PresidentBuilder : Builder<President>
{
    public PresidentBuilder()
        : base()
    {
    }
    public PresidentBuilder(President instance)
        : base(instance)
    {
    }
    public PresidentBuilder WithId(string id)
    {
        Instance.Id = id;
        return this;
    }
    public PresidentBuilder WithName(string name)
    {
        Instance.Name = name;
        return this;
    }
    public override BuilderResponse<President> Build()
    {
        // Entity specifications
        var shouldHaveName = new PresidentShouldHaveNameSpecification();
 
        if (!shouldHaveName.IsSatisfiedBy(Instance))
        {
            var notificationMessage = LocalizationHelper.GetString(
                AppConsts.LocalizationSourceName,
                President.Error.PresidentNameMustHaveValue);
            Response.AddNotification(President.Error.PresidentNameMustHaveValue, notificationMessage);
        }
 
        return base.Build();
    }
}

Camada de aplicação

Temos dois serviços de aplicação em nosso cenário:

  • WhiteHouseAppService: Consumindo dados do Carol
  • CountryAppService: Consumindo dados do Entity Framework Core utilizando a criação de CRUD padrão do TNF.

Abaixo podemos visualizar as interfaces de cada serviço de nosso cenário:

ICountryAppService.cs
public interface ICountryAppService : IAsyncCrudAppService<CountryDto>
{
}
IWhiteHouseAppService.cs
public interface IWhiteHouseAppService : IApplicationService
{
	Task<PagingDtoResponse<PresidentDto>> GetAllPresidents(GellAllPresidentsRequestDto request);
    Task<PresidentDto> GetPresidentById(string id);
    Task<DtoResponseBase<List<PresidentDto>>> InsertPresidentAsync(List<PresidentDto> dtos, bool sync = true);
    Task<DtoResponseBase> UpdatePresidentAsync(PresidentDto dto);
    Task<DtoResponseBase> DeletePresidentAsync(string id);
}

Camada de Serviços

Agora é a hora de expormos toda a nossa arquitetura criada.

Para isso temos uma camada de serviços feita em AspNetCore com duas APIs:

  • api/countries:
    • GetAll
    • Get
    • Post
    • Put
    • Delete
  • api/white-house:
    • GetAll
    • Get
    • Post
    • Put
    • Delete
  • Sem rótulos