it-swarm.com.de

Steuern der Ausführungsreihenfolge von Komponententests in Visual Studio

Okay, ich bin fertig damit, nach guten Informationen zu suchen. Ich habe eine Reihe von Unit-Tests, die eine statische Klasse aufrufen, die nach der Initialisierung Eigenschaften festlegt, die sich nicht ändern können (oder wollen).

Mein Problem ist, dass ich keine festgelegte Reihenfolge für die Ausführung der Tests erzwingen kann. Wenn ich könnte, könnte ich sie so ausführen, dass die statischen Eigenschaften auf zuverlässige Weise festgelegt würden, und ich könnte sie bestätigen, aber leider führt das Microsoft.VisualStudio.TestTools.UnitTesting-Framework sie nur in einer scheinbar zufälligen Reihenfolge aus .

Also fand ich diese http://msdn.Microsoft.com/en-us/library/Microsoft.visualstudio.testtools.unittesting.priorityattribute.aspx im Abschnitt "Bemerkungen" "Dieses Attribut ist nicht wird vom Testsystem verwendet und dem Benutzer für benutzerdefinierte Zwecke zur Verfügung gestellt. " Huh? Was nützt es dann? Erwarten sie von mir, dass ich meinen eigenen Test-Wrapper schreibe, um dieses fabelhafte Attribut zu nutzen?

Also genug von dem Geschwätz; Fazit: Gibt es eine Möglichkeit, die Reihenfolge zu steuern, in der meine Einheitentests ausgeführt werden?

[TestMethod]
[Priority(0)]

usw. scheint NICHT zu funktionieren, was Sinn macht, da Microsoft sagt, dass dies nicht der Fall ist.

Bitte auch keine Kommentare zu "Isolationsverletzung". Die TestClass isoliert, was ich teste, nicht die einzelnen TestMethods. Unabhängig davon kann jeder Test unabhängig ausgeführt werden. Sie können nur nicht in zufälliger Reihenfolge zusammen ausgeführt werden, da es keine Möglichkeit gibt, die statische Klasse herunterzufahren.

Oh, ich kenne auch "Bestellter Test".

61
iGanja

Führen Sie Ihre Tests zu einem einzigen großen Test zusammen. Um die Testmethode lesbarer zu machen, können Sie so etwas tun

[TestMethod]
public void MyIntegratonTestLikeUnitTest()
{
    AssertScenarioA();

    AssertScenarioB();

    ....
}

private void AssertScenarioA()
{
     // Assert
}

private void AssertScenarioB()
{
     // Assert
}

Tatsächlich schlägt das Problem vor, dass Sie wahrscheinlich die Testbarkeit der Implementierung verbessern sollten.

48
Gang Gao

Sie können Playlist verwenden

Klicken Sie mit der rechten Maustaste auf die Testmethode -> Zur Wiedergabeliste hinzufügen -> Neue Wiedergabeliste

die Ausführungsreihenfolge ist so, wie Sie sie der Wiedergabeliste hinzufügen. Wenn Sie sie jedoch ändern möchten, haben Sie die Datei

enter image description here

108
HB MAAM

Ich sehe niemanden, der die Attributmethode ClassInitialize erwähnt. Die Attribute sind ziemlich einfach.

Erstellen Sie Methoden, die entweder mit dem Attribut [ClassInitialize()] oder [TestInitialize()] markiert sind, um Aspekte der Umgebung vorzubereiten, in der Ihr Komponententest ausgeführt werden soll. Der Zweck dieses Vorgangs besteht darin, einen bekannten Status für die Ausführung Ihres Komponententests festzulegen. Beispielsweise können Sie die Methode [ClassInitialize()] oder [TestInitialize()] verwenden, um bestimmte Datendateien zu kopieren, zu ändern oder zu erstellen, die Ihr Test verwenden wird.

Erstellen Sie Methoden, die entweder mit dem Attribut [ClassCleanup()] oder [TestCleanUp{}] Markiert sind, um die Umgebung nach der Ausführung eines Tests in einen bekannten Zustand zu versetzen. Dies kann das Löschen von Dateien in Ordnern oder die Rückkehr einer Datenbank in einen bekannten Zustand bedeuten. Ein Beispiel hierfür ist das Zurücksetzen einer Inventardatenbank in den Ausgangszustand nach dem Testen einer Methode, die in einer Anwendung zur Auftragserfassung verwendet wird.

  • [ClassInitialize()] Verwenden Sie ClassInitialize, um Code auszuführen, bevor Sie den ersten Test in der Klasse ausführen.

  • [ClassCleanUp()] Verwenden Sie ClassCleanup, um Code auszuführen, nachdem alle Tests in einer Klasse ausgeführt wurden.

  • [TestInitialize()] Verwenden Sie TestInitialize, um Code auszuführen, bevor Sie jeden Test ausführen.

  • [TestCleanUp()] Verwenden Sie TestCleanup, um Code nach jedem Testlauf auszuführen.

