2009-01-13 12 views
6

Ich schreibe einen Parser, der den 32-Bit-Opcode für jeden Befehl generiert. Zum Beispiel für die folgende Erklärung ab:Wie kann ich Einheitentestfälle für einen Parser am besten schreiben?

set lcl_var = 2 

mein Parser die folgenden Opcodes erzeugt:

// load immdshort 2 (loads the value 2) 
0x10000010 
// strlocal lclvar (lcl_var is converted to an index to identify the var) 
0x01000002 

Bitte beachten Sie, dass lcl_var kann alles sein heißt, kann eine beliebige Variable angegeben werden. Wie kann ich die Unit-Testfälle dafür schreiben? Können wir eine harte Codierung der Werte vermeiden? Gibt es eine Möglichkeit, es generisch zu machen?

+0

Harte Codierung ist am besten, der Komponententest sollte Ihnen sehr genau sagen, wo der Fehler in der Codebasis ist. Wenn es generisch ist, könnte der Fehler in der "Liste der gültigen Codes" und nicht im Parser stehen. – Paxic

Antwort

0

Sie geben nicht an, in welcher Sprache Sie den Parser schreiben, daher gehe ich aus Gründen der Argumentation davon aus, dass Sie eine objektorientierte Sprache verwenden.

Wenn dies der Fall ist, könnte die Abhängigkeitsinjektion Ihnen hier helfen. Wenn das Ziel der emittierten Opcodes eine Instanz einer Klasse ist (wie z. B. Datei), versuchen Sie, Ihrer Emitterklasse einen Konstruktor zu geben, der ein Objekt dieses Typs als Ziel für den ausgegebenen Code verwendet. Anschließend können Sie aus einem Komponententest ein Mock-Objekt übergeben, das eine Instanz einer Unterklasse der Zielklasse darstellt, die ausgegebenen Opcodes für bestimmte Anweisungen erfassen und bestätigen, dass sie korrekt sind.

Wenn Ihre Zielklasse nicht leicht erweiterbar ist, möchten Sie möglicherweise eine Schnittstelle erstellen, die sowohl von der Zielklasse als auch von der Mock-Klasse implementiert werden kann.

+0

Ich schreibe den Parser mit C++ – Vinay

0

Wie ich es verstehe, würden Sie zuerst einen Test für Ihr konkretes Beispiel schreiben, also dort, wo der Eingang zu Ihrem Parser ist:

set lcl_var = 2 

und der Ausgang ist:

0x10000010 // load immdshort 2 
0x01000002 // strlocal lclvar 

Wenn Sie Sie haben den Produktionscode implementiert, um diesen Test zu bestehen, und ihn neu strukturiert. Wenn Sie nicht zufrieden sind, kann er jede lokale Variable verarbeiten, einen anderen Test mit einer anderen lokalen Variablen schreiben und sehen, ob er erfolgreich ist oder nicht. z.B. neuer Test mit Eingang:

set lcl_var2 = 2 

Und Ihre neuen Test schreiben die eine andere Ausgabe zu erwarten, die Sie wollen. Tun Sie dies solange, bis Sie sich davon überzeugt haben, dass Ihr Produktionscode robust genug ist.

2

Es hängt davon ab, wie Sie Ihren Parser strukturiert haben. Ein Unit-Test testet eine einzelne UNIT.

Wenn Sie also Ihren gesamten Parser als eine einzelne Einheit testen möchten, können Sie ihm eine Liste von Befehlen geben und sicherstellen, dass er die richtigen Opcodes erzeugt (die Sie beim Schreiben des Tests manuell überprüft haben). Sie können Tests für jeden Befehl schreiben und die normale Verwendung, Edge-Case-Verwendung, Just-Edge-Case-Verwendung testen. Zum Beispiel testet, dass:

Satz lcl_var = 2

Ergebnisse in:

0x10000010 0x01000002

Und das gleiche für 0, -1, MAX_INT-1, MAX_INT + 1,. ..

Sie kennen das korrekte Ergebnis für diese Werte. Gleiches gilt für verschiedene Variablen.

