Pourquoi Mockito ne se moque-t-il pas des méthodes statiques?

267

J'ai lu ici quelques discussions sur les méthodes statiques et je pense comprendre les problèmes que peuvent entraîner une mauvaise utilisation / utilisation excessive des méthodes statiques. Mais je n'ai pas vraiment compris pourquoi il est difficile de se moquer des méthodes statiques.

Je sais que d'autres cadres de simulation, comme PowerMock, peuvent le faire, mais pourquoi pas Mockito?

J'ai lu cet article , mais l'auteur semble être religieusement contre le mot static, c'est peut-être ma mauvaise compréhension.

Une explication / lien facile serait formidable.

Abidi
la source
12
Juste une remarque: PowerMock n'est pas une bibliothèque d'objets factices en soi, il ajoute simplement ces fonctionnalités (statiques et ctors moqueurs) au-dessus d'autres bibliothèques. Nous utilisons PowerMock + Mockito au travail, ils flottent bien les uns avec les autres.
Matthias

Réponses:

238

Je pense que la raison peut être que les bibliothèques d'objets factices créent généralement des simulations en créant dynamiquement des classes au moment de l'exécution (en utilisant cglib ). Cela signifie qu'ils implémentent une interface au moment de l'exécution (c'est ce que fait EasyMock si je ne me trompe pas), ou ils héritent de la classe pour se moquer (c'est ce que fait Mockito si je ne me trompe pas). Les deux approches ne fonctionnent pas pour les membres statiques, car vous ne pouvez pas les remplacer à l'aide de l'héritage.

La seule façon de se moquer de la statique est de modifier le code d'octet d'une classe au moment de l'exécution, ce qui, je suppose, est un peu plus complexe que l'héritage.

C'est ma conjecture, pour ce que ça vaut ...

Matthias
la source
7
Il en va de même pour les constructeurs moqueurs, soit dit en passant. Ceux-ci ne peuvent pas non plus être modifiés par héritage.
Matthias
11
Il peut également être utile d'ajouter que certains partisans du TDD / TBD perçoivent le manque de méthode statique et de moquerie du constructeur comme une bonne chose. Ils soutiennent que lorsque vous devez vous moquer de méthodes statiques ou de constructeurs, alors c'est un indicateur de mauvaise conception de classe. Par exemple, lorsque vous suivez une approche IoC puriste dans l'assemblage de vos modules de code, vous n'aurez même jamais besoin de se moquer de la statique ou des ctors en premier lieu (à moins qu'ils ne fassent partie d'un composant de la boîte noire, bien sûr). Voir aussi giorgiosironi.blogspot.com/2009/11/…
Matthias
201
Je pense que les outils moqueurs devraient vous donner ce dont vous avez besoin sans supposer qu'ils savent ce qui est mieux pour vous. Par exemple, si j'utilisais une bibliothèque tierce qui utilisait un appel de méthode statique que je devais simuler, ce serait bien de pouvoir le faire. L'idée qu'un cadre factice ne vous fournira pas certaines capacités car il est considéré comme une mauvaise conception est fondamentalement erronée.
Lo-Tan
11
@ Lo-Tan - c'est comme dire qu'une langue devrait être capable de tout, sans supposer qu'elle sache mieux que vous. C'est juste de la vanité de votre part, car ils se révèlent imposants. Le problème ici est que la bataille "anti / pro statique" n'est pas claire, tout comme les cadres. Je suis d'accord que nous devrions avoir les deux. Mais là où les faits sont clairs, je préfère un cadre qui impose ces faits. C'est une façon d'apprendre - des outils pour vous garder sur la bonne voie. Vous n'avez donc pas à le faire vous-même. Mais maintenant, chaque tête de nouilles peut imposer son soi-disant "bon design". "Fondamentalement imparfait" ...
Nevermind
13
@nevvermind Eh? Un langage de haut niveau est destiné à vous aider et à avoir les abstractions nécessaires afin que vous puissiez vous concentrer sur les pièces importantes. Une bibliothèque de tests est un outil - un outil que j'utilise pour produire du code de meilleure qualité et, espérons-le, mieux conçu. Quel est l'intérêt d'une bibliothèque testing / mock lorsqu'elle a des limitations qui peuvent signifier que je ne peux pas l'utiliser lorsque je dois intégrer du code mal conçu de quelqu'un d'autre? Cela ne semble pas bien pensé, alors que de bonnes langues l'ont été .
Lo-Tan
28