9
MattyMerrix

Wie die Kommentatoren bereits betonten, deutet die Abhängigkeit von Tests von anderen Tests auf einen Konstruktionsfehler hin. Trotzdem gibt es Möglichkeiten, dies zu erreichen. Wie in einer zuvor gestellten Frage hier beantwortet, können Sie geordnete Komponententests erstellen. Dies ist im Grunde ein einziger Testcontainer, der dies gewährleistet die Testsequenz.

Hier ist eine Anleitung zu MSDN: http://msdn.Microsoft.com/en-us/library/ms182631.aspx

6
Honza Brestan

Da Sie bereits die Funktionalität für geordnete Tests erwähnt haben, die das Visual Studio-Testframework bereitstellt, werde ich dies ignorieren. Sie scheinen sich auch darüber im Klaren zu sein, dass das, was Sie versuchen, um diese statische Klasse zu testen, eine "schlechte Idee" ist. Deshalb werde ich das ignorieren.

Konzentrieren wir uns stattdessen darauf, wie Sie möglicherweise tatsächlich garantieren können, dass Ihre Tests in der von Ihnen gewünschten Reihenfolge ausgeführt werden. Eine Option (wie von @gaog bereitgestellt) ist "eine Testmethode, viele Testfunktionen", die Ihre Testfunktionen in der gewünschten Reihenfolge innerhalb einer einzelnen Funktion aufruft, die mit dem Attribut TestMethod gekennzeichnet ist. Dies ist der einfachste Weg und der einzige Nachteil ist, dass die erste fehlgeschlagene Testfunktion verhindert, dass eine der verbleibenden Testfunktionen ausgeführt wird.

Mit Ihrer Beschreibung der Situation ist dies die Lösung, die ich Ihnen vorschlagen würde.

Wenn der fettgedruckte Teil ein Problem für Sie darstellt, können Sie eine geordnete Ausführung von Einzeltests durchführen, indem Sie die eingebauten datengesteuerten Testfunktionen nutzen. Es ist komplizierter und fühlt sich ein bisschen schmutzig an, aber es erledigt die Arbeit.

Kurz gesagt, Sie definieren eine Datenquelle (z. B. eine CSV-Datei oder eine Datenbanktabelle), die die Reihenfolge steuert, in der Sie Ihre Tests ausführen müssen, sowie die Namen der Funktionen, die tatsächlich die Testfunktionen enthalten. Anschließend binden Sie diese Datenquelle in einen datengesteuerten Test ein, verwenden die sequenzielle Leseoption und führen Ihre Funktionen in der gewünschten Reihenfolge als einzelne Tests aus.

[TestClass]
public class OrderedTests
{
    public TestContext TestContext { get; set; }

    private const string _OrderedTestFilename = "TestList.csv";

    [TestMethod]
    [DeploymentItem(_OrderedTestFilename)]
    [DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", _OrderedTestFilename, _OrderedTestFilename, DataAccessMethod.Sequential)]
    public void OrderedTests()
    {
        var methodName = (string)TestContext.DataRow[0];
        var method = GetType().GetMethod(methodName);
        method.Invoke(this, new object[] { });
    }

    public void Method_01()
    {
        Assert.IsTrue(true);
    }

    public void Method_02()
    {
        Assert.IsTrue(false);
    }

    public void Method_03()
    {
        Assert.IsTrue(true);
    }
}

In meinem Beispiel habe ich eine unterstützende Datei namens TestList.csv, die in die Ausgabe kopiert wird. Es sieht aus wie das:

TestName
Method_01
Method_02
Method_03

Ihre Tests werden in der von Ihnen angegebenen Reihenfolge und bei normaler Testisolierung ausgeführt (d. H., Wenn einer fehlschlägt, wird der Rest weiterhin ausgeführt, wobei statische Klassen verwendet werden).

Das Obige ist eigentlich nur die Grundidee, wenn ich es in der Produktion verwenden würde, würde ich die Testfunktionsnamen und ihre Reihenfolge dynamisch generieren, bevor der Test ausgeführt wird. Vielleicht haben Sie mithilfe von PriorityAttribute einen einfachen Reflektionscode gefunden, um die Testmethoden in der Klasse zu extrahieren und entsprechend zu ordnen. Schreiben Sie dann diese Reihenfolge in die Datenquelle.

5
Todd Bowles

Wie Sie inzwischen wissen sollten, sagen Puristen, es sei verboten, geordnete Tests durchzuführen. Das könnte für Unit-Tests zutreffen. MSTest und andere Unit-Test-Frameworks werden verwendet, um reine Unit-Tests auszuführen, aber auch UI-Tests, vollständige Integrationstests, wie Sie es nennen. Vielleicht sollten wir sie nicht Unit-Test-Frameworks nennen, oder wir sollten sie nach unseren Bedürfnissen verwenden. Das machen sowieso die meisten Leute.

Ich führe VS2015 aus und MUSS Tests in einer bestimmten Reihenfolge ausführen, da ich UI-Tests (Selenium) durchführe.

Priorität - Tut überhaupt nichts Dieses Attribut wird vom Testsystem nicht verwendet. Es wird dem Benutzer für benutzerdefinierte Zwecke zur Verfügung gestellt .

orderedtest - es funktioniert, aber ich empfehle es nicht, weil:

