[JAVA] Je veux écrire un test unitaire!

L'habitude d'écrire des tests unitaires

Surtout jusqu'à ce que vous puissiez créer et déployer l'application que vous développez et la voir à l'écran Cela prend 5 minutes et 10 minutes, mais dans ce cas, L'opération d'écrire la logique métier mais de la tester uniquement manuellement ne produit que de la douleur.

Dans cet article, Je sais qu'il vaut mieux l'écrire d'une manière ou d'une autre, mais honnêtement, je ne sais pas comment l'écrire avec la structure actuelle, Aux développeurs qui n'auraient pas envie de dépenser autant parce qu'ils ne savent pas de quoi ils sont heureux. Il fournit une méthode de conception pour écrire efficacement des tests unitaires avec xUnit.

En gros, il s'agit de java8 et JUnit4, mais le contenu n'est pas si limité en langage.

Prémisse majeure

Le test unitaire n'est pas un "test"

Les tests unitaires ne peuvent vérifier le comportement d'une fonction que par assertion.

Les tests manuels sont le moyen le plus efficace de détecter les défauts inconnus.

Les tests unitaires sont un outil d'aide au développement

Plutôt que d'améliorer la qualité, il est possible d'effectuer automatiquement les vérifications minimales afin que la vitesse de développement soit ralentie en étant ennuyeuse et que des retouches inattendues ne se produisent pas plus tard.

Évidemment, cet article est destiné aux personnes qui n'écrivent généralement pas de tests unitaires, donc juste au cas où.

Avantages de la rédaction d'un test unitaire

Le retravail est sur le devant

Comme dans l'exemple ci-dessus, il faut 5 minutes et 10 minutes pour pouvoir vérifier depuis l'écran, mais dans ce cas, Pour une logique métier complexe, il est plus efficace de générer une retouche avant de tester à partir de l'écran.

Les tests unitaires vous permettent généralement de voir les résultats plus rapidement que vous ne pouvez démarrer le serveur et le voir à l'écran. Aussi, comme le nom du test unitaire (unitaire), il est possible de vérifier au niveau de chaque fonction avant de connecter toutes les fonctions. Une autre raison est que le délai de mise en œuvre => de validation est plus petit.

En d'autres termes

Dans ces deux points, les retouches peuvent être détectées rapidement.

Les spécifications peuvent être décrites

Souvent mentionné comme un mérite du TDD, Les tests unitaires sont généralement écrits sous la forme d'assertions (comme la valeur de retour de la fonction XX doit être ZZ lorsque l'argument est YY). Cela permet aux développeurs suivants de ** écrire avec succès ** en lisant le code de test pour le test unitaire. Vous saurez le plus clairement que "cette fonction a de telles spécifications".

(Bien écrire est très important, C'est assez déroutant s'il n'a pas à la fois l'exhaustivité en tant que scénario de test et la lisibilité en tant que code.)

Non seulement les développeurs peuvent le comprendre, bien sûr. Si un développeur qui ne comprend pas la fonction fait une mauvaise modification, Tant que le CI tourne correctement Il est possible de détecter automatiquement que "le comportement a changé par rapport aux spécifications souhaitées" dans le test unitaire.

Un bon entretien des tests unitaires est Cela conduit à la possibilité de détecter automatiquement des défauts qui, autrement, ne seraient remarqués qu'au stade du test d'intégration.

Facilité de compréhension de l'utilisabilité des fonctions publiques

Ceci est également souvent cité comme un avantage du TDD, Le code de test sera le premier utilisateur de la fonction publique (prévue) que vous implémentez.

Surtout en Java, je pense personnellement que la conception de la signature détermine divers facteurs tels que l'évolutivité et le risque de modification. En appelant réellement la fonction à partir du code de test, il est souvent possible de retrouver l'utilisabilité de la fonction.

Si vous remarquez cela après avoir été appelé à partir de différentes classes, le risque et la portée de la modification seront énormes.

Peut tester interdit

Lors d'un test combiné, il y a une interdiction. Le fait est que les valeurs qui ne peuvent pas être saisies à partir de l'écran sont exclues du scénario de test en tant que règle interdite. Dans ce cas, le jugement selon lequel "vous ne pouvez pas entrer depuis l'écran" n'est pas fiable.

