Devriez-vous affirmer non nul avec l'instruction assert dans le code de production? [fermé]

32

J'ai vu cette question, mais j'ai quelques autres questions sur l'utilisation du assertmot clé. Je discutais avec quelques autres codeurs de l'utilisation assert. Pour ce cas d'utilisation, il y avait une méthode qui peut retourner null si certaines conditions préalables sont remplies. Le code que j'ai écrit appelle la méthode, puis affirme qu'elle ne retourne pas null et continue d'utiliser l'objet renvoyé.

Exemple:

class CustomObject {
    private Object object;

    @Nullable
    public Object getObject() {
        return (object == null) ? generateObject() : object;
    }
}

Imaginez maintenant que je l'utilise comme ceci:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    assert object != null;
    // Do stuff using object, which would throw a NPE if object is null.
}

On m'a dit que je devrais supprimer le assert, qu'ils ne devraient jamais être utilisés dans le code de production, seulement utilisés dans les tests. Est-ce vrai?

Big_Bad_E
la source
19
Par défaut, les assertions sont désactivées. Vous devez les activer explicitement lors de l'exécution via -eaou équivalent.
jarmod
Question connexe sur l'ingénierie logicielle : softwareengineering.stackexchange.com/q/137158/187318 (bien qu'elle soit assez ancienne, la réponse acceptée recommande donc toujours une bibliothèque externe, qui n'est plus nécessaire depuis l'introduction de Objects.requireNonNullJava 8).
Hulk
Ce titre de question est beaucoup trop large, mais la question énoncée dans le corps est beaucoup plus étroite et, selon les réponses, elle est entièrement gérée par des fonctions d'assistance.
smci
Pour être clair, vous demandez si le code client doit implémenter des vérifications / assertions non nulles sur les objets. (C'est différent de demander si le code de bibliothèque lui-même doit garantir que les objets ne peuvent pas être nuls, ou l'affirmer).
smci

Réponses:

19

Utilisez Objects.requireNonNull(Object)pour cela.

Vérifie que la référence d'objet spécifiée n'est pas nulle. Cette méthode est conçue principalement pour faire la validation des paramètres dans les méthodes et les constructeurs, [...]

Dans votre cas, ce serait:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    Objects.requireNonNull(object);
    // Do stuff using object, which would throw a NPE if object is null.
}

Cette fonction est faite aux fins que vous avez mentionnées, c'est-à-dire marquer explicitement ce qui ne doit pas être nul; également en production. Le grand avantage est que vous vous assurez de trouver des valeurs nulles là où elles ne devraient pas se produire en premier lieu. Vous aurez moins de problèmes de débogage causés par des valeurs nulles qui ont été passées quelque part où elles ne devraient pas être.

Un autre avantage est la flexibilité supplémentaire concernant les contrôles nuls contrairement à assert. While assertest un mot-clé pour vérifier une valeur booléenne, Objects.requireNonNull(Object)est une fonction et peut donc être intégré dans du code beaucoup plus flexible et lisible. Par exemple:

Foo foo = Objects.requireNonNull(service.fetchFoo());

// You cannot write it in on line.
Bar bar = service.fetchBar();
assert bar != null;
service.foo(Objects.requireNonNull(service.getBar()));

// You cannot write it in on line.
Bar bar = service.getBar();
assert bar != null;
service.foo(bar);

Gardez à l'esprit que Objects.requireNonNull(Object)c'est uniquement pour la vérification nulle où assertest généralisé. asserta des objectifs légèrement différents à cet égard, à savoir principalement les tests. Il doit être activé, vous pouvez donc l'activer pour les tests et le désactiver pour la production. Quoi qu'il en soit, ne l'utilisez pas pour le code de production. Cela pourrait ralentir l'application avec des validations inutiles et compliquées destinées aux tests. Utilisez-le pour séparer les chèques de test uniquement des chèques destinés également à la production.

Consultez la documentation officielle pour plus de détails assert.

