Inhaltsverzeichnis

Testunterstützung

Der Abschnitt Test bietet explizite Unterstützung für das Testen der Implementierung. Siehe jedoch auch die Features zu Debug, Trace und Logging.

Methoden für Unit Tests

Einzelne Methoden am Baustein können attributiert werden, um anzugeben, dass diese Methode einen Unit-Test darstellt. Eine Unit-Test Methode darf keinen Rückkehrwert haben, sondern ist immer vom Typ void. Zum Beispiel:

[ModuleUnitTest]
private void CheckGenerics()
{
	// check something
}

Mit der Methode RunUnitTests werden sämtliche derart attributierten Methoden ausgeführt.

protected override void OperationDurchfuehren()
{
	// ...snip...
#if DEBUG
	this.Test.RunUnitTests();
#endif
}

Der Unit-Test gilt als erfolgreich, wenn keine Condition oder Exception aufgeworfen wurde. Die Ausführungszeit wird bei einem InfoLevel > 1 im Monitor gezeigt:

User     33640 /5300  : Performance: AfBeispielDotnetDemo.CheckGenerics succeeded - 774ms

Außerdem wird die Ausführung in der Log-Datei mit Ursprung der Sourcecode-Datei und -Zeile festgehalten:

<LogEntry Date="2020-04-02 12:33:17" Severity="Performance" Source="D:\utils\Test\Module\DM01\AfBeispielDotnetDemo\AfBeispielDotnetDemo.cs" Line="26" ThreadId="3">
  <Message>AfBeispielDotnetDemo.CheckGenerics succeeded - 774ms</Message>
</LogEntry>

Eine Methode kann mehrfach mit dem ModuleUnitTest attributiert werden. Jedes Attribut steht für einen Testlauf, möglicherweise mit unterschiedlichen Bedingungen.

Wenn eine Methode mit einem oder mehreren ModuleUnitTest-Attributen versehen ist, kann es eine Methode mit dem gleichen Namen wie dem der Testmethode und dem Suffix _prepare geben, die vor der Ausführung jedes einzelnen Testlaufs der Testmethode ausgeführt wird. Ebenfalls kann es eine zur Testmethode passende Methode mit dem Suffix _cleanup geben, die nach jedem jeweiligen Testlauf ausgeführt wird. Diese beiden Methoden werden nicht in der Zeitmessung und nicht in dem Task aufgenommen. Diese beiden Methoden können, müssen aber nicht vorhanden sein. Sie müssen jedoch die identische Signatur besitzen, also ebenfalls vom Typ void sein und die gleichen Argumente bekommen. Beispiel:

[ModuleUnitTest(task: "NewList")]
private void TNA_ObjList00_NewList()
{
    const int count = 1000;
    for (var i = 1; i <= count; ++i) {
        var list = this.Data.NewList(DzugStrukturSnawa.Type);
        this.AssertIsNotNull(list);
    }
}
 
private void TNA_ObjList00_NewList_prepare()
{
    // ensure datatype is prepared and not measured
    var list = this.Data.NewList(DzugStrukturSnawa.Type);
    this.AssertIsNotNull(list);
}

Tests mit Fehler

Falls ein Unit-Test eine Condition oder Exception aufwirft, oder der Arbeitsgang auf IsFault gesetzt wurde, gilt der Test als nicht bestanden. Das wird sowohl im Monitor als auch in der Log-Datei verbucht.