Si vous devez vous moquer d'une méthode statique, c'est un indicateur fort d'une mauvaise conception. Habituellement, vous vous moquez de la dépendance de votre classe en cours de test. Si votre classe en cours de test fait référence à une méthode statique - comme java.util.Math # sin par exemple - cela signifie que la classe en cours de test a besoin exactement de cette implémentation (de la précision par rapport à la vitesse par exemple). Si vous voulez faire abstraction d'une implémentation concrète de sinus, vous avez probablement besoin d'une interface (vous voyez où cela va)?

Jan
la source
3
Eh bien, j'ai utilisé des méthodes statiques pour fournir des abstractions de haut niveau, comme une "façade de persistance statique". Une telle façade éloigne le code client des complexités et des détails de bas niveau d'une API ORM, fournissant une API plus cohérente et facile à utiliser, tout en offrant beaucoup de flexibilité.
Rogério
Et pourquoi faut-il s'en moquer? Si vous dépendez de la méthode statique, votre "unité" ou "module" n'est pas la classe seule mais inclut également la "façade de persistance statique".
Jan
89
Certes, mais parfois vous n'avez pas le choix si, par exemple, vous devez vous moquer d'une méthode statique qui se trouve dans une classe tierce.
Stijn Geukens
7
Certes, mais parfois nous avons affaire à des singletons.
Manu Manjunath
La seule pensée qui ne peut pas être résolue par l'abstraction est trop de niveaux d'abstraction ... L'ajout de couches d'abstraction ajoute de la complexité et est souvent inutile. Je pense aux exemples que j'ai vus de frameworks qui essaient de se moquer de System.currentTimeMillis () en enveloppant ce simple appel dans une seule classe. Nous nous retrouvons avec une classe singleton par méthode au lieu d'avoir simplement des méthodes - uniquement pour faciliter les tests. Et puis, lorsque vous introduisez un dep tiers qui appelle la méthode statique directement au lieu de passer par votre wrapper singleton, les tests échouent quand même ...
Père Jeremy Krieg
5

Je pense sérieusement que c'est une odeur de code si vous devez également vous moquer des méthodes statiques.

  • Méthodes statiques pour accéder aux fonctionnalités communes? -> Utilisez une instance singleton et injectez-la
  • Code tiers? -> Enveloppez-le dans votre propre interface / délégué (et si nécessaire, faites-en aussi un singleton)

