| Índice |
|---|
O Construímos o TNF foi construído voltado para que a as 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.
devemos construir um código que possibilite ser testado tentando obter o máximo de cobertura possível.
Vamos conhecer a estrutura que o TNF oferece para criação de nossos testes unitários e integrados.
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.
É um framework free, voltado a comunidade Framework open source, largamente utilizado para realizar testes na plataforma .NET. Ele suporte que suporta todas as tecnologias, atualmente fazendo . Atualmente faz parte do .NET Foundation.
Para sua instalação basta realizar a adição de 2 pacotes via nuget ao seu assembly: xunit e xunit.runner.visualstudio.
Referencia: https://xunit.github.io/
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/
Framework com estrutura de asserção. Junto com o Xunit fornece uma gama de metodos métodos de extensão para realizar os assert asserts 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
...
Framework open source para realização de .NET mocking. Com ele é possível realizar mock de propriedades, classes, interfaces entre outras funcionalidades.
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
...
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
[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 : IDto
{
public IList<string> _expandables { get; set; }
public string Name { get; set; }
} |
...
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
[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 : IDto
{
public IList<string> _expandables { get; set; }
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; }
} |
Nessa camada temos um serviço chamado Serviço 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.
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
// Domain service interface
public interface IWhiteHouseService : IDomainService
{
Task<DtoResponseBase<PresidentDto>>Task<PresidentDto> InsertPresidentAsync(PresidentDto request);
}
// Domain service
public class WhiteHouseService : DomainService<IWhiteHouseRepository>AppDomainService<IWhiteHouseRepository>, IWhiteHouseService
{
private readonly IEventBus _eventBus;
public WhiteHouseService(IWhiteHouseRepository repository, IEventBus eventBus)
: base(repository)
{
_eventBus = eventBus;
}
public Task<DtoResponseBase<PresidentDto>>Task<PresidentDto> InsertPresidentAsync(PresidentDto request)
{
var response = new DtoResponseBase<PresidentDto>();
var builder = new PresidentBuilder()
.WithId(item.Id)
.WithName(item.Name);
var buildpresident = builder.Build();
if (!buildNotification.SuccessHasNotification())
{
response.AddNotifications(build.Notifications);
return responsevar ids = await Repository.InsertPresidentsAsync(new List<President>() { president }, sync);
}
var presidentCreateddto.Id = await Repository.InsertPresidentAsync(presidents);
ids[0];
response.Data = presidentCreated;
// Trigger president created event
// Trigger event
_eventBus.Trigger(new PresidentCreatedEvent(presidentCreatedpresident));
}
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 é disparadoAlém da validação da entidade nosso caso de testes faz uso de Domain Events e dispara um evento após inserir a entidade de presidente.
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
publicinternal class PresidentBuilder : Builder<President> { public PresidentBuilder(INotificationHandler notification) : base(notification) { } public PresidentBuilder(President instance, INotificationHandler notification) : base(instance, notification) { } public PresidentBuilder WithId(string id) { Instance.Id = id; return this; } public PresidentBuilder WithName(string name) { Instance.Name = name; return this; } public protected override BuilderResponse<President>void BuildSpecifications() { // Entity specifications var shouldHaveName = AddSpecification(new PresidentShouldHaveNameSpecification()); if (!shouldHaveName.IsSatisfiedBy(Instance))} { public override var notificationMessage = LocalizationHelper.GetString(President Build() AppConsts.LocalizationSourceName,{ President.Error.PresidentNameMustHaveValue); Response.AddNotification(President.Error.PresidentNameMustHaveValue, notificationMessage base.Validate(); } return base.Build(); } } |
Temos dois serviços Serviços de aplicação em nosso cenário:
...
:
...
...
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
public interface ICountryAppService : IAsyncCrudAppService<CountryDto>
{
} |
| Bloco de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
public interface IWhiteHouseAppService : IApplicationService
{
Task<PagingDtoResponse<PresidentDto>>Task<ListDto<PresidentDto>> GetAllPresidents(GellAllPresidentsRequestDtoGetAllPresidentsDto request);
Task<PresidentDto> GetPresidentById(stringRequestDto<string> id);
Task<DtoResponseBase<List<PresidentDto>>>Task<PresidentDto> InsertPresidentAsync(List<PresidentDto>PresidentDto dtospresident, bool sync = true);
Task<DtoResponseBase>Task<PresidentDto> UpdatePresidentAsync(string id, PresidentDto dtopresident);
Task<DtoResponseBase>Task DeletePresidentAsync(string id);
} |
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 com padrão RESTful para os dois serviços de aplicação criados expostos com ASP NET Core.