Autofixture: Ansatz zur Generierung von Zufallsmustern basierend auf Anpassung

Erstellt am 8. Jan. 2018  ·  11Kommentare  ·  Quelle: AutoFixture/AutoFixture

Hallo, ich möchte in der Lage sein, unterschiedliche Werte basierend auf einem ICustomization mit ISpecimenBuilder.CreateMany zu generieren. Ich habe mich gefragt, was die beste Lösung wäre, da AutoFixture für alle Entitäten dieselben Werte generiert.

```c#
öffentliche Klasse FooCustomization : ICustomization
{
public void Anpassen (IFixture Fixture)
{
var Probe = Fixture.Build()
.OmitAutoProperties()
.With(x => x.CreationDate, DateTime.Now)
.With(x => x.Identifier, Guid.NewGuid().ToString().Substring(0, 6)) // Sollte unterschiedliche Werte generieren
.Mit(x => x.Mail, $"[email protected]")
.Schaffen();

        fixture.Register(() => specimen);
}

}
```

PS.: Das Hauptziel besteht darin, die Codemenge für meine Tests zu reduzieren, daher muss ich vermeiden, With() aufzurufen, um die Werte für jeden Test anzupassen. Gibt es eine richtige Methode, dies zu tun?

Update: Ich habe gerade diese Antwort von @ploeh gelesen: https://github.com/AutoFixture/AutoFixture/issues/361#issuecomment -86873060 das könnte das sein, wonach ich suche.
Dieser Ansatz hat viele Nachteile: Erstens scheint es wirklich nicht intuitiv zu sein, Create<List<Foo>>() aufzurufen, weil es den Zweck dessen, was man von CreateMany<Foo> erwartet, irgendwie verfehlt; das würde eine hartcodierte Liste erzeugen> (?). Ein weiterer Nachteil besteht darin, dass ich für jede Entität zwei Anpassungen vornehmen müsste. eine zum Erstellen benutzerdefinierter Sammlungen und eine andere zum Erstellen einer einzelnen Instanz, da wir das Verhalten von Create<T> überschreiben, um eine Sammlung zu erstellen.

question

Alle 11 Kommentare

Dies scheint ein guter Kandidat für eine Stack Overflow- Frage zu sein. Es ist nichts Falsches daran, hier eine Frage zu stellen, aber ich denke, diese Frage ist von der Art, bei der eine Antwort auch anderen Menschen zugute kommen könnte, und es ist einfacher, Fragen und Antworten auf Stack Overflow im Vergleich zu GitHub-Problemen zu finden. Zum einen leistet Google gute Arbeit bei der Indizierung von Stack Overflow-Fragen, während es anscheinend (geschlossene) GitHub-Probleme niedriger einstuft ...

@ploeh da stimme ich dir zu....
Ich habe gerade eine SO-Frage basierend auf diesem Problem erstellt. Dies ist der Link für zukünftige Referenzen 👍

@ploeh Danke für die Beantwortung der Frage zu SO: erröten:

@thiagomajesk Eigentlich gibt es keine Probleme mit der Beantwortung der Fragen hier. Der Grund, warum Mark SO vorschlug, sind die Suchfunktionen, die es bietet. Außerdem überwacht Mark derzeit SO, wenn du also dort eine Frage postest, gibt es mehr Chancen, sein Fachwissen zu erhalten (wie dieses Mal):sweat_smile:

@zvirja Danke für die Klarstellung 😄

@zvirja Ich werde die Frage zu SO basierend auf der großartigen Antwort von @ploeh schließen .
Aber ich möchte Sie um ein wenig mehr Informationen zu den Implementierungsdetails zu diesem Thema bitten.
Da ich nicht noch weiter vom Thema abschweifen möchte, denke ich, dass es besser wäre, hier nach diesen Besonderheiten zu fragen. Also ich habe diese Implementierung:

```c#
Öffentliche Klasse UniqueShortGuidBuilder: ISpecimenBuilder
{
private schreibgeschützte Zeichenfolge propName;
private readonly int Länge;

public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
{
    propName = ((MemberExpression)expr.Body).Member.Name;
    this.lenght = lenght;
}

öffentliches Objekt Create(Objektanforderung, ISpecimenContext-Kontext)
{
var pi = Anfrage als PropertyInfo;

if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
    return new NoSpecimen();

return Guid.NewGuid().ToString().Substring(0, lenght);

}
```

Ich hatte erwartet, dass beim Erstellen vieler Objekte mit AutoFixture ISpecimenBuilder.Create mehrmals aufgerufen wird, wodurch unterschiedliche Anleitungen für meine Entitäten generiert werden. aber anscheinend ist das nicht wahr und sie teilen tatsächlich dasselbe.

Nun, ich habe Ihren Code gerade getestet und er hat genau wie gewünscht funktioniert:

```c#
öffentliche Klasse Foo
{
öffentliche Zeichenfolge PropertyToCustomize { get; einstellen; }
}

Öffentliche Klasse UniqueShortGuidBuilder: ISpecimenBuilder
{
private schreibgeschützte Zeichenfolge propName;
private readonly int Länge;

public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
{
    propName = ((MemberExpression) expr.Body).Member.Name;
    this.lenght = lenght;
}

public object Create(object request, ISpecimenContext context)
{
    var pi = request as PropertyInfo;

    if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
        return new NoSpecimen();

    return Guid.NewGuid().ToString().Substring(0, lenght);
}

}

[Tatsache]
public void TestCustomization()
{
var Fixture = new Fixture();
Fixture.Customizations.Add(neuer UniqueShortGuidBuilder(x => x.EigenschaftToCustomize, 5));

var manyFoos = fixture.CreateMany<Foo>(10);

var uniqueIds = manyFoos
    .Select(x => x.PropertyToCustomize)
    .Where(x => x.Length == 5)
    .Distinct()
    .ToArray();
Assert.Equal(10, uniqueIds.Length);

}
```