  1. Eine orderedtest Textdatei, die Ihre Tests in der Reihenfolge auflistet, in der sie ausgeführt werden sollen. Wenn Sie einen Methodennamen ändern, müssen Sie die Datei reparieren.
  2. Die Testausführungsreihenfolge wird innerhalb einer Klasse eingehalten. Sie können nicht anordnen, welche Klasse ihre Tests zuerst ausführt.
  3. Eine orderedtest Datei ist an eine Konfiguration gebunden, entweder Debug oder Release
  4. Sie können mehrere orderedtest Dateien haben, aber eine bestimmte Methode kann nicht in verschiedenen orderedtest Dateien wiederholt werden. Sie können also nicht eine orderedtest Datei für Debug und eine andere für Release haben.

Andere Vorschläge in diesem Thread sind interessant, aber Sie verlieren die Möglichkeit, den Testfortschritt im Test Explorer zu verfolgen.

Sie haben die Lösung, von der der Purist abraten wird, aber tatsächlich funktioniert die Lösung wie folgt: Nach Deklarationsreihenfolge sortieren .

Der MSTest-Executor verwendet ein Interop, mit dem die Deklarationsreihenfolge abgerufen werden kann, und dieser Trick funktioniert, bis Microsoft den Test-Executor-Code ändert.

Dies bedeutet, dass die Testmethode, die an erster Stelle deklariert ist, vor der Testmethode ausgeführt wird, die an zweiter Stelle deklariert ist usw.

Um Ihnen das Leben zu erleichtern, sollte die Deklarationsreihenfolge mit der alphabetischen Reihenfolge übereinstimmen, die im Test-Explorer angezeigt wird.

  • A010_FirstTest
  • A020_SecondTest
  • etc
  • A100_TenthTest

Ich empfehle dringend einige alte und getestete Regeln:

  • verwenden Sie einen Schritt von 10, da Sie später eine Testmethode einfügen müssen
  • vermeiden Sie es, Ihre Tests neu zu nummerieren, indem Sie einen großzügigen Schritt zwischen den Testnummern verwenden
  • verwenden Sie 3 Ziffern, um Ihre Tests zu nummerieren, wenn Sie mehr als 10 Tests ausführen
  • verwenden Sie 4 Ziffern, um Ihre Tests zu nummerieren, wenn Sie mehr als 100 Tests ausführen

SEHR WICHTIG

Um die Tests in der Deklarationsreihenfolge auszuführen, müssen Sie im Test-Explorer Alle ausführen verwenden.

Angenommen, Sie haben 3 Testklassen (in meinem Fall Tests für Chrome, Firefox und Edge). Wenn Sie eine bestimmte Klasse auswählen und mit der rechten Maustaste auf Ausgewählte Tests ausführen , wird normalerweise die an letzter Stelle deklarierte Methode ausgeführt.

Wie ich bereits sagte, sollte festgelegte Reihenfolge und aufgeführte Reihenfolge übereinstimmen, da sonst in kürzester Zeit große Probleme auftreten.

4

Hier ist eine Klasse, mit der geordnete Tests unabhängig vom MS Ordered Tests-Framework eingerichtet und ausgeführt werden können. Sie müssen beispielsweise nicht die Argumente von mstest.exe auf einem Buildcomputer anpassen oder geordnete mit nicht geordneten Tests in einer Klasse mischen.

Das ursprüngliche Testframework sieht die Liste der geordneten Tests nur als einen einzelnen Test, sodass Init/Cleanup wie [TestInitalize ()] Init () nur vor und nach dem gesamten Satz aufgerufen wird.

Verwendung:

