Testerzeugung

Unittests haben in der Regel die folgenden Bestandteile:

Im Folgenden finden Sie eine Beispielteststruktur:

[Test]
void MyTest() {
    // data
    ArrayList a = new ArrayList();

    // method sequence
    a.Add(5);

    // assertions
    Assert.IsTrue(a.Count==1);
    Assert.AreEqual(a[0], 5);
}

IntelliTest kann relevante Argumentwerte für allgemeinere parametrisierte Unittests, in welchen die Sequenz der Methodenaufrufe und Assertionen angegeben werden, häufig automatisch bestimmen.

Testgeneratoren

IntelliTest generiert Testfälle, indem es eine Sequenz von Methoden der zu testenden Implementierung zum Ausführen auswählt, und dann Eingaben für die Methoden generiert, während es Assertionen zu den abgeleiteten Daten überprüft.

Ein parametrisierter Unittest gibt in seinem Text direkt eine Sequenz von Methodenaufrufen an.

Wenn IntelliTest Objekte erstellen muss, werden der Sequenz nach Bedarf automatisch Aufrufe an die Konstruktoren und Factorymethoden hinzugefügt.

Parametrisierte Unittests

Parametrisierte Unittests (PUTs) sind Tests, die Parameter annehmen. Im Gegensatz zu herkömmlichen Unittests, bei denen es sich in der Regel um geschlossene Methoden handelt, nehmen PUTs einen beliebigen Satz von Parametern an. Ist es wirklich so einfach? Ja: von dort aus versucht IntelliTest, die (minimale) Gruppe der Eingaben zu generieren, die den aus dem Test erreichbaren Code vollständig abdecken.

PUTs werden mit dem benutzerdefinierten PexMethod-Attribut definiert, ähnlich wie MSTest (oder NUnit, xUnit). PUTs sind Instanzmethoden, die logisch in Klassen gruppiert sind, die mit PexClass markiert wurden. Das folgende Beispiel zeigt einen einfachen PUT, der in der MyPexTest-Klasse gespeichert ist:

[PexMethod]
void ReplaceFirstChar(string target, char c) {

    string result = StringHelper.ReplaceFirstChar(target, c);

    Assert.AreEqual(result[0], c);
}

wobei ReplaceFirstChar eine Methode ist, die das erste Zeichen einer Zeichenfolge ersetzt:

class StringHelper {
    static string ReplaceFirstChar(string target, char c) {
        if (target == null) throw new ArgumentNullException();
        if (target.Length == 0) throw new ArgumentOutOfRangeException();
        return c + target.Substring(1);
    }
}

Aus diesen Test kann IntelliTest für einen PUT, die viele Ausführungspfade des getesteten Codes abdeckt, automatisch Eingaben generieren. Jede Eingabe, die einen unterschiedlichen Ausführungspfad abdeckt, wird als ein Unittest „serialisiert“:

[TestMethod, ExpectedException(typeof(ArgumentNullException))]
void ReplaceFirstChar0() {
    this.ReplaceFirstChar(null, 0);
}
...
[TestMethod]
void ReplaceFirstChar10() {
    this.ReplaceFirstChar("a", 'c');
}

Generische parametrisierte Unittests

Parametrisierte Unittests können generische Methoden sein. In diesem Fall muss der Benutzer die verwendeten Typen angeben, um die Methode mit PexGenericArguments zu instanziieren.

[PexClass]
public partial class ListTest {
    [PexMethod]
    [PexGenericArguments(typeof(int))]
    [PexGenericArguments(typeof(object))]
    public void AddItem<T>(List<T> list, T value)
    { ... }
}

Zulassen von Ausnahmen

IntelliTest bietet zahlreiche Validierungsattribute, um bei der Selektierung der Ausnahmen in erwarteten Ausnahmen und unerwartete Ausnahmen zu helfen.

Erwartete Ausnahmen generieren negative Testfälle mit den entsprechenden Anmerkungen wie z.B. ExpectedException(typeof(xxx)), während unerwartete Ausnahmen fehlgeschlagene Testfälle generieren.

[PexMethod, PexAllowedException(typeof(ArgumentNullException))]
void SomeTest() {...}

Die Validierungssteuerelemente sind:

Testen von internen Typen