Par exemple, même si le frontal est validé, Cela n'a aucun sens si la valeur est réécrite par un utilisateur malveillant au moment de l'envoi.

Étant donné que le test unitaire n'a pas la restriction qu'il peut être saisi ou ne peut pas être Même pour les modèles d'entrée qui sont des cas interdits Il est possible de tester.

Ce serait bien de pouvoir écrire un test qui s'attend à ce qu'une exception soit levée.

Pourquoi ne puis-je pas écrire un test unitaire?

Eh bien, j'ai donné divers mérites, Si vous lisez cet article, comme mentionné précédemment, sans écrire de test unitaire, Je suis aux prises avec un travail de développement d'applications à grande échelle chaque jour.

Pourquoi je ne l'ai pas écrit, pourquoi mes aînés autour de moi Considérant que personne n'a écrit à perte de vue Peut-être en fait En premier lieu, cela peut être dû au fait que le code produit a une structure qui rend difficile l'écriture de tests unitaires.

Dans cette section, "Pourquoi ne puis-je pas écrire un test unitaire?" Je vais vous expliquer les fonctionnalités lorsque la structure est difficile à écrire un test unitaire.

Ici, comme une fonction commune sur votre lieu de travail Imaginez quelque chose nommé «complet» qui est responsable du processus d'achèvement de quelque chose.

public Map<String,Object> complete(BigBean bigBean) {
// ...
SomeKindOfConfig config = dao.loadConfig(someId);
this.computeSomeValue(bigBean);
if(config.getSomeAttribute() == 1) {
// ...
} else {
// ...
}
// ...
for(DtlBean dtl : bigBean.getManyDtls()) {
// ...
}
if(bigBean.getOtherAttribute() == 0) {
// ...
}
dao.updateSomething(bigBean);
return res;
}

La fonction est trop longue et la valeur de retour attendue n'est pas claire

Lors de l'exécution de quelque chose Je pense qu'il est courant de créer un design fonctionnel qui est pratique car divers processus peuvent être effectués en appuyant simplement sur un bouton. C'est un modèle dans lequel ils sont tous écrits dans une fonction.

Un tel traitement est souvent effectué Je dois essayer ce paramètre plus tard et brancher le processus! Parce que cela peut être une situation difficile comme L'accès à la base de données est également chaotique, comme entrer ad hoc sur le chemin.

Pour une logique métier qui ressemble à cet héritage Les tests unitaires sont très difficiles à écrire.

Il y a divers problèmes autres que le fait d'être trop long, Si vous le faites souvent avec une fonction, Il est difficile d'écrire sous la forme d'une assertion que "la sortie devrait être comme ça pour l'entrée" C'est une caractéristique importante. Comme il y a de nombreuses branches et que la combinaison explose, la quantité de description de classe de test devient énorme, ce qui est inefficace.

J'ai changé l'argument

private void computeSomeValue(BigBean bigBean) {
//...
bigBean.setSomeValue(someValue);
//...
}

S'il existe une méthode void dont le contenu de l'argument peut changer D'autre part Puisqu'il devient impossible d'écrire clairement l'affirmation que "quand il y a une telle entrée, cela devrait être comme ça comme une sortie" Je pense que vous devriez l'éviter autant que possible.

Si vous souhaitez vraiment répondre aux exigences ci-dessus, La signature elle-même,

private SomeValueClass computeSomeValue(BigBean bigBean) {
//...
return someValue;
}

Il vaut mieux écrire comme ça.

Dépend de quelque chose d'autre que des arguments

Selon le fonctionnement du test unitaire, Si quelque chose comme un environnement de test n'est pas prêt pour une actualisation rapide Par exemple, il n'est pas facile de tester un DB dépendant de la valeur avec un test unitaire.

Dans le cas du processus ci-dessus Je vais obtenir une valeur semblable à un paramètre de la base de données en cours de route, Cela entraînera une assertion de méthode complète pour le même argument Selon l'état de la base de données, il réussira ou échouera.

En premier lieu, pour les développeurs ultérieurs Je pense que c'est une mauvaise implémentation d'aller du coup voir les réglages en cours de route.

Dans ce cas, simulez Dao avec Mockito côté test unitaire, etc. Il existe un moyen de gérer cela.

Conception de test unitaire facile à écrire

Division appropriée

Une fonction de complexité qui n'explose pas la combinaison de branches, S'il est mis en œuvre séparément dans un état faiblement couplé, Vous pouvez implémenter et exécuter des tests unitaires très efficacement.

Par exemple, disons que vous avez la possibilité de payer un loyer pendant plusieurs mois par jour. Nous mettrons en place une fonction de prévisualisation qui vous permettra de voir combien de loyer vous paierez dans le mois.

Comme argument

Il y aura.

En sortie,

--La date est affichée en AAAA / MM / JJ.

Supposons que ce soit une spécification.

Si vous essayez de tout connecter à partir de l'écran et de le frapper normalement,

--Quand il y en a plusieurs? Quand c'est singulier? --Quand c'est un mois à deux chiffres? Quand c'est un mois à un chiffre? ――Quand avant? La prochaine fois?

Il y a diverses choses et cela a tendance à augmenter en combinaison.

Cependant, comme il s'agit d'un ensemble de spécifications simples si elles sont divisées, Si vous écrivez un test unitaire sur "une fonction qui ne formate qu'un seul mois" Vous pouvez voir que vous pouvez faire confiance au format de la lune dans une certaine mesure, Si vous écrivez une unité de "fonction pour trier plusieurs mois", Il n'est pas nécessaire de tester sans relâche l'ordre des «fonctions qui sortent en reliant le début à la fin de plusieurs mois avec des tirets». Et, "Comparez la date, l'année et le mois, et si l'année et le mois sont antérieurs," avant ", S'il y a un test unitaire de "une fonction qui renvoie" suivant "si l'année et le mois sont postérieurs", Le test unitaire de la fonction d'affichage de l'aperçu final est Il vous suffit de tester la valeur représentative pour savoir s'ils sont correctement combinés.

Si vous implémentez chacun comme un ensemble avec un test unitaire qui est petit et garantit une valeur de retour pour l'argument, "Au moins ça marche correctement jusqu'à présent, n'est-ce pas?" Il y a aussi l'avantage de pouvoir développer l'esprit tranquille car on peut procéder en vérifiant.

Transparence des références

An expression is said to be referentially transparent if it can be replaced with its corresponding value without changing the program's behavior. As a result, evaluating a referentially transparent function gives the same value for same arguments.

https://en.wikipedia.org/wiki/Referential_transparency

"Une fonction est la référence transparente signifie que le comportement du programme ne change pas même si la fonction est remplacée par sa valeur de retour. Par conséquent, lorsque vous exécutez une fonction transparente à la référence, ** renvoie toujours la même valeur de retour pour le même argument **. "

Écrivez la logique métier qui vous tient à cœur dans une référence transparente. Personnellement, je suis heureux de séparer clairement la classe qui a l'état de la fonction qui renvoie la sortie pour l'entrée.

Non seulement il est facile d'écrire un test unitaire, mais il est également facile d'écrire si la même valeur de retour est toujours renvoyée pour le même argument. Il rend le test unitaire efficace en ce qu'il "reproduit le mouvement garanti par le test unitaire dans n'importe quel état".

Alors, combien dois-je écrire?

En gros, il semble bon d'en préparer au moins un pour chaque méthode publique

Je pense qu'il y a des règles ici.

Écriture efficace

Testez la conception rapidement et correctement

Lors de la rédaction d'un test unitaire comme description de spécifications, Cependant, il peut être contre-productif de transmettre la valeur qui est arrivée au nuage noir et de faire une affirmation.

Tout d'abord, le minimum

Il est efficace de saisir certaines des méthodes généralement connues sous le nom de méthodes de test.

Au contraire, dans le cas d'une fonction qui ne peut pas être mise dans le cas avec ces deux Puisque le test lui-même dans le test unitaire peut ne pas convenir, Vous voudrez peut-être envisager la validation avec d'autres méthodes.

Vous permet de combiner et d'exécuter des cas conçus

@Runwith(Theories.class)

Ou

@Runwith(Parameterized.class)