        [TestMethod] // place only on the list--not the individuals
        public void OrderedStepsTest()
        {
            OrderedTest.Run(TestContext, new List<OrderedTest>
            {
                new OrderedTest ( T10_Reset_Database, false ),
                new OrderedTest ( T20_LoginUser1, false ),
                new OrderedTest ( T30_DoLoginUser1Task1, true ), // continue on failure
                new OrderedTest ( T40_DoLoginUser1Task2, true ), // continue on failure
                // ...
            });                
        }

Implementierung:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace UnitTests.Utility
{    
    /// <summary>
    /// Define and Run a list of ordered tests. 
    /// 2016/08/25: Posted to SO by crokusek 
    /// </summary>    
    public class OrderedTest
    {
        /// <summary>Test Method to run</summary>
        public Action TestMethod { get; private set; }

        /// <summary>Flag indicating whether testing should continue with the next test if the current one fails</summary>
        public bool ContinueOnFailure { get; private set; }

        /// <summary>Any Exception thrown by the test</summary>
        public Exception ExceptionResult;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="testMethod"></param>
        /// <param name="continueOnFailure">True to continue with the next test if this test fails</param>
        public OrderedTest(Action testMethod, bool continueOnFailure = false)
        {
            TestMethod = testMethod;
            ContinueOnFailure = continueOnFailure;
        }

        /// <summary>
        /// Run the test saving any exception within ExceptionResult
        /// Throw to the caller only if ContinueOnFailure == false
        /// </summary>
        /// <param name="testContextOpt"></param>
        public void Run()
        {
            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                ExceptionResult = ex;
                throw;
            }
        }

        /// <summary>
        /// Run a list of OrderedTest's
        /// </summary>
        static public void Run(TestContext testContext, List<OrderedTest> tests)
        {
            Stopwatch overallStopWatch = new Stopwatch();
            overallStopWatch.Start();

            List<Exception> exceptions = new List<Exception>();

            int testsAttempted = 0;
            for (int i = 0; i < tests.Count; i++)
            {
                OrderedTest test = tests[i];

                Stopwatch stopWatch = new Stopwatch();
                stopWatch.Start();

                testContext.WriteLine("Starting ordered test step ({0} of {1}) '{2}' at {3}...\n",
                    i + 1,
                    tests.Count,
                    test.TestMethod.Method,
                    DateTime.Now.ToString("G"));

                try
                {
                    testsAttempted++;
                    test.Run();
                }
                catch
                {
                    if (!test.ContinueOnFailure)
                        break;
                }
                finally
                {
                    Exception testEx = test.ExceptionResult;

                    if (testEx != null)  // capture any "continue on fail" exception
                        exceptions.Add(testEx);

                    testContext.WriteLine("\n{0} ordered test step {1} of {2} '{3}' in {4} at {5}{6}\n",
                        testEx != null ? "Error:  Failed" : "Successfully completed",
                        i + 1,
                        tests.Count,
                        test.TestMethod.Method,
                        stopWatch.ElapsedMilliseconds > 1000
                            ? (stopWatch.ElapsedMilliseconds * .001) + "s"
                            : stopWatch.ElapsedMilliseconds + "ms",
                        DateTime.Now.ToString("G"),
                        testEx != null
                            ? "\nException:  " + testEx.Message +
                                "\nStackTrace:  " + testEx.StackTrace +
                                "\nContinueOnFailure:  " + test.ContinueOnFailure
                            : "");
                }
            }

            testContext.WriteLine("Completed running {0} of {1} ordered tests with a total of {2} error(s) at {3} in {4}",
                testsAttempted,
                tests.Count,
                exceptions.Count,
                DateTime.Now.ToString("G"),
                overallStopWatch.ElapsedMilliseconds > 1000
                    ? (overallStopWatch.ElapsedMilliseconds * .001) + "s"
                    : overallStopWatch.ElapsedMilliseconds + "ms");

            if (exceptions.Any())
            {
                // Test Explorer prints better msgs with this hierarchy rather than using 1 AggregateException().
                throw new Exception(String.Join("; ", exceptions.Select(e => e.Message), new AggregateException(exceptions)));
            }
        }
    }
}
4
crokusek

