Autofixture: Congelar uma simulação não substitui uma instância injetada

Criado em 31 jul. 2018  ·  1Comentário  ·  Fonte: AutoFixture/AutoFixture

Comportamento Atual

Ao injetar duas instâncias concretas, a segunda instância substitui a primeira.

Ao injetar uma instância concreta, seguida por uma instância simulada, a segunda instância não substitui a primeira.

Isso torna a ideia de realizar alguns testes que usam uma simulação e alguns testes que usam uma implementação (usando um construtor de classe de teste para compartilhar uma fixação entre os testes) bastante estranha.

Comportamento Esperado

Ao injetar duas instâncias concretas, a segunda instância substitui a primeira.

Ao injetar uma instância concreta, seguida por uma instância simulada, a segunda instância substitui a primeira.

Exemplo de comportamento esperado

`` `C #
using System.Collections.Generic;
usando AutoFixture;
using AutoFixture.AutoMoq;
usando Moq;
using Xunit;

namespace SomeNamespace
{
public class AutoFixtureTests
{
[Facto]
// Passes
public void Should_OverridePreviouslyInjectedString ()
{
const string test1 = "test1";
const string test2 = "test2";

        var fixture = new Fixture();

        fixture.Inject(test1);
        fixture.Inject(test2);

        Assert.Equal(fixture.Create<string>(), test2);
    }

    [Fact]
    // Fails
    public void Should_OverridePreviouslyInjectedInstance()
    {
        var fixture = new Fixture().Customize(new AutoMoqCustomization());

        var sut1 = new List<int>();

        fixture.Inject<IList<int>>(sut1);
        fixture.Freeze<Mock<IList<int>>>();

        var sut2 = fixture.Create<IList<int>>();

        Assert.NotSame(sut1, sut2);
    }
}

}
`` `

question

Comentários muito úteis

Olá @ charles-salmon,

Obrigado por dedicar seu tempo para relatar isso.

O comportamento que você está observando é _por design_. Permita-me explicar. 🙂

Se você observar a implementação do método Freeze<T> :

var value = fixture.Create<T>();
fixture.Inject(value);
return value;

Você verá que tudo o que ele está fazendo é criar um espécime de T e injetá-lo no acessório. Na verdade, o método Freeze surgiu como um atalho útil para essa mesma operação. Como @ploeh escreveu em sua postagem apresentando o método Freeze em 2010:

Descobrimos que usamos tanto esse idioma de codificação que decidimos encapsulá-lo em um método de conveniência. Depois de algum debate, chegamos ao nome Freeze, porque essencialmente congelamos uma única variável anônima no aparelho, contornando o algoritmo padrão para a criação de novas instâncias.

Dado isso, não deve ser surpresa que congelar o mesmo tipo T como uma instância injetada resulta na mesma instância.

Agora, para abordar seu ponto:

Isso torna a ideia de realizar alguns testes que usam uma simulação e alguns testes que usam uma implementação (usando um construtor de classe de teste para compartilhar uma fixação entre os testes) bastante estranha.

Dado que um aparelho representa o _contexto_ no qual um teste é executado, você certamente _pode_ ter vários testes compartilhando o mesmo aparelho ; no entanto, isso vem com o custo de misturar várias preocupações - e possivelmente conflitantes.

O livro xUnit Patterns resume bem :

O maior problema com uma luminária compartilhada é que ela pode levar a "colisões" entre os testes, possivelmente resultando em testes erráticos , uma vez que os testes podem depender dos resultados de outros testes. Outro problema é que um acessório projetado para servir a muitos testes tende a ser muito mais complicado do que o acessório mínimo necessário para um único teste.

Essa "colisão" é exatamente o que acontece quando um teste espera que um objeto do tipo T seja uma instância específica, enquanto outro teste espera que T seja um objeto falso .

Já que não consigo pensar em um cenário de teste onde faça sentido para o mesmo tipo T ser uma simulação _e_ uma instância concreta (embora esteja feliz em provar que estou errado), sugiro que você tenha estes os testes usam acessórios diferentes, cada um configurado para servir ao seu cenário particular.

>Todos os comentários

Olá @ charles-salmon,

Obrigado por dedicar seu tempo para relatar isso.

O comportamento que você está observando é _por design_. Permita-me explicar. 🙂

Se você observar a implementação do método Freeze<T> :

var value = fixture.Create<T>();
fixture.Inject(value);
return value;

Você verá que tudo o que ele está fazendo é criar um espécime de T e injetá-lo no acessório. Na verdade, o método Freeze surgiu como um atalho útil para essa mesma operação. Como @ploeh escreveu em sua postagem apresentando o método Freeze em 2010:

Descobrimos que usamos tanto esse idioma de codificação que decidimos encapsulá-lo em um método de conveniência. Depois de algum debate, chegamos ao nome Freeze, porque essencialmente congelamos uma única variável anônima no aparelho, contornando o algoritmo padrão para a criação de novas instâncias.

Dado isso, não deve ser surpresa que congelar o mesmo tipo T como uma instância injetada resulta na mesma instância.

Agora, para abordar seu ponto:

Isso torna a ideia de realizar alguns testes que usam uma simulação e alguns testes que usam uma implementação (usando um construtor de classe de teste para compartilhar uma fixação entre os testes) bastante estranha.

Dado que um aparelho representa o _contexto_ no qual um teste é executado, você certamente _pode_ ter vários testes compartilhando o mesmo aparelho ; no entanto, isso vem com o custo de misturar várias preocupações - e possivelmente conflitantes.

O livro xUnit Patterns resume bem :

O maior problema com uma luminária compartilhada é que ela pode levar a "colisões" entre os testes, possivelmente resultando em testes erráticos , uma vez que os testes podem depender dos resultados de outros testes. Outro problema é que um acessório projetado para servir a muitos testes tende a ser muito mais complicado do que o acessório mínimo necessário para um único teste.

Essa "colisão" é exatamente o que acontece quando um teste espera que um objeto do tipo T seja uma instância específica, enquanto outro teste espera que T seja um objeto falso .

Já que não consigo pensar em um cenário de teste onde faça sentido para o mesmo tipo T ser uma simulação _e_ uma instância concreta (embora esteja feliz em provar que estou errado), sugiro que você tenha estes os testes usam acessórios diferentes, cada um configurado para servir ao seu cenário particular.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

Ridermansb picture Ridermansb  ·  4Comentários

DeafLight picture DeafLight  ·  5Comentários

ecampidoglio picture ecampidoglio  ·  7Comentários

malylemire1 picture malylemire1  ·  7Comentários

zvirja picture zvirja  ·  4Comentários