OopsEntr 33640 /5300  : D:\utils\Test\Module\DM01\AfBeispielDotnetDemo\AfBeispielDotnetDemo.cs(26): Die angegebene Umwandlung ist ungültig.
   bei AL.BeispielDotnetDemo.AfBeispielDotnetDemo.AfBeispielDotnetDemo.CheckGenerics() in D:\utils\Test\Module\DM01\AfBeispielDotnetDemo\AfBeispielDotnetDemo.cs:Zeile 31. (AF-BEISPIEL-DOTNET-DEMO.DURCHFUEHREN) (TEST.001) [0x0596008c (TEST.AF-BEISPIEL-DOTNET-DEMO)/{BAA3FC7F-2C6F-434C-8096-56C08A95CB35}/33640//V9.9.0.76]
User     33640 /5300  : Performance: AfBeispielDotnetDemo.CheckGenerics failed - 1.982ms
<LogEntry Date="2020-04-02 14:53:07" Severity="Exception" Source="D:\utils\Test\Module\DM01\AfBeispielDotnetDemo\AfBeispielDotnetDemo.cs" Line="26" ThreadId="3">
  <Exception Type="System.InvalidCastException" Source="AL.BeispielDotnetDemo.AfBeispielDotnetDemo.AfBeispielDotnetDemo.CheckGenerics">
    <Message>Die angegebene Umwandlung ist ungültig.</Message>
    <StackTrace>   bei AL.BeispielDotnetDemo.AfBeispielDotnetDemo.AfBeispielDotnetDemo.CheckGenerics() in D:\utils\Test\Module\DM01\AfBeispielDotnetDemo\AfBeispielDotnetDemo.cs:Zeile 31.</StackTrace>
  </Exception>
</LogEntry>
<LogEntry Date="2020-04-02 14:53:07" Severity="Performance" Source="D:\utils\Test\Module\DM01\AfBeispielDotnetDemo\AfBeispielDotnetDemo.cs" Line="26" ThreadId="3">
  <Message>AfBeispielDotnetDemo.CheckGenerics failed - 1.982ms</Message>
</LogEntry>

Wenn der Test fehlschlägt, wird eine Ausnahme UnitTestException aufgeworfen, die ggf. eine InnerException mit der tatsächlich aufgeworfene Ausnahme oder Condition beinhaltet.

try {
	this.Test.RunUnitTests();
}
catch (UnitTestException exception) {
	// ...snip...
}

Wenn ein Testlauf fehlschlägt, werden weitere Testläufe trotzdem ausgeführt. Erst am Ende aller Testläufe wird im Fehlerfall eine UnitTestException aufgeworfen. Wenn Testläufe mit einer aufgeworfenen Ausnahme fehlschlagen, ist die erste aufgeworfene Ausnahme als InnerException an der UnitTestException gespeichert. Wenn weitere Testläufe mit einer Ausnahme fehlschlagen, können diese über die Eigenschaft AdditionalExceptions abgefragt werden.

Spezielle Attributierung

Das ModuleUnitTest Attribut kennt einige optionale Argumente, mit dem der Unit Test zusätzlich gesteuert werden kann.

Caption

Mit dem Argument Caption kann der Testlauf mit einem zusätzlichem Namen versehen werden.

[ModuleUnitTest(caption: "GenericSample")]
private void CheckGenerics()
{
	// check something
}

Im Monitor wirkt sich das wie folgt aus:

User     37684 /38588 : Performance: AfBeispielDotnetDemo.CheckGenerics.GenericSample succeeded - 1.750ms

Und in der Log-Datei wie folgt:

<LogEntry Date="2020-04-02 15:05:01" Severity="Performance" Source="D:\utils\Test\Module\DM01\AfBeispielDotnetDemo\AfBeispielDotnetDemo.cs" Line="26" ThreadId="3">
  <Message>AfBeispielDotnetDemo.CheckGenerics.GenericSample succeeded - 1.750ms</Message>
</LogEntry>

Da das ModuleUnitTest Attribut bei einer Methode mehrfach angegeben werden kann, können so die einzelnen Testläufe unterschiedlich beschriftet werden.

Task

Falls der Testlauf ein Bestandteil einer Sammlung von Tests ist, kann ein Bezug zu dieser Sammlung (Task) festgelegt werden. Die Angabe wird in den Output Daten übernommen und kann auch bei der Messung mit VTune sinnvoll genutzt werden.

[ModuleUnitTest(caption: "Bulk", task: "DataOps", data: new object[] { 1000, "B_", 512 })]
[ModuleUnitTest(caption: "Small", task: "DataOps", data: new object[] { 10, "S_", 16 })]
private void BuildData(int instancesToCreate, string id, int lengthToUse)
{
	// snip
}

Ergibt im Monitor:

User     9092  /32448 : Performance: DataOps (AfBeispielDotnetDemo.BuildData.Bulk) succeeded - 119ms
User     9092  /32448 : Performance: DataOps (AfBeispielDotnetDemo.BuildData.Small) succeeded - 9ms

Wenn Testläufe mit Task-Markierung definiert sind, kann auch beim Aufruf von RunUnitTests optional mitgegeben werden, welche Tasks ausgeführt werden sollen:

public void RunUnitTests(params string[] tasks)
try {
	this.Test.RunUnitTests("NewList", "DataOps");
}
catch (UnitTestException exception) {
	// ...snip...
}

Repeat

Mit der Angabe Repeat kann gesteuert werden, wie oft die zugrundeliegende Methode für den Testlauf aufgerufen werden soll. Diese Angabe macht besonders bei Unit-Tests, die für Performance-Messungen gebaut wurden, Sinn.

[ModuleUnitTest(repeat: 1000, caption: "Bulk")]
[ModuleUnitTest(repeat: 1, caption: "Singular")]
private void CheckGenerics()
{
	// check something
}

Max Duration

Mit der Angabe MaxDuration kann gesteuert werden, wie lange die zugrundeliegende Methode für den Testlauf dauern darf. Diese Angabe macht besonders bei Unit-Tests, die für Performance-Messungen gebaut wurden, Sinn.

[ModuleUnitTest(repeat: 1000, caption: "Bulk", maxDuration: 20)]
[ModuleUnitTest(repeat: 1, caption: "Singular", maxDuration: 5)]
private void CheckGenerics()
{
	// check something
}

Überschreitet die Methode diese Angabe wird eine entsprechende Exception aufgeworfen.

<LogEntry Date="2020-09-30 14:08:57" Severity="Exception" Source="D:\Test\TBTestUnitTest\TbEfunTestmoduleUnitTest.cs" Line="32" ThreadId="3">
 <Exception Type="System.Exception" Source="D:\Test\TBTestUnitTest\TbEfunTestmoduleUnitTest.cs">
   <Message>Die maximale Laufzeit von 5ms wurde überschritten.</Message>
    <StackTrace />
 </Exception>
</LogEntry>

Description

Mit dem Argument Description kann der Testlauf mit einem Beschreibung versehen werden.1)

