AutoFixture / AutoMoq ignoruje instancję wtrysku / zamrożoną próbkę

Krótkie jedzenie teraz, gdy rozwiązanie zostało znalezione:

AutoFixture zwraca zamrożenie makiety w porządku; mój szew, który również został wygenerowany przez AutoFixture, miał po prostu właściwość publiczną z lokalną wartością domyślną, która była ważna dla testu, a ta AutoFixture została ustawiona na nową wartość. Poza odpowiedzią Marka można się wiele nauczyć.

Oryginalne pytanie:

Wczoraj zacząłem wypróbowywać AutoFixture dla moich testów xUnit.net, które mają na sobie Moqa. Miałem nadzieję, że zastąpię niektóre rzeczy Moqa lub sprawię, że będą łatwiejsze do przeczytania, a szczególnie interesuje mnie użycie AutoFixture w pojemności fabryki SUT.

Uzbroiłem się w kilka postów na blogu Marka Seemanna na temat AutoMockingu i próbowałem stamtąd pracować, ale nie zaszedłem daleko.

Tak wyglądał mój test bez 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));
}

Historia tutaj jest dość prosta - upewnij się, żeSettingMappingXml pytaISettings zależność z poprawnym kluczem (który jest zakodowany / wstrzyknięty) i zwraca wynik jakoXElement. TheITracingService ma znaczenie tylko w przypadku błędu.

Próbowałem pozbyć się potrzeby jawnego tworzeniaITracingService obiekt, a następnie ręcznie wstrzyknij zależności (nie dlatego, że ten test jest zbyt złożony, ale ponieważ jest wystarczająco prosty, aby wypróbować i zrozumieć je).

Enter AutoFixture - pierwsza próba:

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

oczekiwałbymCreateAnonymous<SettingMappingXml>(), po wykryciuISettings parametr konstruktora, aby zauważyć, że konkretna instancja została zarejestrowana dla tego interfejsu i wstrzyknąć ją - jednak nie robi tego, ale zamiast tego tworzy nową anonimową implementację.

Jest to szczególnie mylące, ponieważfixture.CreateAnonymous<ISettings>() rzeczywiście zwraca moją instancję -

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

sprawia, że ​​test jest idealnie zielony, a ta linia jest dokładnie tym, czego oczekiwałem, gdy AutoFixture będzie wewnętrznie robił wystąpienieSettingMappingXml.

Potem jest koncepcja zamrożenia komponentu, więc poszedłem dalej, zamroziłem próbkę w urządzeniu, zamiast uzyskiwać wyszydzony obiekt:

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

Na pewno działa to doskonale - tak długo, jak nazywamSettingMappingXml konstruktor jawnie i nie polegaj naCreateAnonymous().



Mówiąc najprościej, nie rozumiem, dlaczego działa tak, jak się wydaje, ponieważ jest sprzeczne z jakąkolwiek logiką, którą mogę wyczarować. Normalnie podejrzewałbym błąd w bibliotece, ale jest to coś tak podstawowego, że jestem pewien, że inni wpadliby na to i długo by go znaleziono i naprawiono. Co więcej, znając wytrwałe podejście Marka do testowania i DI, nie może to być niezamierzone.

To z kolei oznacza, że ​​brakuje mi czegoś raczej elementarnego. Jak mogę utworzyć mój SUT przez AutoFixture z prekonfigurowanym wyszydzonym obiektem jako zależnością? Jedyne, czego teraz jestem pewien, to że potrzebujęAutoMoqCustomization więc nie muszę niczego konfigurować dlaITracingService.

Pakiety AutoFixture / AutoMoq to 2.14.1, Moq to 3.1.416.3, wszystkie z NuGet. Wersja .NET jest 4.5 (zainstalowana z VS2012), zachowanie jest takie samo w VS2012 i 2010.

Podczas pisania tego posta odkryłem, że niektórzy ludzie mają problemy z przekierowaniami Moq 4.0 i składania, więc skrupulatnie oczyściłem moje rozwiązanie z wszystkich instancji Moq 4 i zainstalowałem Moq 3.1, instalując AutoFixture.AutoMoq w „czystych” projektach. Jednak zachowanie mojego testu pozostaje niezmienione.

Dziękujemy za wszelkie wskazówki i wyjaśnienia.

Aktualizacja: Oto kod konstruktora Mark zapytany o:

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

    this.SettingKey = "gcCreditApplicationUsdFieldMappings";
}

I dla kompletnościGetXml() metoda wygląda tak:

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 to po prostu automatyczna właściwość.

questionAnswers(2)

yourAnswerToTheQuestion