Quelle est la meilleure façon de tester à l'unité une méthode qui ne retourne rien? Plus précisément en c #.
Ce que j’essaie vraiment de tester est une méthode qui prend un fichier journal et l’analyse pour des chaînes spécifiques. Les chaînes sont ensuite insérées dans une base de données. Rien de ce qui n'a été fait auparavant mais TRÈS nouveau sur TDD, je me demande s'il est possible de tester ceci ou si c'est quelque chose qui n'a pas vraiment été testé.
Si une méthode ne retourne rien, c'est l'une des méthodes suivantes.
Méthodes impératives - vous pouvez vérifier si la tâche a bien été effectuée. Vérifiez si le changement d'état a réellement eu lieu. par exemple.
void DeductFromBalance( dAmount )
peut être testé en vérifiant si le solde après ce message est bien inférieur à la valeur initiale par dAmount
Les méthodes d'information - sont rares en tant que membre de l'interface publique de l'objet ... donc normalement pas testées par unité. Cependant, si vous devez le faire, vous pouvez vérifier si le traitement à effectuer sur une notification a bien lieu. par exemple.
void OnAccountDebit( dAmount ) // emails account holder with info
peut être testé en vérifiant si l'e-mail est envoyé
Postez plus de détails sur votre méthode et les gens seront en mesure de mieux vous répondre.
Update : Votre méthode fait 2 choses. En fait, je l'aurais scindé en deux méthodes pouvant maintenant être testées indépendamment.
string[] ExamineLogFileForX( string sFileName );
void InsertStringsIntoDatabase( string[] );
String [] peut être facilement vérifié en fournissant à la première méthode un fichier fictif et les chaînes attendues. La seconde est un peu délicate. Vous pouvez utiliser un Mock (Google ou search stackoverflow sur des frameworks moqueurs) pour imiter la base de données ou appuyer sur la base de données et vérifier si les chaînes ont été insérées au bon endroit. Vérifiez ce fil pour quelques bons livres ... Je recommanderais Pragmatic Unit Testing si vous êtes en crise.
Dans le code, il serait utilisé comme
InsertStringsIntoDatabase( ExamineLogFileForX( "c:\OMG.log" ) );
Testez ses effets secondaires. Ceci comprend:
Bien sûr, il y a une limite à comment beaucoup vous pouvez tester. Vous ne pouvez généralement pas tester avec toutes les entrées possibles, par exemple. Testez de manière pragmatique - suffisamment pour vous assurer que votre code est conçu de manière appropriée et correctement implémenté, et suffisamment pour servir de documentation supplémentaire indiquant les attentes d'un appelant.
Comme toujours: testez ce que la méthode est supposée faire!
Devrait-il changer d’état global (euh, odeur de code!) Quelque part?
Devrait-il appeler une interface?
Devrait-il lancer une exception lorsqu'il est appelé avec les mauvais paramètres?
Ne devrait-il pas lancer une exception lorsqu'il est appelé avec les bons paramètres?
Devrait-il ...?
Les types de renvois/sous-programmes sont des anciennes actualités. Je n'ai pas fait de type Void (sauf si j'étais extrêmement paresseux) depuis 8 ans (à partir du moment de cette réponse, donc un peu avant que cette question ne soit posée).
Au lieu d'une méthode comme:
public void SendEmailToCustomer()
Créez une méthode qui suit le paradigme int.TryParse () de Microsoft:
public bool TrySendEmailToCustomer()
Peut-être n’a-t-il aucune information que votre méthode doit renvoyer pour utilisation à long terme, mais le retour de l’état de la méthode après qu’il effectue son travail est une utilisation énorme pour l’appelant.
De plus, bool n'est pas le seul type d'état. Il existe un certain nombre de fois où un sous-programme précédemment créé peut en réalité renvoyer trois états différents ou plus (Bon, Normal, Mauvais, etc.). Dans ces cas, vous utiliseriez simplement
public StateEnum TrySendEmailToCustomer()
Cependant, alors que Try-Paradigm répond quelque peu à cette question sur la façon de tester un retour à vide, il existe également d'autres considérations. Par exemple, pendant/après un cycle "TDD", vous ferez un "refactoring" et remarquerez que vous faites deux choses avec votre méthode ... brisant ainsi le "principe de responsabilité unique". Donc, cela devrait être pris en charge en premier. Deuxièmement, vous avez peut-être identifié une dépendance ... vous touchez des données "persistantes".
Si vous effectuez les opérations d'accès aux données dans la méthode en question, vous devez procéder à une refactorisation dans une architecture à plusieurs niveaux ou à plusieurs niveaux. Mais nous pouvons supposer que lorsque vous dites "Les chaînes sont ensuite insérées dans une base de données", vous voulez en fait dire que vous appelez une couche de logique métier ou quelque chose du genre. Ya, on va supposer ça.
Lorsque votre objet est instancié, vous comprenez maintenant que votre objet a des dépendances. C'est à ce moment que vous devez décider si vous allez effectuer une injection de dépendance sur l'objet ou sur la méthode. Cela signifie que votre constructeur ou la méthode en question a besoin d'un nouveau paramètre:
public <Constructor/MethodName> (IBusinessDataEtc otherLayerOrTierObject, string[] stuffToInsert)
Maintenant que vous pouvez accepter une interface de votre objet métier/données, vous pouvez la simuler pendant les tests unitaires sans craindre de tests d'intégration "accidentels".
Donc, dans votre code live, vous transmettez un objet REAL IBusinessDataEtc
. Mais dans vos tests unitaires, vous transmettez un objet MOCK IBusinessDataEtc
. Dans cette maquette, vous pouvez inclure des propriétés non liées à l'interface telles que int XMethodWasCalledCount
ou quelque chose dont le ou les états sont mis à jour lors de l'appel des méthodes d'interface.
Ainsi, votre test unitaire examinera votre (vos) méthode (s) en question, exécutera sa logique et appellera une ou deux, ou un ensemble sélectionné de méthodes dans votre objet IBusinessDataEtc
. Lorsque vous faites vos assertions à la fin de votre test unitaire, vous avez plusieurs choses à tester maintenant.
IBusinessDataEtc
.Pour plus d'informations sur les idées d'injection de dépendance au niveau de la construction ... en ce qui concerne les tests unitaires ... examinez les modèles de conception de Builder. Il ajoute une interface et une classe de plus pour chaque interface/classe actuelle, mais elles sont très petites et fournissent d'énormes fonctionnalités pour un meilleur test unitaire.
cela aura un effet sur un objet .... demande le résultat de l'effet. S'il n'a pas d'effet visible, cela ne vaut pas la peine de faire des tests unitaires!
Vraisemblablement, la méthode fait quelque chose et ne retourne pas simplement?
En supposant que ce soit le cas, alors:
Si vous nous faites savoir ce que fait la méthode, je pourrais être plus précis.
Essaye ça:
[TestMethod]
public void TestSomething()
{
try
{
YourMethodCall();
Assert.IsTrue(true);
}
catch {
Assert.IsTrue(false);
}
}
Utilisez Rhino Mocks pour définir quels appels, actions et exceptions peuvent être attendus. En supposant que vous puissiez vous moquer ou tronquer des parties de votre méthode. Difficile de savoir sans connaître certains détails sur la méthode, ni même sur le contexte.
Cela dépend de ce que ça fait. S'il y a des paramètres, transmettez des répliques que vous pourrez demander plus tard si elles ont été appelées avec le bon jeu de paramètres.
Vous pouvez même l'essayer de cette façon:
[TestMethod]
public void ReadFiles()
{
try
{
Read();
return; // indicates success
}
catch (Exception ex)
{
Assert.Fail(ex.Message);
}
}
Quelle que soit l’instance que vous utilisez pour appeler la méthode void, vous pouvez simplement utiliser, Verfiy
Par exemple:
Dans mon cas, c'est _Log
est l'instance et LogMessage
est la méthode à tester:
try
{
this._log.Verify(x => x.LogMessage(Logger.WillisLogLevel.Info, Logger.WillisLogger.Usage, "Created the Student with name as"), "Failure");
}
Catch
{
Assert.IsFalse(ex is Moq.MockException);
}
Est-ce que Verify
lève une exception en raison de l'échec de la méthode dont le test aurait échoué?