[ModuleUnitTest(description: "BeschreibungsText")]
private void CheckGenerics()
{
	// check something
}

Argumente

Die Unit-Test Methoden sollten grundsätzlich vom Typ void sein. Sie können allerdings Argumente beinhalten, die pro Testlauf unterschiedlich bestückt werden können. Auch hier macht der Einsatz von der Caption-Angabe Sinn:

[ModuleUnitTest(caption: "Bulk", data: new object[] { 1000, "B_", 512 })]
[ModuleUnitTest(caption: "Small", data: new object[] { 10, "S_", 16 })]
private void BuildData(int instancesToCreate, string id, int lengthToUse)
{
	// ...snip...
}

Monitor Ausgabe für dieses Beispiel:

User     33324 /4184  : Performance: AfBeispielDotnetDemo.BuildData.Bulk succeeded - 696ms
User     33324 /4184  : Performance: AfBeispielDotnetDemo.BuildData.Small succeeded - 11ms

Und in der Log-Datei findet sich:

<LogEntry Date="2020-04-02 15:20:17" Severity="Performance" Source="D:\utils\Test\Module\DM01\AfBeispielDotnetDemo\AfBeispielDotnetDemo.cs" Line="26" ThreadId="3">
  <Message>AfBeispielDotnetDemo.BuildData.Bulk succeeded - 696ms</Message>
</LogEntry>
<LogEntry Date="2020-04-02 15:20:17" Severity="Performance" Source="D:\utils\Test\Module\DM01\AfBeispielDotnetDemo\AfBeispielDotnetDemo.cs" Line="27" ThreadId="3">
  <Message>AfBeispielDotnetDemo.BuildData.Small succeeded - 11ms</Message>
</LogEntry>