akuzminykh
la source
14
Qui et quand déclaré déclaré hérité? Des liens / références? Je ne peux pas imaginer un développeur sensé définissant "hérité" l'outil permettant des validations sans empreinte qui peuvent être facilement activées dans les tests et désactivées en production.
Dmitry Pisklov
Étant donné que vous pouvez décider au moment de l'exécution si assert fonctionne, mais vous ne pouvez pas déterminer au moment de l'exécution si le NPE est lancé par requireNonNull, que voulez-vous dire par «vous avez plus de contrôle avec lui qu'avec assert»?
Pete Kirkham
@DmitryPisklov Vous avez raison, je l'ai édité. C'est un excellent outil pour les tests.
akuzminykh
2
@PeteKirkham Vous avez raison, le contrôle n'était pas le bon mot pour cela. Je parlais plutôt de la flexibilité de la syntaxe. De plus: voici des raisons pour lesquelles vous voudriez que le code lance des NPE.
akuzminykh
1
"ne l'utilisez pas pour le code de production. Cela pourrait ralentir l'application" Cela pourrait, mais cela pourrait valoir la peine. Java a des contrôles d'exécution intégrés pour vous assurer de ne jamais dépasser un tableau. Ceux-ci sont activés même sur les déploiements de production, malgré la pénalité de performances possible. Nous n'avons même pas le choix. Il n'est pas toujours faux d'avoir des contrôles d'exécution dans le code de production.
Max Barraclough
22

La chose la plus importante à retenir à propos des assertions est qu'elles peuvent être désactivées, alors ne supposez jamais qu'elles seront exécutées.

Pour des raisons de compatibilité descendante, la JVM désactive la validation d'assertion par défaut. Ils doivent être explicitement activés à l'aide de l'argument de ligne de commande -enableassertions ou de son raccourci -ea:

java -ea com.whatever.assertion.Assertion

Donc, ce n'est pas une bonne pratique de compter sur eux.

Comme les assertions ne sont pas activées par défaut, vous ne pouvez jamais supposer qu'elles seront exécutées lorsqu'elles seront utilisées dans le code. Vous devez donc toujours vérifier les valeurs nulles et les options vides, éviter d'utiliser des assertions pour vérifier les entrées dans une méthode publique et utiliser à la place une exception non vérifiée ... En général, faites toutes les vérifications comme si l'assertion n'était pas là.

jeprubio
la source
Évidemment, c'est une mauvaise pratique de s'en remettre à eux, mais est-ce une mauvaise pratique de l'utiliser en général?
Big_Bad_E
3
@Big_Bad_E Les assertions sont un outil de débogage qui existe pour faire échouer un programme aussi tôt et aussi bruyamment que possible. C'est alors le travail de la suite de tests de s'assurer qu'il n'y a aucun moyen qu'un programme puisse échouer malgré les affirmations . L'idée est qu'une fois que la suite de tests (elle a une couverture à 100%, n'est-ce pas?!?) S'exécute sans échec, il est préférable de supprimer les assertions car elles ne peuvent de toute façon pas se déclencher. Bien sûr, il y a un peu d'idéalisme dans ce raisonnement, mais c'est l'idée.
cmaster - réintègre monica
11

Ce qu'on vous dit, c'est sûrement un mensonge flagrant. Voici pourquoi.

Les assertions sont désactivées par défaut si vous exécutez simplement jvm autonome. Lorsqu'elles sont désactivées, elles n'ont aucune empreinte, elles n'affecteront donc pas votre application de production. Cependant, ils sont probablement vos meilleurs amis lors du développement et du test de votre code, et la plupart des exécuteurs de framework de test activent les assertions (JUnit le fait), donc votre code d'assertion est exécuté lorsque vous exécutez vos tests unitaires, vous aidant à détecter tout bogue potentiel plus tôt (par exemple vous pouvez ajouter des assertions pour certaines vérifications des limites de la logique métier, ce qui aidera à détecter du code qui utilise des valeurs inappropriées).

Cela dit, comme l'autre réponse le suggère, pour exactement cette raison (elles ne sont pas toujours activées), vous ne pouvez pas vous fier aux assertions pour effectuer des vérifications vitales ou (surtout!) Maintenir un état.

Pour un exemple intéressant de la façon dont vous pouvez utiliser les assertions, jetez un œil ici - à la fin du fichier, il y a une méthode singleThreadedAccess()qui est appelée à partir de l'instruction assert à la ligne 201 et qui est là pour détecter tout accès multithread potentiel dans les tests.

Dmitry Pisklov
la source
4

Les autres réponses couvrent déjà suffisamment bien cela, mais il existe d'autres options.

Par exemple, Spring a une méthode statique:

org.springframework.util.Assert.notNull(obj)

Il existe également d'autres bibliothèques avec leurs propres Assert.something()méthodes. Il est également assez simple d'écrire le vôtre.

Cependant, gardez à l'esprit les exceptions que vous jetez s'il s'agit d'un service Web. La méthode précédente mentionnée, par exemple, renvoie un IllegalArgumentExceptionqui par défaut dans Spring renvoie un 500.

