Autofixture: Das Anwenden von Anpassungen von der Basisklasse oder Schnittstelle schlägt in 4.0.0-rc1 fehl (Regression)

Erstellt am 20. Nov. 2017  ·  10Kommentare  ·  Quelle: AutoFixture/AutoFixture

Die folgenden Tests sind in AutoFixture 3.51.0 erfolgreich, schlagen jedoch in 4.0.0-rc1 fehl:
(sogar im neuesten Commit im Master: c856cd6 )

``` c#
[Tatsache]
public void ShouldApplyCustomizationsFromBaseClass()
{
var Fixture = new Fixture();
Befestigung.Anpassen(c => c.Ohne(bc => bc.Id));
var res = Fixture.Create();
Assert.Equal(0, res.Id);
}

[Tatsache]
public void ShouldApplyCustomizationsFromInterface()
{
var Fixture = new Fixture();
Befestigung.Anpassen(c => c.Ohne(bc => bc.Id));
var res = Fixture.Create();
Assert.Equal(0, res.Id);
}

öffentliche Schnittstelle IInterface
{
int-Id { erhalten; einstellen; }
}

öffentliche Klasse BaseClass
{
öffentliche int-Id { get; einstellen; }
}

öffentliche Klasse ImplClass : BaseClass, IInterface
{

}
```

question

Alle 10 Kommentare

Danke, dass du die Frage gestellt hast. Nun, dies ist die gewünschte Änderung und wurde absichtlich vorgenommen.

Zuvor hatten wir ein Problem, bei dem sich die Anpassung eines Untertyps auf einen anderen Untertyp auswirken konnte. Zum Beispiel:

```c#
Klasse Basis { öffentlicher String Common { get; einstellen; } }
Klasse Child1: Basis { }
Klasse Child2: Basis { }

[Tatsache]
public void ProvideCustomizationAreNotAffected()
{
var Fixture = new Fixture();
Befestigung.Anpassen(c => c.With(x => x.Common, "Dummy"));

var result = fixture.Create<Child2>();

Assert.NotNull(result.Common);

}


In the v3 this test failed, causing a lot of confusion to people. As you might imagine, the scenarios were more complicated and it was very non-obvious why the particular members are not initialized. 

For instance, the following code will not work, which again proves that API is not designed for that.
```c#
fixture.Customize<Base>(c => c.With(x => x.Common, "foo"));

Um dieses Problem zu beheben, haben wir den Ansatz geändert , sodass die Anpassung eines Typs jetzt keine Auswirkungen auf andere Typen hat, selbst wenn sie zur gleichen Vererbungslinie gehören. Wie Sie vielleicht in dieser PR sehen können, wurden eine Reihe von Usability-Problemen behoben und mehr Klarheit geschaffen (während die Änderung tatsächlich kaputt geht).

Wenn Sie Basis-/Schnittstelleneigenschaften dennoch weglassen möchten, können Sie das folgende Snippet verwenden:
```c#
private Klasse SamePropertySpecification : IRequestSpecification
{
privater schreibgeschützter Typ _declaringType;
private schreibgeschützte Zeichenfolge _name;

public SamePropertySpecification(Type declaringType, string name)
{
    _declaringType = declaringType;
    _name = name;
}

public bool IsSatisfiedBy(object request)
{
    if (request is PropertyInfo pi)
    {
        return pi.DeclaringType == this._declaringType &&
               pi.Name.Equals(this._name, StringComparison.Ordinal);
    }

    return false;
}

}

[Tatsache]
public void TestBasePropertyOmitting()
{
var Fixture = new Fixture();

fixture.Customizations.Add(new Omitter(new SamePropertySpecification(typeof(Base), nameof(Base.Common))));

var result = fixture.Create<Child1>();
Assert.Null(result.Common);

}
```

Wenn Sie diese Funktion häufig benötigen, können Sie Ihre eigene Erweiterungsmethode für fixture erstellen. Ich würde jedoch dafür stimmen, ein solches Feature nicht out-of-the-box zu haben, da es sehr verwirrend aussieht.

