web-dev-qa-db-fra.com

Test unitaire et vars privés

J'écris un test unitaire BDD pour un méthode publique. La méthode modifie une propriété privée (private var) je voudrais donc écrire un expect () et m'assurer qu'il est correctement défini. Puisqu'il est privé, je ne peux pas déterminer comment y accéder à partir de la cible de test unitaire.

Pour Objective-C, je voudrais simplement ajouter un en-tête d'extension. Existe-t-il des astuces similaires dans Swift? Remarque: la propriété possède également un didSet () avec du code.

36
Robert Gummesson

(Notez que Swift 2 ajoute le @testable attribut qui peut rendre les méthodes et propriétés internes disponibles pour les tests. Voir les commentaires de @ JeremyP ci-dessous pour plus d'informations.)

Non. À Swift, privé est privé. Le compilateur peut utiliser ce fait pour optimiser, donc selon la façon dont vous utilisez cette propriété, il est légal pour le compilateur de la supprimer, de la mettre en ligne ou de faire toute autre chose qui donnerait le comportement correct en fonction du code réellement dans cette fichier. (Que l'optimiseur soit réellement intelligent aujourd'hui ou non, il est permis de l'être.)

Maintenant, bien sûr, si vous déclarez que votre classe est @objc, alors vous pouvez casser ces optimisations, et vous pouvez aller fouiller avec ObjC pour le lire. Et il existe des solutions de contournement bizarres qui peuvent vous permettre d'utiliser Swift pour appeler arbitraire @objc méthodes exposées (comme un délai nul NSTimer). Mais ne fais pas ça.

Il s'agit d'un problème de test classique, et la réponse de test classique est de ne pas tester de cette façon. Ne testez pas l'état interne. S'il est littéralement impossible de dire de l'extérieur que quelque chose s'est passé, alors il n'y a rien à tester. Reconcevoir l'objet afin qu'il soit testable sur son interface publique. Et cela signifie généralement la composition et les moqueries.

La mise en cache est probablement la version la plus courante de ce problème. Il est très difficile de tester que quelque chose est réellement mis en cache, car la seule différence peut être qu'il est récupéré plus rapidement. Mais c'est toujours testable. Déplacez la fonctionnalité de mise en cache vers un autre objet et laissez votre objet en cours de test accepter un objet de mise en cache personnalisé. Ensuite, vous pouvez passer une maquette qui enregistre si les bons appels de cache ont été effectués (ou des appels réseau, ou des appels de base de données, ou quel que soit l'état interne).

Fondamentalement, la réponse est: repenser pour qu'il soit plus facile à tester.

D'accord, mais vous en avez vraiment, vraiment, vraiment besoin ... comment faire? OK, c'est possible sans briser le monde.

Créez une fonction à l'intérieur du fichier à tester qui expose ce que vous voulez. Pas une méthode. Juste une fonction gratuite. Ensuite, vous pouvez mettre cette fonction d'assistance dans un #if TEST, et définissez TEST dans votre configuration de test. Idéalement, je ferais en sorte que la fonction teste réellement la chose qui vous intéresse plutôt que d'exposer la variable (et dans ce cas, vous pouvez peut-être laisser la fonction être interne ou même publique). Mais de toute façon.

47
Rob Napier