Die Argumentwerte müssen gemäß .NET-Standards konstante Ausdrücke sein. Bei der Zuweisung wird versucht, die in den Attributen festgelegten Argumentwerte auf den Datentyp des jeweiligen Parameters zu konvertieren. Fehler bei der Konvertierung werden mit einer Oopsmeldung quittiert, führen jedoch nicht zum Abbruch des Testlaufs. Stattdessen erfolgt die Argumentzuweisung für den jeweiligen Parameter mit einem Defaultwert.

[ModuleUnitTest(task: "Timestamp", repeat: 10000, data: "undefined")]
private void TNA_TimestampVarTest(DateTime tsVar)
{
	// ...snip...
}
OopsEntr 31740 /33280 : D:\utils\Test\Module\DM01\AfBeispielDotnetDemo\AfBeispielDotnetDemo.cs(63): TNA_TimestampVarTest - Timestamp (AfBeispielDotnetDemo.TNA_TimestampVarTest): Die Zeichenfolge wurde nicht als gültige DateTime erkannt. Ein unbekanntes Wort beginnt bei Index 0.
   bei System.DateTimeParse.Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles)
   bei System.Convert.ToDateTime(String value, IFormatProvider provider)
   bei System.String.System.IConvertible.ToDateTime(IFormatProvider provider)
   bei System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
   bei TeamWiSE.Runtime.Common.TestSupport.InvokeWithArgs(MethodInfo methodInfo, ModuleUnitTestAttribute methodRun) in D:\WORK\dll32\NativeSupport\Common\TestSupport.cs:Zeile 294. (AF-BEISPIEL-DOTNET-DEMO.DURCHFUEHREN) (TEST.001) [0x092ecaec (TEST.AF-BEISPIEL-DOTNET-DEMO)/{C13243B3-CEC4-4B7E-8C7E-5FEFE1424DFF}/31740//V9.9.0.76]

Ereignisse

Beim Ablauf bzw. Ausführung von Unit-Tests in NativeSupport wird dem Entwickler über Ereignisse die Möglichkeit geboten, Einfluss auf den konkreten Testverlauf nehmen.2) Dafür stehen folgende Events zur Verfügung:

Executing

using TeamWiSE.Runtime.ModuleUnitTest;
public event EventHandler<ExecutingArgs> Executing;
public void OnExecuting(Object sender, ExecutingArgs e);

Dieses Ereignis wird für jeden betroffenen Unit-Test aufgerufen, bevor es ausgeführt wird. Die Klasse ExecutingArgs stellt dabei die Ereignisdaten bereit.3)

ExecutingArgs

Stellt Ereignisdaten für das Ereignis Executing bereit.4) Zur Verfügung stehen folgende Eigenschaften:

Cancel

bool Cancel {get; set; }

Hiermit kann ggf. die Ausführung des jeweiligen Unit-Tests verhindert werden. Der Cancel-Auftrag betrifft nur den im aktuellen Ereignis betroffenen Test. Der Wert wird mit false vorbelegt.

Caption

String Caption { get; set; }

Caption ist ein zusätzlicher Name am Testlauf und wird aus Feld Caption des ModuleUnitTest Attributes vorbelegt. Die Eigenschaft wird beim Protokollieren, Logs, Outputs und im Monitor angezeigt. Eine Anpassung der Caption bewirkt, dass die veränderte Angabe anstelle des Originaltextes im weiteren Verlauf verwendet bzw. angezeigt wird.

Repeat

UInt32 Repeat { get; set; }

Stammt aus der Angabe Repeat im zugehörigen ModuleUnitTest Attribut und gibt an, wie oft die Methode, zu der das ModuleUnitTest Attribut definiert wurde, ausgeführt werden soll. Durch eine Veränderung wird die Häufigkeit der Ausführung der Methode entsprechend angepasst.

MethodName

String MethodName { get; }

Ist der Name der Methode, zu der das ModuleUnitTest Attribut gesetzt wurde. Der Name kann nicht verändert werden. Für weitere Informationen siehe Methoden fuer UnitTests.

MaxDuration

Int64 MaxDuration { get; set; }