Dans le cas d'un service Web, ce n'est souvent pas une erreur de serveur interne et ne doit pas être un 500, mais plutôt un 400, ce qui est une mauvaise demande.

Christopher Schneider
la source
1
Si la méthode "Assert" ne plante pas immédiatement le processus, lançant une sorte d'exception à la place, ce n'est pas une assert. Le but assertest d'obtenir un crash immédiat avec un fichier core produit afin que vous puissiez faire un post-mortem avec votre débogueur préféré juste à l'endroit où la condition a été violée.
cmaster - réintègre monica
Les assertions ne bloquent pas immédiatement un processus. Ils lancent une erreur qui peut être interceptée. Les exceptions non gérées planteront vos tests aussi bien que les erreurs. Vous pouvez discuter de la sémantique si vous le souhaitez. Si vous le souhaitez, écrivez une assertion personnalisée qui génère une erreur au lieu d'une exception. Le fait est que, dans la très grande majorité des cas, l'action appropriée consiste à lever une exception plutôt qu'une erreur.
Christopher Schneider
Ah, Java définit bien le assertlancer. Intéressant. La version C / C ++ ne fait pas une telle chose. Il déclenche immédiatement un signal indiquant que a) tue le processus et b) crée un vidage de mémoire. Il le fait pour une raison: il est très simple de déboguer un tel échec d'assertion car vous avez toujours toutes les informations sur la pile d'appels disponibles. La définition assertde lever une exception à la place, qui peut ensuite être interceptée (non) intentionnellement par programme, va à l'encontre du but, à mon humble avis.
cmaster - réintègre monica
Tout comme il y a (ou beaucoup) de choses étranges en C et C ++, il y en a en Java. L'un d'eux l'est Throwable. Ils peuvent toujours être capturés. Que ce soit une bonne chose ou non, je ne sais pas. Je suppose que cela dépend. De nombreux programmes Java sont des services Web, et il ne serait pas souhaitable qu'il se bloque pour presque n'importe quelle raison, donc à peu près tout est capturé et enregistré. L'une des grandes choses est la trace de la pile, et c'est généralement suffisant pour diagnostiquer la raison d'une exception ou d'une erreur par elle-même.
Christopher Schneider
3

Utilisez les assertions généreusement chaque fois que cela aide à détecter les erreurs de programmation, par exemple les bogues.

N'utilisez pas assert pour attraper quelque chose qui pourrait logiquement se produire, par exemple une entrée mal formatée. N'utilisez assert que lorsque l'erreur est irrécupérable.

Ne placez aucune logique de production dans le code qui s'exécute lorsque l'assertion est vérifiée. Si votre logiciel est bien écrit, cela est trivialement vrai, mais si ce n'est pas le cas, vous pourriez avoir des effets secondaires subtils et un comportement global différent avec les assertions activées et désactivées.

Si votre entreprise dispose de "code de test" et de "code de production" faisant la même chose mais en tant que bases de code différentes (ou différentes étapes d'édition), sortez de là et ne revenez jamais. Essayer de corriger ce niveau d'incompétence est probablement une perte de temps. Si votre entreprise ne place aucune déclaration d'assertion en dehors du code des tests, veuillez lui dire que les assertions sont désactivées dans la génération de production et que si ce n'est pas le cas, la correction de cette erreur est maintenant votre première priorité.

La valeur des assertions doit précisément être utilisée dans la logique métier et pas seulement dans la suite de tests. Cela facilite la production de nombreux tests de haut niveau qui n'ont pas à tester explicitement beaucoup de choses pour parcourir de gros morceaux de votre code et déclencher toutes ces assertions. Dans quelques-uns de mes projets, les tests typiques n'ont même pas vraiment affirmé quoi que ce soit, ils ont simplement ordonné qu'un calcul se produise sur la base d'entrées spécifiques, ce qui a provoqué la vérification de centaines d'assertions et la découverte de problèmes, même dans de minuscules éléments de logique au fond.

Kafein
la source
2

Vous pouvez utiliser assert à tout moment. Le débat est de savoir quand utiliser. Par exemple dans le guide :

  • N'utilisez pas d'assertions pour la vérification des arguments dans les méthodes publiques.
  • N'utilisez pas d'assertions pour effectuer le travail requis par votre application pour un fonctionnement correct.
Gatusko
la source
4
De bonnes règles. Mais la réponse serait meilleure avec quelques raisons ajoutées.
cmaster - réintègre monica