Autofixture: NSubstitute.ReceivedCalls () retornando valor errado usando AutoFixture.AutoNSubstitute

Criado em 28 mar. 2018  ·  10Comentários  ·  Fonte: AutoFixture/AutoFixture

Temos muitos testes usando 'Class.ReceivedCalls ()' e 'var tmp = Class.Received (int) .Property;' para verificar a contagem de chamadas. Na v3 do AutoFixture ele relatou corretamente a contagem de chamadas, especialmente de Propriedades, o que não é mais feito. A contagem de chamadas dos métodos parece ainda estar ok.
Dado que temos o seguinte código:

    public interface IRunSpeed
    {
        int Speed { get; }
    }

    public class GetToDaChoppa
    {
        private readonly IRunSpeed _runSpeed;

        public GetToDaChoppa(IRunSpeed runSpeed)
        {
            _runSpeed = runSpeed ?? throw new ArgumentNullException(nameof(runSpeed));
        }

        public void DoItNow()
        {
            var runningSpeed = _runSpeed.Speed;
        }
    }

E dado que temos os seguintes testes:

        [Fact]
        public void DoItNow_WithOutAutoNSubstitute()
        {
            // Arrange
            var runSpeed = Substitute.For<IRunSpeed>();
            runSpeed.Speed.Returns(2);

            var sut = new GetToDaChoppa(runSpeed);

            //Act
            sut.DoItNow();

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Single(runSpeed.ReceivedCalls());
        }

        [Theory, AutoNSubstituteData]
        public void DoItNow_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            sut.DoItNow();

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Single(runSpeed.ReceivedCalls());

Meu AutoNSubstituteDataAttribute é assim:

        public class AutoNSubstituteDataAttribute : AutoDataAttribute
        {
            public AutoNSubstituteDataAttribute()
                : base(() => new Fixture()
                           .Customize(new AutoNSubstituteCustomization()))
            {
            }
        }

O primeiro teste 'DoItNow_WithOutAutoNSubstitute' está funcionando bem. Mas o segundo teste 'DoItNow_UsingAutoNSubstitute' retorna 2 para 'ios.Received (1) .Speed;'.
Ele também retorna 2 para 'runSpeed.ReceivedCalls ();'.
Por causa disso, atualmente não podemos atualizar nossas soluções para v4, pois instantaneamente temos mais de 1000 testes com falha por solução. Alguma orientação sobre qual pode ser o problema ou onde procurar a solução?

question

Todos 10 comentários

Obrigado por disparar o problema. Na verdade, esse problema não tem nada a ver com a AutoFixtura, embora também possamos ser afetados por ela 😕

Esse problema é causado principalmente pelo xUnit e se você reescrever o teste da seguinte forma, ele passará:
`` `c #
[Teoria, AutoNSubstituteData]
public void DoItNow_UsingAutoNSubstitute_CreateManually (IFixture fixture)
{
// Arranjo
var runSpeed ​​= fixture.Freeze();
var sut = fixture.Create();
runSpeed.Speed.Returns (49);

//Act
sut.DoItNow();

//Assert
var tmp = runSpeed.Received(1).Speed;
Assert.Single(runSpeed.ReceivedCalls());

}
`` `

Quando você executa os testes, o xUnit tenta formatar o nome do teste para você. Se ele descobrir que o tipo não é conhecido e não tem sobrecarga de ToString() , ele usa a inspeção estrutural e busca recursivamente os valores da propriedade. Se você verificar o nome do teste, verá o seguinte:
image

Como você pode notar, o xUnit buscou o Speed da propriedade

É muito estranho que você não tenha esse problema com a v3, pois nada mudou a esse respeito. Acabei de testar o xUnit2 + AutoFixture v3 e ainda tenho o problema. Provavelmente, você também atualizou o xUnit.


Quanto à solução alternativa, tenho boas e más notícias. A boa notícia é que isso será resolvido na próxima versão principal do NSubsitute, pois ele começou a substituir o método ToString() para retornar um id de proxy. Como resultado, o xUnit não toca mais nas propriedades:
image

A má notícia é que o NSubsitute v4 ainda não foi lançado.