Okay, gut zu wissen, dass es Absicht war. Ich stimme definitiv zu, dass es verwirrend war, aber wenn es explizit angewendet werden kann, ist es ein nettes Feature. Werde dein Snippet ausprobieren (und wahrscheinlich die Erweiterungsmethode machen).

Übrigens: Wollen Sie das in die Breaking-Change-Liste aufnehmen? Könnte verhindern, dass dieselbe Frage erneut gestellt wird.

Es gibt keine Möglichkeit, das alte Verhalten für eine Anpassung aller Anpassungen zu aktivieren, oder?

Möchten Sie dies der Liste der Breaking Changes hinzufügen?

Nun, es wird im Abschnitt " Fehlerbehebungen " erwähnt. Dies ist in der Tat keine Breaking Change, da das vorherige Verhalten nie als "Feature" angegeben wurde. Das war eher ein ungewollter Nebeneffekt und wurde endgültig beseitigt 😉

Es gibt keine Möglichkeit, das alte Verhalten für eine Anpassung aller Anpassungen zu aktivieren, oder?

Nein, das gibt es meiner Meinung nach nicht, da ich den Ansatz komplett überarbeitet habe.

Bitte testen Sie das Snippet und wenn es funktioniert, können Sie das Problem gerne schließen. Oder lass es mich wissen, wenn du weitere Fragen hast 😃

Ich habe es so optimiert, dass es auch für Schnittstellen funktioniert und eine Erweiterungsmethode erstellt, damit es in "Fixture.Customize" verwendet werden kann(...)“. Fühlt sich etwas hackiger an, als mir lieb ist, zumal die Funktionsweise in v3 perfekt für uns war. Aber da unser einziger Anwendungsfall das Weglassen von Eigenschaften ist und es (am wichtigsten 😉 ) funktioniert, wird es den Zweck erfüllen. Vielen Dank!

@nphmuller Entschuldigung, dass ich deinen

Überhaupt keine Schuld. Verstehe deine Argumentation perfekt und stimme sogar zu. Schade für mich

Könnte später in das Problem eintauchen, um zu sehen, ob es einen saubereren Weg gibt. Im Moment ist es in Ordnung.

Nur aus Neugier - was ist Ihr wirklicher Anwendungsfall dafür? Weil ich nie einen Bedarf an einer solchen Funktionalität gedeckt habe. Wenn ich einen Test schreibe, muss ich normalerweise nur einen bestimmten Typ anpassen, anstatt den Basis- oder Schnittstellentyp zu konfigurieren. Schreiben Sie eine Art globale Anpassung?

In diesem Fall verwenden wir AutoFixture als Testdatengenerator für Entity Framework-Integrationstests.

Wir haben einige Basistypen oder Schnittstellen, die unsere Entitäten verwenden, die gemeinsame Eigenschaften enthalten. ID, Tenant-ID (und Navigationseigenschaft), solche Sachen.

Eine dieser Eigenschaften ist beispielsweise, welcher Benutzer das Objekt erstellt hat. Diese Eigenschaft ist für die meisten Tests nicht so interessant, und wenn sie generiert würde, würde sie einen riesigen Graphen erzeugen (die User-Klasse hat auch viele Navigationseigenschaften), der generiert und in die Datenbank eingefügt werden müsste. Dies würde den Test verlangsamen oder sogar auf DB-Ebene fehlschlagen, weil das Objekt nicht auf diese Weise eingefügt werden sollte.

Daher ist es einfacher, diese Eigenschaften einfach auszuschließen und bei Bedarf zu aktivieren. Auf Basisklassenebene, weil sonst für jeden Entitätstyp die gleiche Auslassungsregel geschrieben werden müsste.

@nphmuller Danke für die Klarstellung, jetzt ist es klar 😉 Ja, das Szenario sieht so aus, als ob es Sinn macht, aber ich muss zugeben, es sieht so aus, als ob man es ziemlich selten braucht.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen