2009-01-13 16 views
6

J'écris un analyseur qui génère l'opcode 32 bits pour chaque commande. Par exemple, pour la déclaration suivante:Comment puis-je mieux écrire des cas de test unitaires pour un analyseur?

set lcl_var = 2 

mon analyseur génère les opcodes suivants:

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

S'il vous plaît noter que lcl_var peut être quelque chose à savoir, peut être donné une variable. Comment puis-je écrire les cas de tests unitaires pour cela? Pouvons-nous éviter de coder en dur les valeurs? Y a-t-il un moyen de le rendre générique?

+0

Le codage dur est le meilleur, le test d'unité devrait vous indiquer très précisément où l'erreur est dans la base de code. Si c'est générique l'erreur pourrait être dans la "liste des codes valides" pas l'analyseur. – Paxic

Répondre

0

Vous ne spécifiez pas la langue dans laquelle vous écrivez l'analyseur, donc je vais supposer pour l'argument que vous utilisez un langage orienté objet.

Si tel est le cas, l'injection de dépendance pourrait vous aider ici. Si la destination des opcodes émis est une instance d'une classe (comme File, par exemple), essayez de donner à votre classe d'émetteur un constructeur qui prend un objet de ce type à utiliser comme destination pour le code émis. Ensuite, à partir d'un test unitaire, vous pouvez passer un objet fantôme qui est une instance d'une sous-classe de votre classe de destination, capturer les codes opération émis pour des instructions spécifiques et affirmer qu'ils sont corrects.

Si votre classe de destination n'est pas facilement extensible, vous pouvez créer une interface basée dessus que la classe de destination et votre classe fictive peuvent implémenter.

+0

J'écris l'analyseur en utilisant C++ – Vinay

0

Si je comprends bien, vous devez d'abord écrire un test pour votre exemple spécifique, à savoir où l'entrée de votre analyseur est:

set lcl_var = 2 

et la sortie est:

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

Lorsque vous avez implémenté le code de production pour passer ce test, et l'avez refactorisé, alors si vous n'êtes pas satisfait, il pourrait gérer n'importe quelle variable locale, écrire un autre test avec une variable locale différente et voir si ça passe ou pas. par exemple. nouveau test avec entrée:

set lcl_var2 = 2 

Et écrivez votre nouveau test pour attendre les différentes sorties que vous voulez. Continuez à le faire jusqu'à ce que vous soyez satisfait que votre code de production est suffisamment robuste.

2

Cela dépend de la façon dont vous avez structuré votre analyseur. Un test d'unité teste une seule unité. Par conséquent, si vous souhaitez tester l'intégralité de votre analyseur en tant qu'unité unique, vous pouvez lui donner une liste de commandes et vérifier qu'elles produisent les opcodes corrects (que vous avez vérifiés manuellement lorsque vous avez écrit le test). Vous pouvez écrire des tests pour chaque commande, et tester l'utilisation normale, l'utilisation des caractères latéraux, l'utilisation juste au-delà des limites. Par exemple, vérifier que:

set lcl_var = 2

résultats dans:

0x10000010 0x01000002

Et même pour 0, -1, MAX_INT-1, MAX_INT + 1,. ..

Vous connaissez le bon résultat pour ces valeurs. Idem pour différentes variables.

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

Si votre question est « Comment puis-je exécuter le même test avec des entrées différentes et les valeurs attendues sans écrire un test xUnit par combinaison d'entrée-sortie? » Ensuite, la réponse à cela serait d'utiliser quelque chose comme l'extension RowTest NUnit. J'ai écrit un quick bootup post sur mon blog récemment. Un exemple de ceci serait

[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

On ne sait pas si vous êtes à la recherche d'une méthodologie ou une technologie spécifique à utiliser pour vos tests.

En ce qui concerne la méthodologie, vous ne voulez peut-être pas effectuer de tests unitaires approfondis. Peut-être une meilleure approche serait d'écrire quelques programmes dans votre langue spécifique au domaine et ensuite exécuter les opcodes pour produire un résultat. Les programmes de test vérifieront alors ce résultat. De cette façon, vous pouvez exercer un tas de code, mais ne cochez qu'un seul résultat à la fin. Commencez avec des simples pour débusquer les bogues évidents et le passage aux plus difficiles. Au lieu de vérifier les opcodes générés à chaque fois.

Une autre approche à prendre est de générer automatiquement des programmes dans votre langage spécifique au domaine avec les opcodes attendus. Cela peut être très simple, comme l'écriture d'un script perl qui produit un ensemble de programmes tels que:

set lcl_var = 2

set lcl_var = 3

Une fois que vous avez une série de programmes de test dans votre langue avoir une sortie correcte, vous pouvez revenir en arrière et générer des tests unitaires qui vérifient chaque opcode. Puisque vous avez déjà les opcodes, il devient nécessaire d'inspecter la sortie de l'analyseur pour vérifier qu'il est correct; revoir son code.

Bien que je n'utilise pas cppunit, j'ai utilisé un outil interne qui ressemblait beaucoup à cppunit. Il était facile d'implémenter des tests unitaires en utilisant cppunit.

0

Que voulez-vous tester? Voulez-vous savoir si l'instruction "store" correcte est créée? Si la bonne variable est ramassé? Décidez ce que vous voulez savoir et le test sera évident. Tant que vous ne savez pas ce que vous voulez accomplir, vous ne saurez pas comment tester l'inconnu.

En attendant, écrivez simplement un test simple. Demain ou plus tard, vous reviendrez à cet endroit parce que quelque chose s'est cassé. À ce moment-là, vous en saurez plus sur ce que vous voulez faire et il pourrait être plus simple de concevoir un test.

Aujourd'hui, n'essayez pas d'être la personne que vous serez demain.