
Venho abordando o uso de GitHub Actions como solução para o build e deployment automatizado de projetos há alguns meses, com diversos artigos publicados inclusive no meu blog.
Neste novo artigo dou continuidade ao tema GitHub Actions, abordando desta vez a execução de testes automatizados durante o build e deployment de uma aplicação Web. Esta é uma necessidade bastante frequente em projetos dos mais variados tipos, haja visto a necessidade de validar alterações antes de se proceder com a publicação de uma Web App.
Detalhes da aplicação e do projeto de testes
A aplicação e o projeto de testes foram disponibilizados no seguinte repositório do GitHub:
ASP.NET Core 3.1 + REST API + xUnit + Fluent Assertions
Esta API REST permitirá a conversão de temperaturas em graus Fahrenheit para o equivalente na escala Celsius, com isto acontecendo na classe ConversorTemperaturasController:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace APITemperatura.Controllers
{
[ApiController]
[Route("[controller]")]
public class ConversorTemperaturasController : ControllerBase
{
[HttpGet("Fahrenheit/{temperatura}")]
public object Get(
[FromServices]ILogger<ConversorTemperaturasController> logger,
double temperatura)
{
logger.LogInformation(
$"Recebida temperatura para conversão: {temperatura}");
return new
{
Fahrenheit = temperatura,
Celsius = ConversorTemperatura.FahrenheitParaCelsius(temperatura)
};
}
}
}
Será o método FahrenheitParaCelsius definido no tipo estático ConversorTemperatura o foco dos testes automatizados (há aqui um erro proposital que levará a uma falha nos testes, devido à ausência de arredondamento para 2 casas decimais):
using System;
namespace APITemperatura
{
public static class ConversorTemperatura
{
public static double FahrenheitParaCelsius(double temperatura)
{
if (temperatura < -459.67)
{
throw new Exception(
$"Valor de temperatura em Fahrenheit inválido: {temperatura}");
}
return (temperatura - 32) / 1.8; // Simulação proposital de falha
}
}
}
No projeto APITemperatura.Testes (baseado no template de xUnit para .NET Core) foi definida a classe TestesConversorTemperatura:
- O método TestarConversaoTemperatura faz uso do atributo Theory e receberá vários conjuntos de parâmetros especificados para a execução de testes por meio do atributo InlineData;
- Ao invés de Assert.Equal quando optamos pelo uso de Fluent Assertions a instrução valorCalculado.Should().Be() permitiu a obtenção de um código mais intuitivo, inclusive com a geração de uma mensagem customizada de falha (através do segundo parâmetro fornecido ao método Be()).
using Xunit;
using FluentAssertions;
namespace APITemperatura.Testes
{
public class TestesConversorTemperatura
{
[Theory]
[InlineData(32, 0)]
[InlineData(86, 30)]
[InlineData(90.5, 32.5)]
[InlineData(212, 100)]
[InlineData(0, -17.78)]
public void TestarConversaoTemperatura(
double fahrenheit, double celsius)
{
double valorCalculado =
ConversorTemperatura.FahrenheitParaCelsius(fahrenheit);
valorCalculado.Should().Be(celsius,
"| Falha na conversão de temperaturas: " +
$"Vl. Fahrenheit = {fahrenheit} | " +
$"Vl. Esperado Celsius = {celsius} | " +
$"Vl. Calculado Celsius = {valorCalculado} |");
}
}
}
Ao executarmos os testes a partir do Visual Studio 2019 teremos um resultado similar ao da próxima imagem, sendo possível notar a mensagem customizada especificada via Fluent Assertions:

Criando o workflow para testes, build e deployment automatizados
As configurações para build e deployment de uma aplicação no Azure App Service já foram detalhadas no vídeo indicado no início deste post e também no artigo:
GitHub Actions: exemplos de workflows para build e deployment em várias tecnologias
Para os testes, build e deployment automatizados aqui descritos utilizei o workflow do .NET Core:

A seguir está o código do workflow, já contemplando a execução de testes (linha 27), build (linha 29 em diante) e deployment no Azure App Service (a partir da linha 35):
name: ASP.NET Core 3.1 + Tests + Azure App Service
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
env:
CAMINHO_PROJETO_TESTES: ./APITemperatura.Testes/APITemperatura.Testes.csproj
CAMINHO_PROJETO_WEBAPP: ./APITemperatura
NOME_WEBAPP_AZURE: groffegithubactions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.301
- name: Executar testes automatizados
run: dotnet test ${{ env.CAMINHO_PROJETO_TESTES }} --verbosity minimal
- name: Build com o utilitario dotnet
run: dotnet build ${{ env.CAMINHO_PROJETO_WEBAPP }} --configuration Release
- name: Publish com o utilitario dotnet
run: dotnet publish ${{ env.CAMINHO_PROJETO_WEBAPP }} -c Release -o app
- name: Login no Azure
uses: Azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deployment no Azure App Service
uses: Azure/webapps-deploy@v2
with:
app-name: ${{ env.NOME_WEBAPP_AZURE }}
package: './app'
A instrução dotnet test ${{ env.CAMINHO_PROJETO_TESTES }} –verbosity minimal executará os testes definidos no projeto APITemperatura.Testes, retornando erro num primeiro momento e interrompendo a execução do workflow :

É possível notar nos detalhamentos inclusive a mensagem configurada via Fluent Assertions:

Alterando o método FahrenheitParaCelsius da classe ConversorTemperatura para que realize o arredondamento com 2 casas decimais:
using System;
namespace APITemperatura
{
public static class ConversorTemperatura
{
public static double FahrenheitParaCelsius(double temperatura)
{
if (temperatura < -459.67)
{
throw new Exception(
$"Valor de temperatura em Fahrenheit inválido: {temperatura}");
}
return Math.Round((temperatura - 32) / 1.8, 2);
}
}
}
Ocorrerá uma nova execução do workflow, desta vez com sucesso nos testes e a consequente publicação do projeto no recurso do Azure App Service:

A próxima imagem traz um exemplo de requisição processada pela aplicação de testes (logo após o deployment):
