[Java] Tester des méthodes privées avec JUnit

Appeler une méthode privée comme test

Auparavant dans Ce que vous devez savoir lors de l'écriture de Java, ** les méthodes privées peuvent être appelées par réflexion, les modificateurs d'accès ne sont donc utilisés que pour les tests. J'ai écrit que vous ne devriez pas le supprimer **. Alors, voici un exemple.

S'il s'agit d'un champ au lieu d'une méthode, écrivez-le comme ceci. → Référez / définissez les variables privées en réflexion

2018/12/26 Ajouté "Appeler le constructeur privé"

Avantages et inconvénients des tests par méthode privée

Nous sommes conscients qu'il existe une opinion selon laquelle "il devrait être appelé de la méthode publique pour tester la méthode privée de manière cohérente". Cependant, selon le projet, il existe de nombreux cas qui incluent un test basé sur une méthode = méthode privée. C'est une regle. Tout le monde écrit le code de test en conséquence. Même si vous le cassez, il vous sera seulement demandé de le réécrire. En logique métier, il peut y avoir une méthode publique dans une classe. Dans un tel cas, le test est difficile. Il est plus facile d'écrire un test pour une méthode publique après avoir garanti le traitement pour chaque méthode privée avec un test. C'est pourquoi les avantages et les inconvénients des tests par méthode privée sont hors de portée.


L'exemple de code omet Javadoc et en quelque sorte. J'ai confirmé l'opération pour qu'elle fonctionne avec le copier-coller, mais ce n'est qu'un exemple de code et de référence.

Classe à tester

Sample.java


public class Sample {
	private String strValue = null;

	public Sample(String value) {
		this.strValue = (value == null) ? "" : value;
	}

	/** 5.Appeler un constructeur privé*/
	private Sample() {
	}

	/** 1.Appel d'une méthode non statique*/
	private boolean equals(String value) {
		return this.strValue.equals(value);
	}

	/** 2.Appel d'une méthode statique*/
	private static boolean isEmpty(String value) {
		return (value == null || "".equals(value)) ? true : false;
	}

	/** 3.Appel sans valeur de retour ni argument*/
	private static void dispMessage() {
		System.out.println("Hello world!");
	}

	/** 4.Vérifier les exceptions*/
	private void setValue(String value) {
		if (value == null || value.isEmpty()) {
			throw new IllegalArgumentException("argument is empty.");
		}
		this.strValue = value;
	}
}

1. Appel d'une méthode non statique

doPrivateMethod est la méthode pour appeler la méthode privée testée. Dans les types, les types des arguments de la méthode privée sont stockés dans un tableau dans l'ordre à partir du premier argument et passés. args transmet également les arguments eux-mêmes. Étant donné que le nom de la méthode à tester, le type et le nombre d'arguments sont spécifiés, le test échouera si la zone est modifiée par refactorisation ou autre. (InvocationTargetException se produit)

SampleTest.java


import static org.junit.Assert.*;

import java.lang.reflect.Method;
import org.junit.Test;

public class SampleTest {
	@Test
	public void test_equals() throws Exception {
		Sample testee = new Sample("test");
		assertTrue((boolean) this.doPrivateMethod(
				testee, "equals", new Class[]{String.class}, new Object[]{"test"}));
	}

	/**
	 *Appel de méthode non statique.
	 * 
	 * @param obj Objet testé
	 * @nom du paramètre Nom de la méthode testée
	 * @types de param Types d'argument de la méthode testée
	 * @param args Arguments de la méthode testée
	 * @return Valeur de retour de la méthode non statique
	 */
	private Object doPrivateMethod(
		Object obj, String name, Class[] types, Object[] args) throws Exception {
		//Obtenir des informations sur la méthode testée
		Method method = obj.getClass().getDeclaredMethod(name, types);
		//Supprimer les restrictions d'accès à la méthode testée
		method.setAccessible(true);
		//Appel de méthode testée
		return method.invoke(obj, args);
	}
}

2. Appelez la méthode statique

doStaticPrivateMethod est la méthode pour appeler la méthode privée sous test. Ceux qui sont vulnérables au refactoring sont les mêmes que 1. Puisqu'il s'agit d'un appel à une méthode statique, aucun objet n'est passé en argument.

SampleTest.java


import static org.junit.Assert.*;

import java.lang.reflect.Method;
import org.junit.Test;