Ich werde die Reihenfolge der Tests nicht ansprechen, sorry. Andere haben es bereits getan. Wenn Sie über "bestellte Tests" Bescheid wissen, ist dies auch die Antwort von MS VS auf das Problem. Ich weiß, dass diese geordneten Tests keinen Spaß machen. Aber sie dachten, es wird "es" sein und darüber gibt es in MSTest wirklich nichts mehr.

Ich schreibe über eine Ihrer Annahmen:

da es keine Möglichkeit gibt, die statische Klasse abzureißen.

Es sei denn, Ihre statische Klasse repräsentiert einen prozessweiten externen Status außerhalb Ihres Codes (wie z. B. den Status einer nicht verwalteten nativen Bibliothek DLL Bibliothek, die vom Rest aufgerufen wird) von Ihrem Code), Ihre Annahme, dass there is no way ist nicht wahr.

Wenn sich Ihre statische Klasse darauf bezieht, dann haben Sie leider vollkommen recht, der Rest dieser Antwort ist irrelevant. Trotzdem gehe ich davon aus, dass Ihr Code "verwaltet" ist, da Sie das nicht gesagt haben.

Denken Sie nach und überprüfen Sie das Ding AppDomain. Es wird selten benötigt, aber genau das ist der Fall, wenn Sie sie wahrscheinlich verwenden möchten.

Sie können eine neue AppDomain erstellen, den Test dort instanziieren und die Testmethode dort ausführen. Statische Daten, die von verwaltetem Code verwendet werden, werden dort isoliert und nach Abschluss können Sie die AppDomain entladen und alle Daten, einschließlich der statischen Daten, werden verdunsten. Dann würde der nächste Test eine andere Anwendungsdomäne initialisieren und so weiter.

Dies funktioniert nur, wenn Sie einen externen Status haben, den Sie verfolgen müssen. AppDomains isolieren nur den verwalteten Speicher. Alle nativen DLL werden weiterhin pro Prozess geladen und ihr Status wird von allen AppDomains gemeinsam genutzt.

Durch das Erstellen/Herunterfahren der Appdomains werden die Tests ebenfalls verlangsamt. Möglicherweise haben Sie auch Probleme mit der Assemblyauflösung in der untergeordneten Anwendungsdomäne, diese können jedoch mit einer angemessenen Menge an wiederverwendbarem Code gelöst werden.

Möglicherweise haben Sie auch kleine Probleme, wenn Sie Testdaten an die untergeordnete AppDomain übergeben und von dort zurück. Übergebene Objekte müssen entweder in irgendeiner Weise serialisierbar sein oder MarshalByRef oder usw. sein. Das domänenübergreifende Sprechen ist fast wie IPC.

Achten Sie hier jedoch darauf, dass das Sprechen zu 100% verwaltet wird. Wenn Sie etwas mehr Sorgfalt walten lassen und etwas Arbeit in das AppDomain-Setup einbringen, können Sie sogar Delegaten übergeben und diese in der Zieldomäne ausführen. Anstatt einige haarige domänenübergreifende Einstellungen vorzunehmen, können Sie Ihre Tests mit so etwas wie folgendem abschließen:

void testmethod()
{
    TestAppDomainHelper.Run( () =>
    {
        // your test code
    });
}

oder auch

[IsolatedAppDomain]
void testmethod()
{
    // your test code
}

wenn Ihr Test-Framework das Erstellen solcher Wrapper/Erweiterungen unterstützt. Nach einigen anfänglichen Recherchen und Arbeiten ist ihre Verwendung fast trivial.

2
quetzalcoatl

Ich sehe, dass dieses Thema fast 6 Jahre alt ist, und wir haben jetzt eine neue Version von Visual Studio, aber ich werde trotzdem antworten. Ich hatte dieses Problem mit der Reihenfolge in Visual Studio 19 und fand es heraus, indem ich Großbuchstaben (Sie können auch Kleinbuchstaben hinzufügen) vor dem Methodennamen und in alphabetischer Reihenfolge wie folgt einfügte:

[TestMethod]
        public void AName1()
        {}
[TestMethod]
        public void BName2()
        {}

Und so weiter. Ich weiß, dass dies nicht ansprechend aussieht, aber es sieht so aus, als würde Visual Ihre Tests im Test-Explorer in alphabetischer Reihenfolge sortieren, unabhängig davon, wie Sie sie in Ihren Code schreiben. Die Wiedergabeliste hat in diesem Fall bei mir nicht funktioniert.

Hoffe, dass dies helfen wird.

0
Taverna Joe