AutoFixture / AutoMoq ignora la instancia inyectada / simulacro congelado

El corto para llevar ahora que la solución ha sido encontrada:

AutoFixture devuelve congelado el simulacro bien; mi sut que también fue generado por AutoFixture solo tenía una propiedad pública con un valor predeterminado local que era importante para la prueba y que AutoFixture tenía un nuevo valor. Hay mucho que aprender más allá de eso de la respuesta de Mark.

Pregunta original:

Comencé a probar AutoFixture ayer para mis pruebas de xUnit.net que tienen Moq por todas partes. Esperaba reemplazar algunas de las cosas Moq o facilitar su lectura, y estoy especialmente interesado en usar AutoFixture en la capacidad de SUT Factory.

Me armé con algunas de las publicaciones del blog de Mark Seemann sobre AutoMocking e intenté trabajar desde allí, pero no llegué muy lejos.

Así es como se veía mi prueba sin AutoFixture:

[Fact]
public void GetXml_ReturnsCorrectXElement()
{
    // Arrange
    string xmlString = @"
        <mappings>
            <mapping source='gcnm_loan_amount_min' target='gcnm_loan_amount_min_usd' />
            <mapping source='gcnm_loan_amount_max' target='gcnm_loan_amount_max_usd' />
        </mappings>";

    string settingKey = "gcCreditApplicationUsdFieldMappings";

    Mock<ISettings> settingsMock = new Mock<ISettings>();
    settingsMock.Setup(s => s.Get(settingKey)).Returns(xmlString);
    ISettings settings = settingsMock.Object;

    ITracingService tracing = new Mock<ITracingService>().Object;

    XElement expectedXml = XElement.Parse(xmlString);

    IMappingXml sut = new SettingMappingXml(settings, tracing);

    // Act
    XElement actualXml = sut.GetXml();

    // Assert
    Assert.True(XNode.DeepEquals(expectedXml, actualXml));
}

La historia aquí es bastante simple - asegúrese de queSettingMappingXml pregunta elISettings la dependencia con la clave correcta (que está codificada / propiedad inyectada) y devuelve el resultado como unXElement. losITracingService Solo es relevante si hay un error.

Lo que estaba tratando de hacer es deshacerme de la necesidad de crear explícitamente elITracingService Objete y luego inyecte las dependencias manualmente (no porque esta prueba sea demasiado compleja, sino porque es lo suficientemente simple como para probar y entender las cosas).

Introduzca AutoFixture - primer intento:

[Fact]
public void GetXml_ReturnsCorrectXElement()
{
    // Arrange
    IFixture fixture = new Fixture();
    fixture.Customize(new AutoMoqCustomization());

    string xmlString = @"
        <mappings>
            <mapping source='gcnm_loan_amount_min' target='gcnm_loan_amount_min_usd' />
            <mapping source='gcnm_loan_amount_max' target='gcnm_loan_amount_max_usd' />
        </mappings>";

    string settingKey = "gcCreditApplicationUsdFieldMappings";

    Mock<ISettings> settingsMock = new Mock<ISettings>();
    settingsMock.Setup(s => s.Get(settingKey)).Returns(xmlString);
    ISettings settings = settingsMock.Object;
    fixture.Inject(settings);

    XElement expectedXml = XElement.Parse(xmlString);

    IMappingXml sut = fixture.CreateAnonymous<SettingMappingXml>();

    // Act
    XElement actualXml = sut.GetXml();

    // Assert
    Assert.True(XNode.DeepEquals(expectedXml, actualXml));
}

Yo esperaríaCreateAnonymous<SettingMappingXml>(), tras la detección de laISettings parámetro de constructor, para observar que una instancia concreta se ha registrado para esa interfaz e inyectarla, sin embargo, no lo hace, sino que crea una nueva implementación anónima.

Esto es especialmente confuso ya quefixture.CreateAnonymous<ISettings>() de hecho devuelve mi instancia -

IMappingXml sut = new SettingMappingXml(fixture.CreateAnonymous<ISettings>(), fixture.CreateAnonymous<ITracingService>());

hace la prueba perfectamente verde, y esta línea es exactamente lo que esperaba que AutoFixture hiciera internamente al crear instanciasSettingMappingXml.

Luego está el concepto de congelar un componente, así que seguí congelando el simulacro en el accesorio en lugar de obtener el objeto simulado:

fixture.Freeze<Mock<ISettings>>(f => f.Do(m => m.Setup(s => s.Get(settingKey)).Returns(xmlString)));

Efectivamente, esto funciona perfectamente bien, siempre que yo llame alSettingMappingXml constructor explícitamente y no confiar enCreateAnonymous().



En pocas palabras, no entiendo por qué funciona de la manera en que aparentemente funciona, ya que va en contra de cualquier lógica que pueda conjurar. Normalmente, sospecho que hay un error en la biblioteca, pero esto es algo tan básico que estoy seguro de que otros se habrían topado con esto y durante mucho tiempo se habrían encontrado y solucionado. Además, al conocer el enfoque asiduo de Mark para las pruebas y la DI, esto no puede ser involuntario.

Eso a su vez significa que debo faltar algo más bien elemental. ¿Cómo puedo tener mi SUT creado por AutoFixture con un objeto simulado preconfigurado como una dependencia? Lo único que estoy seguro ahora es que necesito elAutoMoqCustomization así que no tengo que configurar nada para elITracingService.

Los paquetes de AutoFixture / AutoMoq son 2.14.1, Moq es 3.1.416.3, todos de NuGet. La versión .NET es 4.5 (instalada con VS2012), el comportamiento es el mismo en VS2012 y 2010.

Mientras escribía esta publicación, descubrí que algunas personas tenían problemas con Moq 4.0 y redirecciones de enlace de ensamblaje, así que purgué meticulosamente mi solución de cualquier instancia de Moq 4 e instalé Moq 3.1 al instalar AutoFixture.AutoMoq en proyectos "limpios". Sin embargo, el comportamiento de mi prueba se mantiene sin cambios.

Gracias por cualquier punteros y explicaciones.

Actualizar: Aquí está el código constructor que Mark pidió:

public SettingMappingXml(ISettings settingSource, ITracingService tracing)
{
    this._settingSource = settingSource;
    this._tracing = tracing;

    this.SettingKey = "gcCreditApplicationUsdFieldMappings";
}

Y para completar, elGetXml() El método se ve así:

public XElement GetXml()
{
    int errorCode = 10600;

    try
    {
        string mappingSetting = this._settingSource.Get(this.SettingKey);
        errorCode++;

        XElement mappingXml = XElement.Parse(mappingSetting);
        errorCode++;

        return mappingXml;
    }
    catch (Exception e)
    {
        this._tracing.Trace(errorCode, e.Message);
        throw;
    }
}

SettingKey Es solo una propiedad automática.

Respuestas a la pregunta(2)

Su respuesta a la pregunta