public class SampleTest {
	@Test
	public void test_isEmpty() throws Exception {
		assertTrue((boolean) this.doStaticPrivateMethod(
				"isEmpty", new Class[]{String.class}, new Object[]{null}));
	}

	/**
	 *appel de méthode statique.
	 * 
	 * @nom du paramètre Nom de la méthode testée
	 * @types de param Types d'argument de la méthode testée
	 * @param args Arguments de la méthode testée
	 * @retourne la valeur de retour de la méthode statique
	 */
	private Object doStaticPrivateMethod(
		String name, Class[] types, Object[] args) throws Exception {
		//Obtenir des informations sur la méthode testée
		Method method = Sample.class.getDeclaredMethod(name, types);
		//Supprimer les restrictions d'accès à la méthode testée
		method.setAccessible(true);
		//Appel de méthode testée
		return method.invoke(null, args);
	}
}

assertTrue((boolean) this.doStaticPrivateMethod("isEmpty", new Class[]{String.class}, new Object[]{null}));

Si vous voulez passer null dans une méthode avec des arguments, placez-le dans un tableau. Un tableau est requis même avec un argument. Si vous le passez tel quel, il échouera.

3. Renvoyer la valeur / appeler sans argument

Si la méthode testée est nulle, il n'y a pas de valeur de retour. (Null même si vous essayez de le ramasser) Passez null lors de l'appel d'une méthode sans argument. Contrairement à 2., il n'est pas nécessaire que ce soit un tableau.

SampleTest.java


import static org.junit.Assert.*;

import java.lang.reflect.Method;
import org.junit.Test;

public class SampleTest {
	@Test
	public void test_dispMessage() throws Exception {
		this.doStaticPrivateMethod("dispMessage", null, null);
	}
}

** Ajouté à partir des commentaires **

this.doStaticPrivateMethod("dispMessage", null, null);

Il n'est pas affirmé car il s'agit d'un exemple de méthode d'appel. Même lors du test d'une méthode d'annulation, il est essentiel de vérifier le résultat du traitement. Par exemple, si vous traitez une base de données ou un fichier, confirmez le résultat. Dans le cas de la classe Sample, vérifiez si la chaîne de caractères sortie vers la console est correcte. Par exemple, comme ça.

//Redirection des résultats de sortie standard
ByteArrayOutputStream out = new ByteArrayOutputStream();
System.setOut(new PrintStream(out));
//Appel testé
this.doStaticPrivateMethod("dispMessage", null, null);
//Affirmer
assertEquals("Hello world!" + System.lineSeparator(),  out.toString());

Si vous souhaitez exécuter l'exemple, importez ByteArrayOutputStream et PrintStream.

4. Vérifiez les exceptions

Toutes les erreurs d'appel sont encapsulées et levées avec ** InvocationTargetException **. Par conséquent, la vérification des exceptions levées dans la cible de test est effectuée en extrayant le contenu encapsulé par InvocationTargetException. De plus, dans le cas d'un test qui confirme qu'une exception est levée, si l'exception n'est pas levée, ou si une exception autre que l'exception prévue se produit, le test échouera sur JUnit, donc ** échouera (explicitement ** échouera ( );**appel.

SampleTest.java


import static org.junit.Assert.*;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.junit.Test;

public class SampleTest {
	@Test
	public void test_setValue() throws Exception {
		try {
			Sample testee = new Sample("test");
			this.doPrivateMethod(
					testee, "setValue", new Class[]{String.class}, new Object[]{""});
			//Test échoué
			fail();
		} catch (InvocationTargetException e) {
			//Vérification du type d'exception
			assertTrue(e.getCause() instanceof IllegalArgumentException);
			//Vérification des messages d'exception
			assertEquals("argument is empty.", e.getCause().getMessage());
		} catch (Exception e) {
			//Test échoué
			fail(e.getMessage());
		}
	}
}

5. Appelez le constructeur privé

N'est-ce pas écrire un constructeur privé sans arguments pour l'empêcher d'être appelé de l'extérieur dans une classe statique? Telle est la méthode de test. Il est utilisé dans les projets où «100% de couverture» est une condition. Le commentaire dit «Écrivez le nom de la classe avec un nom complet», mais comme l'exemple de classe cette fois n'est pas empaqueté, seul le nom de la classe est décrit. Normalement, je pense qu'il existe un package, donc il ne fonctionnera pas à moins que ce soit un nom complet.

SampleTest.java


import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.*;

import java.lang.reflect.Constructor;
import org.junit.Test;

public class SampleTest {
	@Test
	public void test_private_constructor() {
		try {
			//Décrivez le nom de la classe comme un nom complet
			Class<?> clazz = Class.forName("Sample");
			Constructor<?>[] constructors = clazz.getDeclaredConstructors();
			constructors[1].setAccessible(true);
			Object obj = constructors[1].newInstance();
			assertNotNull(obj);
			assertThat(obj, instanceOf(clazz));
		} catch (Exception e) {
			fail(e.getMessage());
		}
	}
}

constructors[1].setAccessible(true);

Ici, décrivez l'ordre décrit dans la classe Sample avec 0 origine. Dans ce cas, le constructeur privé que vous souhaitez tester est le deuxième des constructeurs de classe Sample, il sera donc "1". Si vous n'avez qu'un constructeur à tester, "0" convient.


Bonne vie à Java! Si quelque chose arrive, je l'écrirai dans un post-scriptum ou un autre article.

Recommended Posts

[Java] Tester des méthodes privées avec JUnit
Tester les méthodes privées dans JUnit
Tester les méthodes privées dans JUnit
Comment tester l'étendue privée avec JUnit
Présentation du test Java automatisé avec JUnit 5 + Gradle
Faites un test unitaire avec Junit.
[Java] Comment tester s'il est nul dans JUnit
[CircleCI 2.0] [Java] [Maven] [JUnit] Agréger les résultats des tests JUnit avec CircleCI 2.0
Présentation des tests Java automatisés avec JUnit 5 + Apache Maven
Mélanger les cas de test avec JUnit 5 et les méthodes par défaut
Comment tester une méthode privée qui prend un tableau ou un argument de longueur variable dans JUnit
[Java] Exemple de cas de test JUnit 4
Tester l'API Web avec junit
Vérifier la méthode privée de l'interface Java9
Méthodes Java
Méthodes Java
Contrôleur de cadre de test Spring avec Junit
[Java] Je souhaite tester l'entrée standard et la sortie standard avec JUnit
Contrôler l'ordre de test dans Junit4 avec un type d'énumération
Test Java EE (CDI / intercepteur) avec Arquillian
Forcer non immuable avec le constructeur privé Effective Java
Le test JUnit 5 Gradle génère une erreur avec l'annotation Lombok
Jugement des nombres premiers Java
Méthodes de classe Java
Comment tester les interruptions pendant Thread.sleep avec JUnit
Créez et testez des applications Java + Gradle avec Wercker
Test JUnit facile de la version Elasticsearch 2018 avec Embedded-elasticsearch
Créer un environnement de test E2E avec Selenium (Java)
[Java] Hello World avec Java 14 x Spring Boot 2.3 x JUnit 5 ~
Tester le code à l'aide d'une maquette avec JUnit (centre EasyMock)
Visualisez la méthode de test en cours d'exécution dans TestNG dans l'écouteur
Comment accéder aux méthodes et champs Java Private
Installez java avec Homebrew
Lancement du test JUnit essayez quelque chose
Test d'intégration avec Gradle
Installez Java avec Ansible
Exécutez Maven sur Java 8 lors de la compilation sur Java 6 et des tests sur Java 11
[Compatible JUnit 5] Ecrire un test en utilisant JUnit 5 avec Spring boot 2.2, 2.3
Téléchargement confortable avec JAVA
Mémo simple de JUnit de java
Mémo privé payant Java
[Java] Se référer et définir des variables privées avec réflexion
Échantillon de bibliothèque de tests unitaires Java
Changer java avec direnv
[Rails] Test avec RSpec
Testez Nokogiri avec Rspec.
Test unitaire de l'API Web et test d'intégration avec SpringBoot + Junit5, 4 modèles
Téléchargement Java avec Ansible
Tester automatiquement avec la jauge
Test de charge avec JMeter
Raclons avec Java! !!
J'ai écrit un test avec Spring Boot + JUnit 5 maintenant
Construire Java avec Wercker
À propos de l'encapsulation Java Private Public
Conversion Endian avec JAVA
Points à garder à l'esprit lors du test de méthodes privées dans JUnit
J'ai étudié Randoop, un générateur de classe de test JUnit pour Java.
(Java) BDD facile avec Spectrum?
Utiliser des couches Lambda avec Java