IntelliTest kann interne Typen „testen“, sofern sie angezeigt werden können. Damit IntelliTest die folgenden Typen erkennen kann, wird das folgende Attribut Ihrem Produkt oder Testprojekt von den Visual Studio-IntelliTest-Assistenten hinzugefügt:

[assembly: InternalsVisibleTo("Microsoft.Pex, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]

Annahmen und Assertionen

Benutzer können Annahmen und Assertionen verwenden, um Vorbedingungen (Annahmen) und Nachbedingungen (Assertionen) zu ihren Tests auszudrücken. Wenn IntelliTest einen Satz von Parameterwerten generiert und den Code „untersucht“, könnte möglicherweise eine Annahme des Tests verletzt werden. In diesem Fall wird für diesen Pfad kein Test generiert, sondern er wird ohne Benachrichtigung ignoriert.

Assertionen sind ein bekanntes Konzept in regulären Unittest-Frameworks, weshalb IntelliTest die integrierten Assert-Klassen, die von jedem unterstützten Testframework bereitgestellt werden, bereits „versteht“. Die meisten Frameworks stellen jedoch keine Assume-Klasse zur Verfügung. In diesem Fall stellt IntelliTest die PexAssume-Klasse zur Verfügung. Wenn Sie kein bereits vorhandenes Testframework verwenden, verfügt IntelliTest auch über die PexAssert-Klasse.

[PexMethod]
public void Test1(object o) {
    // precondition: o should not be null
    PexAssume.IsNotNull(o);

    ...
}

Insbesondere kann die „Non-Nullness“-Annahme („Nicht-NULL“-Annahme) als benutzerdefiniertes Attribut codiert werden:

[PexMethod]
public void Test2([PexAssumeNotNull] object o)
// precondition: o should not be null
{
   ...
}

Precondition

Eine Vorbedingung einer Methode drückt die Bedingungen aus, unter denen die Methode erfolgreich ausgeführt wird.

In der Regel wird die Vorbedingung durch Überprüfen der Parameter und des Objektzustands, und, wenn sie verletzt wird, durch Auslösen einer ArgumentException oder InvalidOperationException erzwungen.

In IntelliTest wird eine Vorbedingung eines parametrisierten Unittests mit PexAssume ausgedrückt.

Nachbedingung

Eine Nachbedingung einer Methode drückt die Bedingungen aus, die während und nach der Ausführung der Methode gelten sollen, vorausgesetzt, dass die Vorbedingungen gültig sind.

Die Nachbedingung wird in der Regel durch Aufrufe an Assert-Methoden erzwungen.

In IntelliTest wird eine Nachbedingung eines parametrisierten Unittests mit PexAssert ausgedrückt.

Fehlgeschlagenen Tests

Wann schlägt ein generierter Test fehl?

  1. Wenn er nicht innerhalb der konfigurierten Pfadgrenzen beendet wird, wird er als fehlgeschlagen betrachtet, es sei denn, die Option TestExcludePathBoundsExceeded wurde festgelegt

  2. Wenn der Test eine PexAssumeFailedException auslöst, ist er erfolgreich. Allerdings wird er in der Regel herausgefiltert, es sei denn, TestEmissionFilter ist auf Alle festgelegt

  3. Wenn der Test eine Assertion verletzt, z.B. durch Auslösen einer Assertionsverletzungsausnahme eines Unittestframeworks, schlägt er fehl

Wenn keiner der oben genannten Fälle eine Entscheidung erzeugt, ist ein Test nur dann erfolgreich, wenn er keine Ausnahme auslöst. Assertionsverstöße werden auf die gleiche Weise wie Ausnahmen behandelt.

Einrichtung und Löschung

Im Rahmen der Integration mit Testframeworks unterstützt IntelliTest das Erkennen und Ausführen von Setup- und Löschmethoden.

Beispiel

using Microsoft.Pex.Framework;
using NUnit.Framework;

namespace MyTests
{
    [PexClass]
    [TestFixture]
    public partial class MyTestClass
    {
        [SetUp]
        public void Init()
        {
            // monitored
        }

        [PexMethod]
        public void MyTest(int i)
        {
        }

        [TearDown]
        public void Dispose()
        {
            // monitored
        }
    }
}

Weitere nützliche Informationen

Sie haben Fragen oder Anmerkungen?

Posten Sie Ihre Ideen und Featureanfragen in der Entwicklercommunity.