Eu poderia sugerir a você:

  • adiar a migração até que o NSubsitute v4 seja lançado;
  • use o código-fonte master recente do NSubsitute, faça uma compilação local e use seu próprio NSubsitute.dll vez do pacote NuGet. Após o lançamento da v4, você pode alternar para o pacote NuGet.
  • se você atualizou a versão do xUnit também (pois isso pode explicar por que você não teve esse problema antes), reverta essa alteração e use a versão anterior do xUnit.

Peço desculpas pelo transtorno, mas, infelizmente, não podemos fazer nada no lado da correção automática para corrigir esse problema.

acabei de testar isso. finalmente, nossos testes afirmando ReceivedCalls voltaram a ter um resultado bem-sucedido novamente.
Algo que ainda falha são as afirmações em Recebido (x) como Recebido (1). Velocidade Daniel mencionado acima.

snip_20180328181559

Você também tem uma resposta / solução para o chapéu?

Vocês estão trabalhando no mesmo projeto? :) Em caso afirmativo, você poderia esclarecer como corrigiu o problema?

As opções sugeridas acima devem ajudar com ambos os problemas. Se não o fizerem, esclareça o cenário.

não é realmente o mesmo projeto, mas na mesma empresa.
estamos usando o xunit 1.9 + nsubstitute 2/3 por um longo tempo e finalmente conseguimos obter nossos assemblies internos (pacotes nuget então uma maneira simples de voltar para uma versão mais antiga de apenas uma referência não é tão fácil quanto parece para ser) para ser compatível com xunit2. e agora temos esses dois problemas.

Já experimentamos algumas coisas e finalmente pensamos que deve ter algo a ver com autofixture - por causa do atributo frozen e uma contagem de execução de teste codificada - ou algo parecido.
depois que Daniel abriu este caso, ele postou o link para mim.

assim como você descreveu acima, substituí a referência do nuget 3.1 por aquela do projeto nsubstitute compilado recentemente. depois disso, agora tenho 118 em vez de 178 testes reprovados porque Recebido (x) ainda está avaliando o número errado de chamadas.

meu projeto é atualmente baseado em .net 4.5.2 com xunit2, versão repo atual do nsubstitute e autofixture 4.2 referenciada. todos os testes estão falhando pelos mesmos dois motivos - também, ainda há algumas chamadas recebidas falhando nos testes de unidade.
Acho que vou ter que dar uma olhada nisso novamente quando estiver de volta ao escritório (para a Páscoa).
talvez @dklinger, entretanto, possa descrever o cenário.

@dklinger @evilbaschdi Por favor, continue depois de ter a chance de verificar isso - é muito interessante porque você ainda verá os problemas, mesmo depois de aplicar o patch.

Reabra o problema para indicar que ainda temos uma investigação em andamento.

Ok, testei nas últimas horas. Em primeiro lugar: Obrigado pela sua resposta, sua explicação e as sugestões - que ajudaram muito.
Eu testei a v4 do NSubstitute. Ele funciona para meu código de exemplo acima, mas não resolve o problema inteiramente em nossos projetos do mundo real. O problema é que ele resolve o problema da contagem incorreta de ReceivedCalls apenas para chamadas SUT para métodos, não para propriedades.

Exemplo de nosso código agora funcionando:

        [Theory, AutoNSubstituteData]
        public void DoItNow_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            sut.DoItNow();

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Equal(1, runSpeed.ReceivedCalls().Count());
        }

Mas se eu mudar o método "GetToDaChoppa.DoItNow ();" para ser uma propriedade "GetToDaChoppa.DoItNow", o ReceivedCallsCount é +1 novamente:

        [Theory, AutoNSubstituteData]
        public void DoItNow_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            var x = sut.DoItNow;

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Equal(1, runSpeed.ReceivedCalls().Count());
        }

Acho que mais uma vez tem a ver com a nomenclatura xUnit conforme obtemos este para a implementação do método de trabalho:
image

E aquele para a implementação de propriedade não funcional:
image

Parece que xUnit está chamando a propriedade SUT para recuperar seu valor e usá-lo para o nome de teste, mas não está fazendo isso para métodos SUT. Meu primeiro pensamento foi que isso tem a ver com o valor de retorno do nosso método "GetToDaChoppa ()" que era nulo. Mas mesmo depois de alterá-lo para "public int GetToDaChoppa ()", o xUnit ainda não o está chamando para obter um valor de retorno para o Nome de teste. É apenas um problema com as propriedades.