MaxDuration steuert, wie lange die zugrundeliegende Methode maximal für den Testlauf dauern darf. Die Dauer wird in Millisekunden festgelegt. Der Wert wird vorbestückt aus dem Feld Max Duration des ModuleUnitTest Attributs. Eine Anpassung dieser Angabe wird bei der nachfolgenden Ausführung berücksichtigt. Das Überschreiten führt zu einer TestSupportException.

TaskName

String TaskName { get; set; }

Die einzelnen Module-Unit-Tests können Bestandteil einer Sammlung von Tests sein, von sogenannten Tasks. Die Angabe wird bei Logs und Outputs und im Monitor angezeigt, für mehr Information siehe Task.

Description

String Description { get; }

Der Wert wird vorbestückt aus dem Feld Description des zugehörigen ModuleUnitTest Attributs. Sie kann z.B. erklärende Informationen zu dem jeweiligen Test enthalten.

Line

public Int32 Line { get; }

Ist die Zeilenangabe, auf welcher das ModuleUnitTest Attribut definiert wurde.

Source

public String Source { get; }

Source enthält den Name der Datei in welcher das ModuleUnitTest Attribut definiert wurde.

Executed

using TeamWiSE.Runtime.ModuleUnitTest;
public event EventHandler<ExecutedArgs> Executed;
public void OnExecuted(object sender, ExecutedArgs e);

Dieses Ereignis wird für jeden betroffenen Unit-Test aufgerufen, nachdem es ausführt wurde. Die Klasse ExecutedArgs stellt dabei die Ereignisdaten bereit.5)

ExecutedArgs

Stellt Ereignisdaten für das Ereignis Executed bereit.6) Zur Verfügung stehen die folgende Eigenschaften:

Caption

String Caption { get; }

Gibt an mit welcher Caption der Testlauf durchgeführt wurde, und dementsprechend beim Protokollieren, Logs, Outputs und im Monitor verwendet wurde.

Repeat

UInt32 Repeat { get; }

Gibt an, wie oft die der Unit-Test ausgeführt wurde.

MethodName

String MethodName { get; }

Ist der Name der ausgeführten Methode. Für weitere Informationen siehe Methoden fuer UnitTests.

MaxDuration

Int64 MaxDuration { get; }

MaxDuration war die maximale Anzahl der Millisekunde, die für die Überwachung des Unit-Tests verwendet wurde.

TaskName

String TaskName { get; }

Die TaskName Eigenschaft benennt die Sammlung von Tests, den sogenannten Task, die als Gruppierung für den ausgeführten Test verwendet wurde.

Description

String Description { get; }

Die Angabe Description wurde bei der Ausführung des Tests mitgeführt. Sie kann z.B. erklärende Informationen zu dem jeweiligen Test enthalten.

Line

public Int32 Line { get; }

Ist die Zeilenangabe, auf welcher das ModuleUnitTest Attribut definiert wurde.

Source

public String Source { get; }

Source enthält den Name der Datei in welcher das ModuleUnitTest Attribut definiert wurde.

Success

bool Success { get; }

Success gibt an, ob die Ausführung des Tests erfolgreich gewesen ist.

Elapsed

TimeSpan Elapsed { get;}

Die Eigenschaft Elapsed gibt an, wie viel Zeit für die Ausführung des Tests benötigt wurde. Die Dauer wird in Logs, Outputs und Monitor in Millisekunden angezeigt.

Module

Module Module { get; }

Die Eigenschaft Module verweist auf den TAA-Baustein, in dessen Kontext der Test ausgeführt wurde. Über Module können bspw. auch Informationen über den Gevo, Arbeitsgang und aufgetretene Conditions besorgt werden.

Exception

UnitTestException Exception { get; }

Die Exception wird mit einer Sammlung von TestSupportException bestückt, welche bei der Ausführung aufgetreten sind.

Mocken

Mocken

VTune Unterstützung

Falls mit dem Werkzeug Intel® VTune™ Profiler Messungen vorgenommen werden, tauchen ausgeführte Unit-Tests dort als Tasks auf.

In den Abschnitten Summary, Bottom-Up und Platform kann mit der Task gearbeitet werden, oder auch darauf gefiltert werden. Hier einige Screenshots:

1) , 2) , 3) , 4) , 5) , 6)
ab v10.00