Autofixture: NSubstitute.ReceivedCalls () devuelve un valor incorrecto usando AutoFixture.AutoNSubstitute

Creado en 28 mar. 2018  ·  10Comentarios  ·  Fuente: AutoFixture/AutoFixture

Tenemos muchas pruebas usando 'Class.ReceivedCalls ()' y 'var tmp = Class.Received (int) .Property;' para comprobar el recuento de llamadas. En la v3 de AutoFixture informó correctamente el recuento de llamadas, especialmente de Propiedades, lo que ya no hace. El recuento de llamadas de métodos parece estar todavía bien.
Dado que tenemos el siguiente 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;
        }
    }

Y dado que tenemos las siguientes pruebas:

        [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());

Mi AutoNSubstituteDataAttribute se ve así:

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

La primera prueba 'DoItNow_WithOutAutoNSubstitute' está funcionando bien. Pero la segunda prueba 'DoItNow_UsingAutoNSubstitute' devuelve 2 para 'ios.Received (1) .Speed;'.
También devuelve 2 para 'runSpeed.ReceivedCalls ();'.
Debido a esto, actualmente no podemos actualizar nuestras Soluciones a v4, ya que instantáneamente tenemos> 1000 pruebas fallidas por solución. ¿Alguna orientación sobre cuál podría ser el problema o dónde buscar la solución?

question

Todos 10 comentarios

Gracias por solucionar el problema. En realidad, este problema no tiene nada que ver con AutoFixture, aunque a nosotros también nos afecta 😕

Este problema es causado principalmente por xUnit y si reescribe la prueba de la siguiente manera, pasará:
`` c #
[Teoría, AutoNSubstituteData]
public void DoItNow_UsingAutoNSubstitute_CreateManually (accesorio IFixture)
{
// Arreglar
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());

}
''

Cuando ejecuta pruebas, xUnit intenta formatear el nombre de la prueba por usted. Si encuentra que el tipo no se conoce y no tiene una sobrecarga de ToString() , utiliza la inspección estructural y recupera de forma recursiva los valores de propiedad. Si marca el nombre de la prueba, verá lo siguiente:
image

Como puede notar, xUnit obtuvo el valor de la propiedad Speed para mostrarle el argumento de prueba. Como se trata de una invocación habitual, NSubstitute lo contó como una llamada.

Es bastante extraño que no tuvieras este problema con la v3, ya que nada cambió en ese sentido. Acabo de probar xUnit2 + AutoFixture v3 y todavía tengo el problema. Probablemente, también haya actualizado la xUnit.


En cuanto a una solución alternativa, tengo buenas y malas noticias. La buena noticia es que esto se resolverá en la próxima versión principal de NSubsitute, ya que comenzó a anular el método ToString() para devolver una identificación de proxy. Como resultado, xUnit ya no toca las propiedades:
image

La mala noticia es que NSubsitute v4 aún no se ha lanzado.

Podría sugerirle:

  • posponer la migración hasta que se publique NSubsitute v4;
  • use el último código fuente master de NSubsitute, haga una compilación local y use su propio paquete NSubsitute.dll lugar del paquete NuGet. Una vez que se publique la versión 4, puede cambiar al paquete NuGet.
  • Si también actualizó la versión de xUnit (ya que esto podría explicar por qué no tuvo este problema antes), revierta ese cambio y use la versión anterior de xUnit.

Pido disculpas por las molestias, pero, desafortunadamente, no podemos hacer nada en el lado de AutoFixture para solucionar este problema.

acabo de probar esto. finalmente nuestras pruebas que afirman ReceivedCalls volvieron a tener un resultado exitoso nuevamente.
Algo que todavía falla son las afirmaciones en Received (x) como Received (1). Speed ​​Daniel mencionado anteriormente.

snip_20180328181559

¿También tiene una respuesta / solución para sombrero?

¿Están trabajando en el mismo proyecto? :) Si es así, ¿podría aclarar cómo solucionó el problema?

Las opciones sugeridas anteriormente deberían ayudar con ambos problemas. Si no es así, aclare el escenario.

en realidad, no es el mismo proyecto, sino la misma empresa.
Hemos estado en xunit 1.9 + nsubstitute 2/3 durante mucho tiempo y finalmente logramos obtener nuestros ensamblajes internos (paquetes nuget, por lo que una forma simple de volver a una versión anterior de solo una referencia no es tan fácil como parece to be) para ser compatible con xunit2. y ahora tenemos estos dos problemas.

Ya probamos algunas cosas y finalmente pensamos que debe tener algo que ver con la autofijación, debido al atributo congelado y un recuento de ejecución de pruebas codificado de forma rígida, o algo así.
Después de que Daniel abrió este caso, me envió el enlace.

tal como describió anteriormente, reemplacé la referencia de nuget 3.1 con la del proyecto nsubstitute compilado nuevo. después de eso, ahora tengo 118 en lugar de 178 pruebas fallidas debido a que Receiced (x) todavía está evaluando el número incorrecto de llamadas.

