tl; dr
Chez Pivotal, nous avons écrit Cedar parce que nous utilisons et aimons Rspec sur nos projets Ruby. Cedar n'est pas censé remplacer ou concurrencer OCUnit; il est destiné à offrir la possibilité de tests de style BDD à Objective C, tout comme Rspec a lancé les tests de style BDD dans Ruby, mais n'a pas éliminé Test :: Unit. Le choix de l'un ou de l'autre dépend en grande partie des préférences de style.
Dans certains cas, nous avons conçu Cedar pour surmonter certaines lacunes dans la façon dont OCUnit fonctionne pour nous. Plus précisément, nous voulions pouvoir utiliser le débogueur dans les tests, exécuter des tests à partir de la ligne de commande et dans les builds CI, et obtenir une sortie texte utile des résultats des tests. Ces choses peuvent vous être plus ou moins utiles.
Longue réponse
Décider entre deux cadres de test comme Cedar et OCUnit (par exemple) se résume à deux choses: style préféré et facilité d'utilisation. Je vais commencer par le style, car c'est simplement une question d'opinion et de préférence; la facilité d'utilisation tend à être un ensemble de compromis.
Les considérations de style transcendent la technologie ou le langage que vous utilisez. Les tests unitaires de type xUnit existent depuis bien plus longtemps que les tests de style BDD, mais ces derniers ont rapidement gagné en popularité, en grande partie grâce à Rspec.
Le principal avantage des tests de style xUnit est leur simplicité et leur large adoption (parmi les développeurs qui écrivent des tests unitaires); presque tous les langages dans lesquels vous pourriez envisager d'écrire du code ont un framework de style xUnit disponible.
Les cadres de style BDD ont tendance à avoir deux différences principales par rapport au style xUnit: la façon dont vous structurez le test (ou les spécifications) et la syntaxe pour écrire vos assertions. Pour moi, la différence structurelle est le principal différenciateur. Les tests xUnit sont unidimensionnels, avec une méthode setUp pour tous les tests dans une classe de test donnée. Cependant, les classes que nous testons ne sont pas unidimensionnelles; nous devons souvent tester des actions dans plusieurs contextes différents, potentiellement conflictuels. Par exemple, considérons une simple classe ShoppingCart, avec une méthode addItem: (pour les besoins de cette réponse, j'utiliserai la syntaxe Objective C). Le comportement de cette méthode peut différer lorsque le panier est vide par rapport au moment où le panier contient d'autres articles; il peut différer si l'utilisateur a entré un code de réduction; il peut différer si l'élément spécifié peut " t être expédié par la méthode d'expédition sélectionnée; etc. Comme ces conditions possibles se croisent, vous vous retrouvez avec un nombre géométriquement croissant de contextes possibles; dans les tests de style xUnit, cela conduit souvent à de nombreuses méthodes avec des noms comme testAddItemWhenCartIsEmptyAndNoDiscountCodeAndShippingMethodApplies. La structure des cadres de style BDD vous permet d'organiser ces conditions individuellement, ce qui, selon moi, facilite la couverture de tous les cas, ainsi que la recherche, la modification ou l'ajout de conditions individuelles. Par exemple, en utilisant la syntaxe Cedar, la méthode ci-dessus ressemblerait à ceci: dans les tests de style xUnit, cela conduit souvent à de nombreuses méthodes avec des noms comme testAddItemWhenCartIsEmptyAndNoDiscountCodeAndShippingMethodApplies. La structure des cadres de style BDD vous permet d'organiser ces conditions individuellement, ce qui, selon moi, facilite la couverture de tous les cas, ainsi que la recherche, la modification ou l'ajout de conditions individuelles. Par exemple, en utilisant la syntaxe Cedar, la méthode ci-dessus ressemblerait à ceci: dans les tests de style xUnit, cela conduit souvent à de nombreuses méthodes avec des noms comme testAddItemWhenCartIsEmptyAndNoDiscountCodeAndShippingMethodApplies. La structure des cadres de style BDD vous permet d'organiser ces conditions individuellement, ce qui, selon moi, facilite la couverture de tous les cas, ainsi que la recherche, la modification ou l'ajout de conditions individuelles. Par exemple, en utilisant la syntaxe Cedar, la méthode ci-dessus ressemblerait à ceci:
describe(@"ShoppingCart", ^{
describe(@"addItem:", ^{
describe(@"when the cart is empty", ^{
describe(@"with no discount code", ^{
describe(@"when the shipping method applies to the item", ^{
it(@"should add the item to the cart", ^{
...
});
it(@"should add the full price of the item to the overall price", ^{
...
});
});
describe(@"when the shipping method does not apply to the item", ^{
...
});
});
describe(@"with a discount code", ^{
...
});
});
describe(@"when the cart contains other items, ^{
...
});
});
});
Dans certains cas, vous trouverez des contextes contenant les mêmes ensembles d'assertions, que vous pouvez sécher en utilisant des exemples de contextes partagés.
La deuxième différence principale entre les cadres de style BDD et les cadres de style xUnit, la syntaxe d'assertion (ou "matcher"), rend simplement le style des spécifications un peu plus agréable; certaines personnes l'aiment vraiment, d'autres pas.
Cela pose la question de la facilité d'utilisation. Dans ce cas, chaque framework a ses avantages et ses inconvénients:
OCUnit existe depuis bien plus longtemps que Cedar et est directement intégré à Xcode. Cela signifie qu'il est simple de créer une nouvelle cible de test et, la plupart du temps, la mise en place et l'exécution des tests «fonctionnent tout simplement». D'un autre côté, nous avons constaté que dans certains cas, tels que l'exécution sur un appareil iOS, il était presque impossible de faire fonctionner les tests OCUnit. La configuration des spécifications Cedar nécessite plus de travail que les tests OCUnit, car vous avez vous-même la bibliothèque et le lien contre elle (jamais une tâche triviale dans Xcode). Nous travaillons à rendre la configuration plus facile et toutes les suggestions sont les bienvenues.
OCUnit exécute des tests dans le cadre de la génération. Cela signifie que vous n'avez pas besoin d'exécuter un exécutable pour exécuter vos tests; si des tests échouent, votre build échoue. Cela rend le processus d'exécution des tests plus simple, et la sortie du test va directement dans votre fenêtre de sortie de génération, ce qui la rend facile à voir. Nous avons choisi d'intégrer les spécifications Cedar dans un exécutable que vous exécutez séparément pour plusieurs raisons:
- Nous voulions pouvoir utiliser le débogueur. Vous exécutez les spécifications Cedar comme vous exécuteriez tout autre exécutable, vous pouvez donc utiliser le débogueur de la même manière.
- Nous voulions que la console se connecte facilement aux tests. Vous pouvez utiliser NSLog () dans les tests OCUnit, mais la sortie va dans la fenêtre de construction où vous devez déplier l'étape de construction pour la lire.
- Nous voulions un rapport de test facile à lire, à la fois sur la ligne de commande et dans Xcode. Les résultats OCUnit apparaissent bien dans la fenêtre de construction dans Xcode, mais la construction à partir de la ligne de commande (ou dans le cadre d'un processus CI) entraîne une sortie de test entremêlée avec beaucoup, beaucoup d'autres sorties de construction. Avec des phases de construction et d'exécution distinctes, Cedar sépare la sortie afin que la sortie de test soit facile à trouver. Le lanceur de test Cedar par défaut copie le style d'impression standard "." pour chaque spécification qui passe, «F» pour les spécifications qui échouent, etc. Cedar a également la possibilité d'utiliser des objets reporter personnalisés, de sorte que vous pouvez obtenir les résultats de sortie comme vous le souhaitez, avec un petit effort.
OCUnit est le cadre de test unitaire officiel pour Objective C et est pris en charge par Apple. Apple a essentiellement des ressources illimitées, donc si elles veulent que quelque chose soit fait, cela se fera. Et, après tout, c'est le bac à sable d'Apple dans lequel nous jouons. Le revers de cette médaille, cependant, est qu'Apple reçoit chaque jour des commandes de support et des rapports de bogues. Ils sont remarquablement bons pour les gérer tous, mais ils peuvent ne pas être en mesure de gérer les problèmes que vous signalez immédiatement ou pas du tout. Cedar est beaucoup plus récent et moins cuit qu'OCUnit, mais si vous avez des questions, des problèmes ou des suggestions, envoyez un message à la liste de diffusion Cedar (cedar-discuss@googlegroups.com) et nous ferons ce que nous pouvons pour vous aider. N'hésitez pas non plus à bifurquer le code de Github (github.com/pivotal/cedar) et à ajouter tout ce qui vous semble manquant.
L'exécution de tests OCUnit sur des appareils iOS peut être difficile. Honnêtement, je n'ai pas essayé cela depuis un certain temps, donc cela a peut-être été plus facile, mais la dernière fois que j'ai essayé, je n'ai tout simplement pas pu obtenir de tests OCUnit pour que la fonctionnalité UIKit fonctionne. Lorsque nous avons écrit Cedar, nous nous sommes assurés de pouvoir tester le code dépendant d'UIKit à la fois sur le simulateur et sur les appareils.
Enfin, nous avons écrit Cedar pour les tests unitaires, ce qui signifie qu'il n'est pas vraiment comparable à des projets comme UISpec. Cela fait un bon moment que j'ai essayé d'utiliser UISpec, mais j'ai compris qu'il se concentrait principalement sur la conduite par programme de l'interface utilisateur sur un appareil iOS. Nous avons spécifiquement décidé de ne pas essayer que Cedar prenne en charge ce type de spécifications, car Apple était (à l'époque) sur le point d'annoncer UIAutomation.