Daher möchte ich Sie noch einmal bitten, zu sehen, wie sich Ihr tatsächlicher Code unterscheidet, damit wir verstehen können, warum dieser Unterschied wichtig ist 😟

@zvirja Das ist seltsam. Der Code unten ist eine minimale Reproduktion des Problems, das ich habe.
Wenn ich das Fixture.Customize(new FooCustomization()); entferne, wird der Test bestanden, andernfalls nicht.

```c#
Verwenden des Systems;
Verwenden von Microsoft.VisualStudio.TestTools.UnitTesting;
Verwenden von AutoFixture.Kernel;
Verwenden von System.Linq.Expressions;
Verwenden von System.Reflection;
Verwenden von AutoFixture;
Verwenden von System.Linq;

Namensraum UnitTestProject1
{
öffentliche Klasse Foo
{
öffentliche Zeichenfolge PropertyToCustomize { get; einstellen; }
}

public class FooCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        var specimen = fixture.Build<Foo>()
            .OmitAutoProperties()
            .With(x => x.PropertyToCustomize)
            .Create();

        fixture.Register(() => specimen);
    }
}

public class UniqueShortGuidBuilder<TEntity> : ISpecimenBuilder
{
    private readonly string propName;
    private readonly int lenght;

    public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
    {
        propName = ((MemberExpression)expr.Body).Member.Name;
        this.lenght = lenght;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as PropertyInfo;

        if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
            return new NoSpecimen();

        return Guid.NewGuid().ToString().Substring(0, lenght);
    }
}

[TestClass]
public class UnitTest1
{
    public static Fixture Fixture { get; private set; }

    [ClassInitialize]
    public static void ClassInitialize(TestContext context)
    {
        Fixture = new Fixture();
        Fixture.Customizations.Add(new UniqueShortGuidBuilder<Foo>(x => x.PropertyToCustomize, 5));
        Fixture.Customize(new FooCustomization());
    }

    [TestMethod]
    public void TestMethod1()
    {
        var manyFoos = Fixture.CreateMany<Foo>(10);

        var uniqueIds = manyFoos
            .Select(x => x.PropertyToCustomize)
            .Where(x => x.Length == 5)
            .Distinct()
            .ToArray();

        Assert.AreEqual(10, uniqueIds.Length);
    }
}

}
```

Empfehlung: Wenn Sie darauf angewiesen sind, die ersten _n_ Zeichen aus einem GUID-String zu entfernen, nennen Sie die Klasse nicht UniqueShortGuidBuilder . Die Werte können ziemlich eindeutig sein, wenn die Länge 5 ist, aber wenn Sie sie auf 1 setzen, werden Sie wahrscheinlich Kollisionen sehen. Einzigartigkeit ist in jedem Fall nicht garantiert.

Wenn Ihnen nur die Länge der Zeichenfolge und die nicht garantierte Eindeutigkeit wichtig sind, fragen Sie AutoFixture einfach nach einer Zahl zwischen, sagen wir, 0 und 99.999, und wandeln Sie sie in eine Zeichenfolge um? IIRC, Nummern sind eindeutig, bis der Bereich erschöpft ist.

Wenn ich das Fixture.Customize (new FooCustomization()) entferne; die Prüfung wird bestanden, sonst nicht.

Nun, das ist tatsächlich das erwartete Verhalten, wenn Sie die Situation genauer betrachten 😅 Siehe meine Antwort in #962. Die bereitgestellte Implementierung von FooCustomization erstellt eine Instanz vom Typ Foo und konfiguriert das Gerät so, dass es immer diese Instanz zurückgibt. Wenn Sie später mehrere Instanzen des Typs Foo erstellen, wird daher jedes Mal dasselbe Objekt zurückgegeben und Ihr Builder wird nicht weiter aufgerufen.

Wenn Sie möchten, dass für jede Anforderung von Foo eine neue Instanz erstellt wird, verwenden Sie die Customize<> API, wie ich hier vorgeschlagen

@ploeh Danke für den Tipp, aber dieser Code ist nur ein grobes Beispiel für Testzwecke. In realen Fällen wird die Länge nicht lächerlich kurz sein, um die Einzigartigkeit der Guids zu beeinträchtigen 😄.

Wenn Ihnen nur die Länge der Zeichenfolge und die nicht garantierte Eindeutigkeit wichtig sind, fragen Sie AutoFixture einfach nach einer Zahl zwischen, sagen wir, 0 und 99.999, und wandeln Sie sie in eine Zeichenfolge um?

Ich wette, dies ist für die meisten Fälle eine gute Lösung, aber ich kann dies für diesen speziellen Fall aufgrund alphanumerischer Validierungen in meinem System nicht tun. Außerdem habe ich komplexere Validierungen in diesem Bereich.

Nun, das ist tatsächlich das erwartete Verhalten, wenn Sie die Situation genauer betrachten

@zvirja Nach deiner Antwort in #962 ist es klar. Aber für mich war dieses Verhalten nicht offensichtlich, da es keine (ausdrückliche) Dokumentation zu AutoFixture gibt (wie ich hier erwähnt habe ) 😁.

Zum Abschluss danke ich euch beiden für die Geduld und all die tollen Erklärungen.
Ich bin sicher, das wird in Zukunft auch für andere nützlich sein 😉.

Okay, fairerweise neige ich dazu, die Dinge zu wörtlich zu nehmen 😄

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen