Press enter to see results or esc to cancel.

Testes mais legíveis com FluentAssertions em projetos nUnit.Xamarin

Eu acredito que ter testes automatizados bem escritos para a aplicação que você está desenvolvendo é essencial para que ela seja manutenível com qualidade no futuro, levo isso bem a sério em todos projetos em que participo, adicionando todos os testes que julgo necessário para o projeto (unidade, integração, funcional, performance, etc.). As duas ferramentas citadas no título ajudam nesses pontos: o nUnit.Xamarin é a versão do nUnit que permite ter testes integrados em aparelhos para aplicativos móveis, e o FluentAssertions é a lib que permite escrever testes de uma maneira mais fluida e legível. Mas os dois não se dão muito bem juntos, é preciso fazer alguns ajustes extras pra que eles funcionem perfeitamente, e é isso que vou mostrar nesse post: como configurar o FluentAssertions pra se comportar direitinho dentro de um projeto de testes com nUnit.Xamarin.

Se você nunca ouviu falar dessas ferramentas, não tem problema, eu faço uma breve introdução de cada uma.

nUnit.Xamarin

Se você já escreveu testes pra .NET alguma vez já deve ter ouvido falar do nUnit, ele é um framework de testes automatizados que começou como um port para .NET do jUnit, é um dos frameworks mais usados para escrita de testes no mundo .NET.

Como o nome sugere, o nUnit.Xamarin é uma versão do nUnit que consegue rodar dentro de aplicativos iOS e Android com a ajuda do Xamarin, ou seja, com isso você é capaz de rodar qualquer teste escrito com nUnit dentro de um aparelho ou simulador/emulador rodando iOS ou Android, o que o torna uma ótima ferramenta para testes de integração em que você precise validar como um código se comporta quando precisa interagir com as APIs reais do sistema operacional.

Com um teste escrito apenas uma vez é possível rodá-lo no iOS:

nunit.xamarin rodando no iOS

E no Android:

nunit.xamarin rodando no Android

FluentAssertions

O FluentAssertions é uma lib bem simples (porém poderosa) que permite escrever testes automatizados no .NET com uma sintaxe muito mais próxima do que falamos na vida real.

A forma como os frameworks de testes propõem que os Asserts sejam feitos não é muito natural para a leitura de humanos, veja esse exemplo:

Assert.AreEqual("Título do Produto", produto.Título);

O código acima não me agrada por dois motivos: o primeiro é que eu nunca lembro qual dos dois argumentos é o valor esperado e qual é o valor obtido. O segundo motivo, e o pior, é que é não é nem um pouco natural ler essa linha:

Verifique que são iguais: 'Título do Produto', produto.Título

Gif com cara de nojo

Comparando com o mesmo assert no FluentAssertions:

produto.Título.Should().Be("Título do Produto");
produto.Título deve ser "Título do Produto"

Gif com cara de nojo

Bem mais natural, né?

O FluentAssertions também dá mensagens de erro melhores, pra ajudar a encontrar o erro no código mais rápido, e as vantagens só aumentam à medida que a complexidade dos asserts também aumenta.

Ele vem com um monte de métodos pra ajudar na escrita dos testes, dá pra conferir todos eles aqui.

Juntando os dois

Dado que eu só escrevo asserts com FluentAssertions, quando comecei a usar o nUnit.Xamarin a primeira coisa que fiz foi adicionar a biblioteca pra ter o mesmo padrão de assertions e mensagens de erro em todos meus projetos de testes. O problema é que o FluentAssertions não sabe rodar dentro do nUnit.Xamarin, e isso resulta numa tentativa de carregar recursos que não existem dentro da plataforma do Xamarin, o que torna as mensagens de erro completamente inúteis pra diagnosticar o problema. Por exemplo, este teste falhando:

[Test]
public void ListaDeveConter42()
{
    var lista = new List<int> { 1, 42, 100 };

    lista.Should().Contain(50);
}

Deveria retornar essa mensagem de erro:

Expected lista {1, 42, 100} to contain 50.

Mas acaba retornando isso, sempre que está rodando em um aparelho físico ou emulador Android:

System.Configuration.ConfigurationErrorsException: Configuration system failed to initialize ----> System.PlatformNotSupportedException: Operation is not supported on this platform.

Solução

Sem entrar em muitos detalhes, o FluentAssertions se perde na hora de repassar o erro para o nUnit.Xamarin. Por conta disso existe um pacote NuGet que arruma esse problema, mas ele te obriga a utilizar uma versão muito desatualizada do FluentAssertions, que não possui muitas funcionalidades importantes, então eu queria uma solução melhor.

Depois de algum tempo lendo o código do FluentAssertions para entender o motivo do problema, consegui fazer o FluentAssertions expor a mensagem esperada quando estiver rodando no celular sem precisar utilizar o pacote específico de Xamarin, permitindo que você matenha o FluentAssertions na versão mais atualizada, com todas as melhorias (pelo menos até que essa issue seja fechada).

Só é preciso utilizar uma ConfigurationStore que não tente usar APIs do .NET Core, já que elas não estão disponíveis no projeto Xamarin. O próprio FluentAssertions permite fazer isso com uma NullConfigurationStore, mas ela é internal, então copiei o código numa classe:

public class NullConfigurationStore : IConfigurationStore
{
    public string GetSetting(string name)
    {
        return null;
    }
}

E passei a utilizar um método de SetUp para garantir que essa linha sempre fosse executada no início do teste:

public class TesteBase
{
    [SetUp]
    public void SetUp()
    {
        Services.ConfigurationStore = new NullConfigurationStore();
    }
}

Com o teste herdando da minha classe base:

public class Test : TesteBase
{
    [Test]
    public void ListaDeveConter42()
    {
        var lista = new List<int> { 1, 42, 100 };

        // Esse assert está errado de propósito, para mostrar que a mensagem
        // de erro é exibida corretamente.
        lista.Should().Contain(50);
    }
}

Pronto! Agora o FluentAssertions não se perde mais e a mensagem de erro é exibida corretamente:

mensagem de erro exibida corretamente

É comum eu precisar fazer algum ajuste simples como esse para que algum framework ou biblioteca que não conhece o Xamarin funcione nele, graças às ferramentas open-source é possível entender o que precisa ser feito só lendo o código-fonte da biblioteca usada. Se quiser, o sample completo desse post pode ser visto aqui.

Imagem usada no post: Sai Kiran Anagani no Unsplash

Tweet about this on TwitterShare on FacebookShare on LinkedInEmail this to someone
Comments

Leave a Comment