11 Stimmen

Unit-Tests für Ausdrucksbäume

Ich habe vor kurzem brauchen, um eine Expression Baum zu bauen, so schrieb ich eine Test-Methode wie so...

    /// <summary>
    /// 
    /// </summary>
    [TestMethod()]
    [DeploymentItem("WATrust.Shared.Infrastructure.dll")]
    public void BuildForeignKeysContainsPredicate_shoud_build_contains_predicate()
    {
        RemoteEntityRefLoader_Accessor<ReferencedEntity> target = CreateRemoteEntityRefLoader_Accessor();

        List<object> foreignKeys = new List<object>() { 1, 2, 3, 4 };
        Expression<Func<ReferencedEntity, bool>> expected = (ReferencedEntity referencedEntity) => foreignKeys.Contains(referencedEntity.Id);
        Expression<Func<ReferencedEntity, bool>> actual;

        actual = target.BuildForeignKeysContainsPredicate(foreignKeys, "Id");

        Assert.AreEqual(expected.ToString(), actual.ToString());
    }

Als ich schließlich die "BuildForeignKeysContainsPredicate"-Methode funktionierte, konnte ich den Test nie zum Bestehen bringen... Hier ist die Methode:

    /// <summary>
    /// 
    /// </summary>
    /// <param name="foreignKeys"></param>
    /// <returns></returns>
    private Expression<Func<TReferencedEntity, bool>> BuildForeignKeysContainsPredicate(List<object> foreignKeys, string primaryKey)
    {
        Expression<Func<TReferencedEntity, bool>> result = default(Expression<Func<TReferencedEntity, bool>>);

        try
        {
            ParameterExpression entityParameter = Expression.Parameter(typeof(TReferencedEntity), "referencedEntity");
            ConstantExpression foreignKeysParameter = Expression.Constant(foreignKeys, typeof(List<object>));
            MemberExpression memberExpression = Expression.Property(entityParameter, primaryKey);
            Expression convertExpression = Expression.Convert(memberExpression, typeof(object));
            MethodCallExpression containsExpression = Expression.Call(foreignKeysParameter
                , "Contains", new Type[] { }, convertExpression);

            result = Expression.Lambda<Func<TReferencedEntity, bool>>(containsExpression, entityParameter);

        }
        catch (Exception ex)
        {
            throw ex;
        }

        return result;
    }

Aber der Test schlägt jedes Mal fehl, ich habe die Zeile Assert.AreEqual(expected, actual); dazu: Assert.AreEqual(expected.ToString(), actual.ToString()); Ich verstehe, warum es fehlschlägt, denn wenn man sich die Ergebnisse der Methode ToString ansieht, sind sie unterschiedlich.

Assert.AreEqual failed.
Expected:<referencedEntity => value(Shared.Infrastructure.Test.RemoteEntityRefLoaderTest+<>c__DisplayClass13).foreignKeys.Contains(Convert(referencedEntity.Id))>.
Actual  :<referencedEntity => value(System.Collections.Generic.List`1[System.Object]                        )            .Contains(Convert(referencedEntity.Id))>.

Ich verstehe einfach nicht, warum... Hat jemand allgemeine Tipps zu Unit-Testing-Ausdrücken und Vorschläge, wie ich meinen spezifischen Test zum Bestehen bringen kann?

Danke...

16voto

Gishu Punkte 130442

Basierend auf dem Code, den Sie gepostet haben,

  • der erwartete Wert ist ein anonymer Delegat/Methode. Die CLR tut einige Magie hinter der Szene, um eine Methode on the fly hinzuzufügen. Für den Fall, dass die anonyme Methode bestimmte lokale Variablen verwendet, erstellt die CLR eine neue Klasse mit Feldern, die auf diese Werte gesetzt sind, mit der neuen anonymen Methode darin (so dass die Methode auf die lokalen Variablenwerte zugreifen kann). Das ist also Ihre ..c_DisplayClass13, mit einem vom Compiler vergebenen seltsamen Namen, damit sie nicht mit benutzerdefinierten Methoden kollidiert.
  • Der tatsächliche Wert, der von Ihrer Methode zurückgegeben wird, ist ein Expression<T> .

Daher schlägt die Gleichheitsprüfung zwischen diesen beiden fehl. Sie müssen die Elemente der Sammlung vergleichen, die von beiden zurückgegeben werden. Ich würde also vorschlagen, sowohl die erwarteten als auch die tatsächlichen Werte in Listen (oder eine bessere Datenstruktur) zu konvertieren und dann eine der NUnit-Asserts aufzurufen, die Auflistungsparameter annehmen.

Update : Du hast mich dazu gebracht, mich über Expression Trees zu informieren. +1 dafür.
Ich ändere meine Antwort - der Vergleich der Ausdrucksbäume mittels Hack-and-Assert würde zu einem fragilen Test führen (z. B. wenn MS die interne Struktur eines Ausdrucksbaums in der Zukunft ändert)
Ausdrucksbäume sind einfach Codeblöcke (wie ich jetzt herausgefunden habe), die zu einem Ergebnis ähnlich einer Func<TInput,TResult) - Mein Test würde also darin bestehen, den erwarteten und den tatsächlichen Codeblöcken die gleiche Eingabe zu geben und zu sehen, ob sie die gleiche Ausgabe liefern. Meine Behauptung für Ihren Test würde also lauten

Assert.AreEqual(expected.Compile().Invoke(inputEntity), 
                actual.Compile().Invoke(inputEntity));

0 Stimmen

Soll das heißen, dass Sie jeden der Knoten in jedem der Ausdrücke vergleichen?

0 Stimmen

Ich mag es... Ich stimme zu, wenn ich die Teile eines Ausdrucks vergleiche und Microsoft einige Änderungen vornimmt, könnte ich plötzlich viele fehlgeschlagene Tests haben. Ihre Strategie gefällt mir. Vielen Dank!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X