Por enquanto, estou preso novamente. Eu concordo totalmente que este não é um problema de AutoFixture. Mas na sua opinião, conhecendo todos os pacotes melhor do que nós, qual seria a sua sugestão?

  • abrindo um problema para NSubstitute?
  • abrindo um problema para xUnit2?
  • ou algo totalmente novo?

@dklinger Obrigado pelo acompanhamento! Você poderia compartilhar o código MCVE do último cenário que ainda falha? Apenas para garantir que nada seja esquecido e eu não tenha entendido mal as condições.

Depois disso, tentarei investigar por que isso acontece e como contornar isso.

Obrigado.

Sim, claro. Aqui está:

Sistema em teste:

    public interface IRunSpeed
    {
        int Speed { get; }
        void Dude();
        int Dude2();
    }

    public class GetToDaChoppa
    {
        private readonly IRunSpeed _runSpeed;

        public GetToDaChoppa(IRunSpeed runSpeed)
        {
            _runSpeed = runSpeed ?? throw new ArgumentNullException(nameof(runSpeed));
        }

        public int DoItNow
        {
            get
            {
                var runningSpeed = _runSpeed.Speed;
                return 0;
            }
        }
    }

Caso de teste:

        [Theory, AutoNSubstituteData]
        public void DoItNowAsProperty_UsingAutoNSubstitute(
            [Frozen] IRunSpeed runSpeed,
            GetToDaChoppa sut)
        {
            // Arrange
            runSpeed.Speed.Returns(49);

            //Act
            var x = sut.DoItNow;

            //Assert
            var tmp = runSpeed.Received(1).Speed;
            Assert.Equal(1, runSpeed.ReceivedCalls().Count());
        }

Atualmente, estou tentando acompanhar as discussões em https://github.com/xunit/xunit/issues/1386 e https://github.com/AutoFixture/AutoFixture/issues/805. Talvez estejamos fazendo algo errado ou haja uma maneira de dizer ao xUnit para não gerar automaticamente o nome do teste.
Também tentei criar meu próprio TheoryAttribute substituindo a propriedade DisplayName, mas isso também não ajudou, pois xUnit de alguma forma ainda está usando o nome gerado automaticamente internamente. Mas isso é apenas fyi e totalmente sem relação com o código de demonstração acima.

        public sealed class MyTheoryAttribute : TheoryAttribute
        {
            public MyTheoryAttribute([CallerMemberName] string memberName = null)
            {
                DisplayName = "MyTestCase";
            }
        }

image

Olá, eu de novo :)
Na verdade, é xUnit2 chamando todas as propriedades e campos dos parâmetros do método de teste que são de um tipo complexo. A linha de código é esta: https://github.com/xunit/assert.xunit/blob/2b70a9b0c5bb291f98472ec24cec437acf8d65c8/Sdk/ArgumentFormatter.cs#L156

Obrigado por seu apoio. Reabri um problema no xUnit-Repo, onde a discussão deve continuar: https://github.com/xunit/xunit/issues/1682

@dklinger Obrigado pelo cenário detalhado. É realmente muito complicado e é difícil de alguma forma contornar isso. Da perspectiva de AutoFixture e NSubsitute, não há diferença se o código é chamado em algum lugar profundamente dentro do xUnit ou no corpo de teste.

Normalmente, como solução alternativa, você pode usar o recurso claro do NSubstitute:

runSpeed.ClearReceivedCalls();

Este código deve ser executado no prólogo de cada teste, onde você verifica o número exato de chamadas. Funciona bem se você tiver alguns testes, mas obviamente, se milhares de testes forem afetados, não ajudará muito 😅

É uma pena que a integração NSubsitute + xUnit2 + AutoFixture não funcione bem e sofra com este tipo de problemas. O produto AutoFixture foi projetado para simplificar a vida, ao invés de torná-la um pesadelo 😕 Espero que os caras do xUnit lhe aconselhem uma maneira rápida de resolver o problema em todo o projeto.

Deixe-me saber se você acredita que podemos fazer algo de nossa parte para melhorar a situação.

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

Questões relacionadas

Eldar1205 picture Eldar1205  ·  5Comentários

JoshKeegan picture JoshKeegan  ·  6Comentários

joelleortiz picture joelleortiz  ·  4Comentários

ploeh picture ploeh  ·  7Comentários

Accc99 picture Accc99  ·  4Comentários