Software Architect

Breaking Changes em Autofac.Extras.Moq

Se, tal como eu, você é fã do Autofac e Moq, populares frameworks de, respetivamente, injeção de dependência e mocking para .NET, provavelmente já conhece o Automoq. Com Automoq nossos testes tendem a ficar bem mais concisos, sem toda a cerimónia inerente à utilização de um framework como Moq. Um teste que tradicionalmente seria algo do género:

[Fact]
public void TestingWithMoq()
{
    var oneMockedService = new Mock<IOneService>();
    oneMockedService.Setup(x => x.DoStuff(It.Is<string>((s) => s.Equals("hello"))))
            .Returns("World");

    var anotherMockedService = new Mock<IAnotherService>();
    anotherMockedService.Setup(x => x.DoAnotherThing(It.IsAny<string>()));

    var app = new AmazingApp(oneMockedService.Object, anotherMockedService.Object);

    var result = app.Execute("hello");
    
    result.ShouldBe("World");
}

Com Automoq fica:

[Fact]
public void TestingWithAutoMoq()
{
    using (var mock = AutoMock.GetLoose()) 
    {
        mock.Mock<IOneService>()
            .Setup(x => x.DoStuff(It.Is<string>((s) => s.Equals("hello"))))
            .Returns("World");

        var result = mock.Create<AmazingApp>().Execute("hello");

        result.ShouldBe("World");
    }
}

Na minha modesta opinião, muito melhor. Só tenho que me preocupar em fazer o setup das dependências que realmente me interessam, e para as outras o Automoq fornece uma implementação padrão. Como nem tudo é 0800, quando temos que testar uma classe em que uma das dependências é uma instância concreta, temos um pouco de trabalho extra. Antes da versão 5, podíamos utilizar mock.Provide para indicar ao framework que pretendemos utilizar uma implementação concreta da nossa dependência:

Nesse caso, IOneService continua sendo um Mock, mas para IAnotherService o framework injetará a implementação AnotherServiceFake.

[Fact]
public void TestingWithProvide()
{
    using (var mock = AutoMock.GetStrict())
    {
        mock.Mock<IOneService>()
            .Setup(x => x.DoStuff(It.Is<string>((s) => s.Equals("hello"))))
            .Returns("World");

        mock.Provide<IAnotherService>(new AnotherServiceFake());

        var result = mock.Create<AmazingApp>().Execute("hello");

        result.ShouldBe("World");
    }
}

Com a versão 5, a extensão Provide já não existe. No seu lugar, podemos fazer a configuração passando uma Action para configurar o container logo na criação do Automock:

[Fact]
public void TestingWithBuilderAction()
{
    using (var mock = AutoMock.GetStrict(
            builder => builder.RegisterInstance(new AnotherServiceFake()).As<IAnotherService>())
        )
    {
        mock.Mock<IOneService>()
            .Setup(x => x.DoStuff(It.Is<string>((s) => s.Equals("hello"))))
            .Returns("World");

        var result = mock.Create<AmazingApp>().Execute("hello");

        result.ShouldBe("World");
    }
}

Esta última opção se torna ainda mais poderosa quando nos apercebemos que o Automoq expõe o ContainerBuilder, e com ele podemos fazer toda a nossa configuração de dependência, incluíndo com a utilização de Modules. Mas isso fica outro post!