Vous pouvez utiliser pour tester en passant plusieurs "ensembles d'arguments et de valeurs attendues" à un code de test. En bref, vous pouvez rendre les entrées et les sorties complètes et faciles à écrire.

Préparez une suite de tests et faites-la pivoter plusieurs fois

Si le nombre de classes de test augmente Créez une classe Suite qui la regroupe pour chaque package, Soyons capables de tourner toutes les classes de test sous lui à la fois à tout moment.

S'il y a un changement Si vous retournez tout immédiatement, Tant qu'il est affirmé dans le test unitaire, il fonctionnera avec les spécifications jusqu'à présent, Vous pouvez voir immédiatement.

Pour les projets qui utilisent des outils CI tels que Jenkins et travis Je pense que c'est une bonne idée de revenir en arrière lorsque des modifications sont apportées à la base de code.

Résumé

Pourquoi écrire un test unitaire?

Pourquoi ne puis-je toujours pas écrire un test unitaire?

Comment rédiger facilement le test unitaire?

C'était tout.

Recommended Posts

Je veux écrire un test unitaire!
Je veux écrire un joli build.gradle
Je veux écrire une simple répétition d'une chaîne de caractères
Comment écrire un test unitaire pour Spring Boot 2
[SpringBoot] Comment écrire un test de contrôleur
[Ruby] Je veux faire un saut de méthode!
Je souhaite concevoir une structure pour la gestion des exceptions
Je veux écrire rapidement de java vers sqlite
Je veux appeler une méthode d'une autre classe
Je veux convertir des caractères ...
Je veux utiliser une petite icône dans Rails
Introduction à Micronaut 2 ~ Test unitaire ~
Je souhaite surveiller un fichier spécifique avec WatchService
Je souhaite définir une fonction dans la console Rails
Pour écrire un programme orienté utilisateur (1)
Je veux cliquer sur une broche GoogleMap dans RSpec
Je souhaite créer une annotation générique pour un type
Je souhaite ajouter une fonction de suppression à la fonction de commentaire
Comment écrire dynamiquement des cas de test itératifs à l'aide de test / unit (Test :: Unit)
[Java] Je souhaite convertir un tableau d'octets en un nombre hexadécimal
Je souhaite générer des informations de manière aléatoire lors de l'écriture du code de test
Je veux trouver un chemin relatif dans une situation où Path est utilisé
Je souhaite implémenter une fonction d'édition des informations produit ~ part1 ~
Je souhaite créer un modèle spécifique d'ActiveRecord ReadOnly
Je veux faire une liste avec kotlin et java!
Je veux appeler une méthode et compter le nombre
Je veux créer une fonction avec kotlin et java!
Je souhaite créer un formulaire pour sélectionner la catégorie [Rails]
Même en Java, je veux afficher true avec un == 1 && a == 2 && a == 3
Je veux donner un nom de classe à l'attribut select
Je veux créer un fichier Parquet même en Ruby
Je souhaite utiliser FireBase pour afficher une chronologie comme Twitter
[Java] Je souhaite tester l'entrée standard et la sortie standard avec JUnit
Comment faire un test unitaire de Spring AOP
Je veux écrire une boucle qui fait référence à un index avec l'API Stream de Java 8
Je souhaite rechercher de manière récursive des fichiers dans un répertoire spécifique
Je veux pouvoir penser et écrire moi-même des expressions régulières. ..
Je souhaite ajouter une fonction de navigation avec ruby on rails
Je souhaite utiliser le balayage arrière sur un écran qui utilise XLPagerTabStrip
Je veux juste écrire Java en utilisant Eclipse sur mon Mac
Seulement ce dont je veux me souvenir, 4 modèles pour écrire des tests unitaires
J'ai essayé d'écrire du code comme une déclaration de type en Ruby
Exemple de code pour le test unitaire d'un contrôleur Spring Boot avec MockMvc
J'ai testé comment utiliser le test / l'unité de Ruby et le code de Janken.
Je veux extraire entre des chaînes de caractères avec une expression régulière
Une interface fluide? -Je veux vous donner l'occasion d'écrire du bon code. 3 [Exemple de refactoring C #]
Je souhaite éliminer les messages d'erreur en double
Introduire RSpec et écrire le code de test unitaire
Je veux créer une application ios.android