mi proyecto se basa actualmente en .net 4.5.2 con xunit2, se hace referencia a la versión actual del repositorio de nsubstitute y autofixture 4.2. todas las pruebas fallan por las mismas dos razones; también hay algunas llamadas recibidas que fallan en las pruebas unitarias.
Creo que tendré que echarle un vistazo más de cerca cuando vuelva a la oficina (fuera de Semana Santa).
tal vez @dklinger mientras tanto pueda describir el escenario.

@dklinger @evilbaschdi Por favor,

Vuelva a abrir el problema para indicar que aún tenemos una investigación en curso.

Ok, he probado las últimas horas. Primero: Gracias por su respuesta, su explicación y las sugerencias, eso ayudó mucho.
He probado la versión 4 de NSubstitute. Funciona para mi código de ejemplo anterior, pero no resuelve el problema por completo en nuestros proyectos del mundo real. El problema es que resuelve el problema del recuento incorrecto de ReceivedCalls solo para SUT-Calls a métodos, no a propiedades.

Ejemplo de nuestro código que ahora funciona:

        [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());
        }

Pero si cambio el método "GetToDaChoppa.DoItNow ();" para ser una propiedad "GetToDaChoppa.DoItNow", ReceivedCallsCount es +1 nuevamente:

        [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());
        }

Creo que nuevamente tiene que ver con el nombre de xUnit, ya que obtenemos este para la implementación del método de trabajo:
image

Y ese para la implementación de propiedad que no funciona:
image

Parece que xUnit está llamando a la propiedad SUT para recuperar su valor y usarlo para el nombre de la prueba, pero no lo está haciendo para los métodos SUT. Lo primero que pensé fue que tiene que ver con el valor de retorno de nuestro método "GetToDaChoppa ()" que estaba vacío. Pero incluso después de cambiarlo para que sea "public int GetToDaChoppa ()", xUnit todavía no lo está llamando para obtener un valor de retorno para el Nombre de prueba. Solo es un problema usar propiedades.

Por ahora estoy atascado de nuevo. Estoy totalmente de acuerdo en que esto no es un problema de AutoFixture. Pero en su opinión, conociendo todos los paquetes mejor que nosotros, ¿cuál sería su sugerencia?

  • abriendo un problema para NSubstitute?
  • abriendo un problema para xUnit2?
  • o algo totalmente nuevo?

@dklinger ¡ Gracias por el seguimiento! ¿Podría compartir el código MCVE para el último escenario que aún falla? Solo para asegurarme de que no se pierda nada y no he entendido mal las condiciones.

Después de eso, intentaré investigar por qué sucede eso y cómo solucionarlo.

Gracias.

Si, claro. Aquí está:

Sistema bajo prueba:

    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 prueba:

        [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());
        }

Actualmente estoy tratando de ponerme al día con las discusiones en https://github.com/xunit/xunit/issues/1386 y https://github.com/AutoFixture/AutoFixture/issues/805. Tal vez estamos haciendo algo mal o hay una manera de decirle a xUnit que no genere automáticamente el nombre de la prueba.
También intenté crear mi propio TheoryAttribute anulando la propiedad DisplayName, pero eso tampoco ayuda, ya que xUnit de alguna manera sigue usando el nombre generado automáticamente internamente. Pero eso es solo para tu información y no tiene nada que ver con el código de demostración anterior.

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

image

Hola, yo de nuevo :)
De hecho, xUnit2 llama a todas las propiedades y campos de los parámetros del método de prueba que son de un tipo complejo. La línea de código es esta: https://github.com/xunit/assert.xunit/blob/2b70a9b0c5bb291f98472ec24cec437acf8d65c8/Sdk/ArgumentFormatter.cs#L156

Gracias por su apoyo. Reabrí un problema en xUnit-Repo donde debería continuar la discusión: https://github.com/xunit/xunit/issues/1682

@dklinger Gracias por el escenario detallado. De hecho, es bastante complicado y es difícil solucionarlo de alguna manera. Desde la perspectiva de AutoFixture y NSubsitute, no hay diferencia si el código se llama en algún lugar profundo dentro de xUnit o en el cuerpo de prueba.

Por lo general, como solución alternativa, puede utilizar la función clara de NSubstitute:

runSpeed.ClearReceivedCalls();

Este código debe ejecutarse en el prólogo de cada prueba donde verifica el número exacto de llamadas. Funciona bien si tiene algunas pruebas, sin embargo, obviamente, si miles de pruebas se ven afectadas, no ayudará mucho 😅

Es una pena que la integración de NSubsitute + xUnit2 + AutoFixture no funcione bien y sufra este tipo de problemas. El producto AutoFixture fue diseñado para simplificar la vida, en lugar de convertirlo en una pesadilla 😕 Espero que los chicos de xUnit le aconsejen una manera rápida de resolver el problema de todo el proyecto.

Avíseme si cree que podemos hacer algo de nuestra parte para mejorar la situación.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

zvirja picture zvirja  ·  4Comentarios

Ephasme picture Ephasme  ·  3Comentarios

Accc99 picture Accc99  ·  4Comentarios

ploeh picture ploeh  ·  3Comentarios

TroyHouston picture TroyHouston  ·  6Comentarios