1
int[] opcodes = Parser.GetOpcodes("set lcl_var = 2"); 
Assert.AreEqual(2, opcodes.Length); 
Assert.AreEqual(0x10000010, opcodes[0]); 
Assert.AreEqual(0x01000002, opcodes[1]); 
1

Wenn Ihre Frage ist: „Wie führe ich den gleichen Test mit verschiedenen Eingaben und erwarteten Werten ohne das Schreiben einen xUnit Tests pro Input-Output-Kombination?“

Dann wäre die Antwort darauf, etwas wie die RowTest NUnit-Erweiterung zu verwenden. Ich schrieb kürzlich einen quick bootup post auf meinem Blog. Ein Beispiel hierfür

[TestFixture] 
    public class TestExpression 
    { 
     [RowTest] 
     [Row(" 2 + 3 ", "2 3 +")] 
     [Row(" 2 + (30 + 50) ", "2 30 50 + +")] 
     [Row(" ((10+20) + 30) * 20-8/4 ", "10 20 + 30 + 20 * 8 4/-")] 
     [Row("0-12000-(16*4)-20", "0 12000 - 16 4 * - 20 -")] 
     public void TestConvertInfixToPostfix(string sInfixExpr, string sExpectedPostfixExpr) 
     { 
      Expression converter = new Expression(); 
      List<object> postfixExpr = converter.ConvertInfixToPostfix(sInfixExpr); 

      StringBuilder sb = new StringBuilder(); 
      foreach(object term in postfixExpr) 
      { 
       sb.AppendFormat("{0} ", term.ToString()); 
      } 
      Assert.AreEqual(sExpectedPostfixExpr, sb.ToString().Trim()); 
     } 
0

wäre Es ist nicht klar, ob Sie für eine Methode oder eine bestimmte Technologie suchen Ihre Tests zu verwenden.

Soweit es die Methodik geht, möchten Sie vielleicht keine umfangreichen Komponententests durchführen. Vielleicht wäre ein besserer Ansatz, einige Programme in Ihre domänenspezifische Sprache zu schreiben und dann die Opcodes auszuführen, um ein Ergebnis zu erzeugen. Die Testprogramme würden dann dieses Ergebnis überprüfen. Auf diese Weise können Sie eine Menge Code ausführen, aber am Ende nur ein Ergebnis prüfen. Beginnen Sie mit einfachen, um offensichtliche Bugs auszuspülen und zu härteren zu bewegen. Anstatt die erzeugten Opcodes jedes Mal zu überprüfen.

Ein anderer Ansatz besteht darin, automatisch Programme in Ihrer domänenspezifischen Sprache zusammen mit den erwarteten Opcodes zu generieren. Dies kann sehr einfach sein, wie ein Perl-Skript zu schreiben, die eine Reihe von Programmen wie produziert:

Satz lcl_var = 2

Satz lcl_var = 3

Sobald Sie haben eine Reihe von Testprogrammen in Ihrer Sprache, die Haben Sie eine korrekte Ausgabe, können Sie rückwärts gehen und Unit-Tests generieren, die jeden Opcode prüfen. Da Sie die Opcodes bereits haben, wird es darum gehen, die Ausgabe des Parsers auf Korrektheit zu überprüfen; Überprüfung seines Codes.

Während ich cppunit nicht verwendet habe, habe ich ein In-House-Tool verwendet, das sehr wie cppunit war. Es war einfach, Komponententests mit cppunit zu implementieren.

0

Was möchten Sie testen? Möchten Sie wissen, ob die richtige "Speicher" -Anweisung erstellt wurde? Ob die richtige Variable abgeholt wird? Entscheide dich, was du wissen willst und der Test wird offensichtlich sein. Solange du nicht weißt, was du erreichen willst, wirst du nicht wissen, wie man das Unbekannte testet.

Schreiben Sie in der Zwischenzeit einen einfachen Test. Morgen oder an einem späteren Tag wirst du wieder an diesen Ort kommen, weil etwas kaputt gegangen ist. Zu diesem Zeitpunkt wissen Sie mehr darüber, was Sie tun möchten, und es könnte einfacher sein, einen Test zu entwickeln.

Heute, versuchen Sie nicht, die Person zu sein, die Sie morgen sein werden.