La seule fois où cela me semble exagéré, ce sont des bibliothèques comme Guava, mais vous ne devriez pas avoir besoin de se moquer de ce type de toute façon car cela fait partie de la logique ... (des trucs comme Iterables.transform (..)) De
cette façon, votre propre code reste propre, vous pouvez simuler toutes vos dépendances de manière propre et vous disposez d'une couche anti-corruption contre les dépendances externes. J'ai vu PowerMock dans la pratique et toutes les classes pour lesquelles nous en avions besoin étaient mal conçues. De plus, l'intégration de PowerMock a parfois causé de graves problèmes
(par exemple https://code.google.com/p/powermock/issues/detail?id=355 )

PS: Il en va de même pour les méthodes privées. Je ne pense pas que les tests devraient connaître les détails des méthodes privées. Si une classe est si complexe qu'elle tente de se moquer des méthodes privées, c'est probablement un signe pour diviser cette classe ...

pete83
la source
2
Singleton vous fera rencontrer toutes sortes de problèmes, en particulier lorsque vous réalisez que vous avez réellement besoin de plus d'une instance et que vous devez maintenant refactoriser l'ensemble de votre système pour y arriver.
Ricardo Freitas
Je n'ai pas dit que je recommande le modèle Singleton à tout le monde. Ce que je voulais dire, c'est que si je dois choisir entre une classe d'utilité statique et un Singleton offrant les mêmes fonctionnalités, je choisirais le Singleton. Et si une classe est un Singleton ou non doit être contrôlée par le cadre DI de toute façon, dans ma classe I @Inject SomeDependencyet dans ma configuration, je définis bind(SomeDependency.class).in(Singleton.class). Donc si demain ce n'est plus un Singleton, je change la config et c'est tout.
pete83
@ pete83 je vous entends frère. Cependant, j'ai un problème avec les bibliothèques de test ou les frameworks qui nécessitent que les développeurs modifient leur conception pour respecter la conception / les limites du framework de test. C'est l'OMI qui met le chariot devant le cheval, ou la queue qui remue le chien.
Matt Campbell
1
Cet argument n'a guère de sens pour moi. Les modèles singleton sont tombés en disgrâce depuis des années maintenant, pour trop de raisons de les énumérer ici. Qu'est-ce qui constitue un code "propre"? Si j'ai une méthode d'instance de classe qui appelle une méthode d'assistance statique qui renvoie une opération d'E / S, pourquoi ne voudrais-je pas que cela soit simulé dans un test? Et comment est ce mauvais design? Toutes ces méthodes statiques moqueuses qui se tordent à la main ne s'additionnent pas. Se moquer d'une méthode est l'opposé de la tester. Si c'est trop difficile à mettre en œuvre, dites-le et
finissez-en
Oh mec, je n'ai jamais parlé de ce modèle Singleton de la vieille école où tout le monde appelle Foo.getInstance()partout. Je viens d'écrire singleton dans la réponse pour contrer l'argument "mais une méthode statique ne nécessite pas la création de nombreux objets wrapper". Sur le plan conceptuel également, il y a peu de différence entre une méthode statique et une méthode d'instance sur un singleton, juste que vous ne pouvez pas vous moquer de ce collaborateur singleton. Mais singleton ou non n'est absolument pas le point que j'essayais de faire valoir, le point est d'injecter et de se moquer des collaborateurs et de ne pas appeler de méthodes statiques si cela rend les tests difficiles.
pete83
4

Mockito renvoie des objets mais statique signifie "niveau classe, pas niveau objet". Ainsi, mockito donnera une exception de pointeur nul pour statique.

salsinga
la source
0

Dans certains cas, les méthodes statiques peuvent être difficiles à tester, surtout si elles doivent être simulées, c'est pourquoi la plupart des frameworks de simulation ne les prennent pas en charge. J'ai trouvé ce billet de blog très utile pour déterminer comment se moquer des méthodes et des classes statiques.

Tyler Treat
la source
1
La simulation de méthodes statiques est encore plus facile que la simulation de méthodes d'instances (car il n'y a pas d'instance), lors de l'utilisation d'une API de simulation appropriée.
Rogério
C'est comme répondre à la question avec la question elle-même, c'est pourquoi il est difficile de le faire, auquel ce n'est pas une réponse.
Matthias
40
Je l'ai rejeté parce que le billet de blog recommande une solution de contournement coûteuse (refactorisation du code de production), plutôt que de résoudre réellement le problème d'isoler une classe des méthodes statiques qu'elle utilise. L'OMI, un outil moqueur qui fait vraiment le travail, ne discriminerait aucune méthode d'aucune sorte; un développeur devrait être libre de décider si l'utilisation de méthodes statiques est bonne ou mauvaise dans une situation donnée, plutôt que d'être contraint de suivre un chemin.
Rogério