Laden...

xUnit, wie TrxLogger benutzen bzw. Trx Datei schreiben?

Erstellt von dr4g0n76 vor 4 Jahren Letzter Beitrag vor 4 Jahren 1.496 Views
dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 4 Jahren
xUnit, wie TrxLogger benutzen bzw. Trx Datei schreiben?

Hallo Kollegen.

Ich hab hier einen Assembly Test Runner, der den Mechanismus von xUnit benutzt (falls ihn jemand benutzen möchte). Es ist auch unbedingt gewünscht, dass wir diesen selber beeinflussen können.
Soweit so gut. (Die Tests sind in einer eigenen Assembly und können durch Angabe des Filenamens der Assembly ausgeführt werden.

Nur, wenn ich den jetzt ausführe, wird ja nicht einfach standardmässig ein *.trx File geschrieben.

Wenn ich aber


dotnet test --logger trx --no-build /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=.\coverage\ Codan.Argus.AcceptanceTests

ausführe, dann schon. Vorausgesetzt die richtigen NuGet packages wurden zur TestAssembly hinzugefügt.

(Danach wird dann der Coverage Report mit


reportgenerator "-reports:C:\Code\TestEnvironment\Codan.Argus.AcceptanceTests\coverage\coverage.opencover.xml" "-targetdir:.\coverage"

erzeugt)

Nur, wenn ich diesen Befehl benutze, ist zwar alles gut bis auf eine Sache:
Die Tests werden ja dann nochmal ausgeführt, einmal durch meinen eigenen TestRunner und einmal eben durch die Kommandozeile.

Wie bekomme ich also den Mechanismus genutzt, von dotnet test -logger trx?

Oder muss ich dann quasi "von Hand" einen trx Logger für jeden Test der ausgeführt wird selbst aufrufen?

EDIT: Wenn ich selber vorher eine Lösung finde, poste ich sie natürlich hier.


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit.Runners;

namespace Codan.Argus.TestEnvironment
{
    public class ExtendedAssemblyRunner : IDisposable
    {
        private AssemblyRunner _assemblyRunner = null;
        
        public delegate void DiscoveryCompleteEventHandler(object sender, DiscoveryCompleteInfo e);
        public event DiscoveryCompleteEventHandler DiscoveryCompleteEvent;

        public delegate void ExecutionCompleteEventHandler(object sender, ExecutionCompleteInfo e);
        public event ExecutionCompleteEventHandler ExecutionCompleteEvent;

        public delegate void TestStartingEventHandler(object sender, TestStartingInfo e);
        public event TestStartingEventHandler TestStartingEvent;

        public delegate void TestSkippedEventHandler(object sender, TestSkippedInfo e);
        public event TestSkippedEventHandler TestSkippedEvent;

        public delegate void TestFailedEventHandler(object sender, TestFailedInfo e);
        public event TestFailedEventHandler TestFailedEvent;

        //public delegate void 

        static object consoleLock = new object();
        private static ManualResetEvent finished = new ManualResetEvent(false);
        private string _testAssembly = null;

        private ExecutionCompleteData _executionCompleteData = null;

        public ExtendedAssemblyRunner(string testAssembly)
        {
            _testAssembly = testAssembly;

            _assemblyRunner = AssemblyRunner.WithAppDomain(testAssembly);
        }

        public ExecutionCompleteData ExecutionCompleteData 
        {
            get { return _executionCompleteData; }
        }
        
        private void WithAppDomain(string testAssembly)
        {
           _assemblyRunner = AssemblyRunner.WithAppDomain(testAssembly);
        }

        public void Start(string testAssembly = null)
        {
            if (testAssembly == null) testAssembly = _testAssembly;

            _assemblyRunner.OnDiscoveryComplete = OnDiscoveryCompleteEvent;
            _assemblyRunner.OnExecutionComplete = OnExecutionCompleteEvent;
            _assemblyRunner.OnTestFailed = OnTestFailedEvent;
            _assemblyRunner.OnTestSkipped = OnTestSkippedEvent;
            _assemblyRunner.OnTestStarting = OnTestStartingEvent;

            _assemblyRunner.Start(typeName: null);

            finished.WaitOne();
            finished.Dispose();
        }

        public AssemblyRunnerStatus Status
        {
            get { return _assemblyRunner.Status; }
        }

        protected void OnDiscoveryCompleteEvent(DiscoveryCompleteInfo info)
        {
            lock (consoleLock)
            {
                if (DiscoveryCompleteEvent != null)
                {
                    DiscoveryCompleteEvent(this, info);
                }

#if DEBUG
                Debug.WriteLine("Discovering...");
#endif
            }
        }


        protected void OnExecutionCompleteEvent(ExecutionCompleteInfo info)
        {
            lock (consoleLock)
            {
                if (ExecutionCompleteEvent != null)
                {
                    ExecutionCompleteEvent(this, info);
                }
#if DEBUG
                Debug.WriteLine(
                    $"Finished: {info.TotalTests} tests in {Math.Round(info.ExecutionTime, 3)}s ({info.TestsFailed} failed, {info.TestsSkipped} skipped)");
#endif
                finished.Set();
            }
        }

        protected void OnTestFailedEvent(TestFailedInfo info)
        {
            lock (consoleLock)
            {
                if (this.TestFailedEvent != null)
                {
                    TestFailedEvent(this, info);
                }

#if DEBUG
                Console.ForegroundColor = ConsoleColor.Red;
                Debug.WriteLine("[FAIL] {0}: {1}", info.TestDisplayName, info.ExceptionMessage);
                if (info.ExceptionStackTrace != null)
                {
                    Debug.WriteLine(info.ExceptionStackTrace);
                }

                Console.ResetColor();
#endif
            }
        }

        protected void OnTestStartingEvent(TestStartingInfo info)
        {
            lock (consoleLock)
            {
                if (this.TestStartingEvent != null)
                {
                    TestStartingEvent(this, info);
                }
#if DEBUG
                Console.ForegroundColor = ConsoleColor.Yellow;
                Debug.WriteLine("[TESTSTARTING] {0}: {1}", info.TestDisplayName, info.MethodName);
                Console.ResetColor();
#endif
            }
        }

        protected void OnTestSkippedEvent(TestSkippedInfo info)
        {
            lock (consoleLock)
            {
                if (this.TestSkippedEvent != null)
                {
                    TestSkippedEvent(this, info);
                }
#if DEBUG
                Console.ForegroundColor = ConsoleColor.Yellow;
                Debug.WriteLine("[SKIP] {0}: {1}", info.TestDisplayName, info.SkipReason);
                Console.ResetColor();
#endif
            }
        }

        public void Dispose()
        {
            _assemblyRunner.Dispose();
            _assemblyRunner = null;
        }
    }
}

P:S.:

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

16.806 Beiträge seit 2008
vor 4 Jahren

Magst Du mal kurz erklären, was das Ziel des ganzen Vorhabens ist, was die Test Runtime so heute nicht unterstützt?

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 4 Jahren

Es wurde gewünscht, dass wir einen eigenen TestRunner haben.
Der soll die Tests ausführen.
Basierend auf XUnit.
Das funktioniert.
Die Tests liegen in einer anderen Assembly.

Das gewünschte Verhalten wäre, dass die trx (Test Results) Datei geschrieben wird,
während der TestRunner ausgeführt wird, was ja natürlich hier nicht im Code vorhanden ist.

Jetzt wäre die Frage, ob von
https://www.nuget.org/packages/Microsoft.TestPlatform.Extensions.TrxLogger/
z.B. der Logger irgendwie benutzt werden kann, um die trx Datei zu schreiben?

Aber anscheinend kann xUnit - soweit ich recherchiert habe - aber nicht auf die Events von diesem zugreifen.

Das heisst:

Wenn ich wüsste, dass das so nicht geht (xUnit + Microsoft Object Model + Extensions TrxLogger)
müsste ich einen eigenen Mechanismus implementieren.

Denn sonst würden ja einmal die Tests ausgeführt.

Und dann in der Kommandozeile mit


dotnet test --logger trx --no-build /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=.\coverage\ Codan.Argus.AcceptanceTests

Würden die Tests noch einmal ausfgeführt. (hier sorgt der Zusatz --logger trx dafür, dass die trx Datei geschrieben wird.

Vielleicht ist es jetzt klarer? 😃

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

16.806 Beiträge seit 2008
vor 4 Jahren

Wollt ihr einen eigenen TestRunner, damit ihr einen eigenen TestRunner habt, oder steckt dahinter wirklich auch eine Anforderung, die mit den aktuellen Runner(n) nicht abgedeckt ist?
Weil viel lässt sich ja prinzipiell auch durch ordentliche Tests und Co konfigurieren, sodass ein eigener Test Runner nur selten wirklich gebraucht wird.

Dass eine TRX Datei während dem Testen bereits geschrieben wird; ungewöhnlich.
I.d.R. wird diese Datei erst am Ende geschrieben - es kann ja prinzipiell Race Conditions geben.
Je nachdem welche Umgebung Du hast, gibt es CI Plattformen, die auf das Erstellen von TRX "hören" und welche bei denen man das TRX Result aktiv publishen muss.
Bei ersterem könnte es also die Race Condition geben, dass die Testsresultate aufgenommen werden, obwohl die Tests gar nicht fertig sind.

Ich hoffe, dass ihr euch das gut überlegt habt 😃