[JAVA] Tester le code à l'aide d'une maquette avec JUnit (centre EasyMock)

À propos des tests avec simulation dans JUnit. Ceci est le deuxième article à diffuser du code de test sur le lieu de travail. Ce n'est pas une histoire très récente, car elle se concentre sur EasyMock.

Environnement d'exécution

Qu'est-ce qu'une simulation

Un objet simulé est un type de stub de module inférieur qui remplace les tests logiciels, en particulier dans le développement piloté par les tests et le développement piloté par le comportement. Module à inspecter par rapport au stub Est utilisé pour vérifier qu'il utilise correctement ses sous-modules.

Source d'objet simulé: Encyclopédie gratuite "Wikipedia"

Pour l'utiliser, par exemple, supposons que vous ayez créé une classe appelée HelloJunit comme indiqué ci-dessous. Lorsque j'essaye de tester cette classe, si la classe GreetDao n'est pas créée (et que la base de données référencée par Dao n'est pas préparée), si je la teste normalement, une erreur se produira. Je pense que ça finira.

HelloJunit


public class HelloJunit {

	@Binding
	GreetDao greetDao;

	public String sayGreeting() {
		return greetDao.getGreet();
	}

À l'origine, je veux juste tester la classe HelloJunit, donc je devrais pouvoir la tester même si le code de la dépendance (GreetDao) n'est pas fait, alors préparons une classe Haribote pour qu'elle puisse fonctionner pour le moment. C'est le rôle du simulacre.

Comment faire une maquette

Il existe les méthodes suivantes.

Les bibliothèques simulées incluent «Mockito», «JMockit» et «EasyMock», «MockInterceptor».

J'ai l'impression que «Mockito» est souvent utilisé de nos jours.

Seasar2 inclut «EasyMock» et «MockInterceptor» par défaut. Je pense qu'il est difficile d'installer une nouvelle bibliothèque dans un environnement où Seasar2 est encore utilisé, donc j'aimerais continuer avec EasyMock cette fois.

Exemple de code de test à l'aide d'EasyMock

Code pour le moment

Le code produit a été légèrement modifié par rapport au contenu présenté ci-dessus.

Code produit


public class HelloJunit {

	GreetDao greetDao;
	
	public HelloJunit(GreetDao greetDao) {
		this.greetDao = greetDao;
	}

	public String sayGreeting() {
		return greetDao.getGreet();
	}
}

Voici le code de test.

Code de test


import static org.easymock.EasyMock.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;
import org.seasar.framework.unit.annotation.EasyMock;
import org.seasar.sastruts.example.dao.GreetDao;

public class HelloJunitTest {

	HelloJunit helloJunit;

	@EasyMock
	GreetDao greetDao;

	@Before
	public void setUp() {
		greetDao = createMock(GreetDao.class);
		helloJunit = new HelloJunit(greetDao);
	}

	@Test
public void dire une salutation() throws Exception {
		//installer
		expect(greetDao.getGreet()).andReturn("Hello");
		replay(greetDao);

		//exercice
		String actual = helloJunit.sayGreeting();

		//Vérifier
		String expected = "Hello";
		assertThat(actual, is(expected));
		verify(greetDao);

		//démolir (post-traitement)
		//si vous avez des problèmes ···
	}
}

Je pense que vous pouvez le comprendre d'une manière ou d'une autre, mais je vais l'expliquer.

Annotation EasyMock

J'ai GreetDao comme variable membre, mais il a une annotation EasyMock. En joignant ceci, vous pouvez utiliser EasyMock.

createMock La méthode setUp utilise la méthode createMock pour créer un greetDao. Le simulacre sera défini en passant le greetDao créé ici lors de la création de l'instance de helloJunit, qui est la méthode cible du test.

Configuration dans la méthode de test

Dans la configuration des commentaires, nous utilisons la méthode ʻexpect`. C'est la partie la plus importante qui détermine le comportement du simulacre.

Lorsque greetDao.getGreet () est appelé comme ceci, il est configuré pour retourner ~. Spécifiez comment appeler la méthode avec ʻexpected () . Puis définissez la valeur de retour avec ʻandReturn () . Dans ce cas, lorsque greetDao.getGreet () sans argument est appelé, "Hello" est renvoyé.

La méthode appelée replay est une image qui se souvient du comportement du jeu de simulation immédiatement avant. Assurez-vous de rejouer après avoir exécuté attendu ().

exercise Ici, la méthode que vous souhaitez tester est exécutée et stockée dans une variable. On dit qu'il est facile de comprendre si les variables stockées sont unifiées en «réelles» quel que soit le type.

verify Ici, nous comparons la valeur de retour et la valeur attendue de helloJunit.sayGreeting ();. De plus, verify (greetDao); vérifie que le comportement de la maquette spécifiée dans la configuration a été appelé comme prévu. Si helloJunit.sayGreeting (); n'a jamais été appelé, une erreur sera renvoyée.

tear down Il s'agit du post-traitement post-test. Par exemple, si des données de test ont été entrées dans le DB, ces données seront supprimées.

Comment utiliser EasyMock

Vérifiez l'argument

Cette fois, vérifiez si la méthode findDreet de greetDao est appelée comme prévu.

Code à tester


public String sayGreeting() {
	return greetDao.findGreet(1);
}

Dans ʻexpect () , écrivez la méthode d'appel de méthode attendue telle quelle. Dans ce cas, on suppose que 1 est inclus dans l'argument de findGreet, donc décrivez simplement findGreet (1)`.

Code de test


@Test
test d'argument public void() throws Exception {
	//installer
	expect(greetDao.findGreet(1)).andReturn("Hello");
	replay(greetDao);

	//exercice
	String actual = helloJunit.sayGreeting();

	//Vérifier
	verify(greetDao);
}

À propos, s'il est appelé par findGreet (2);, l'erreur suivante sera générée.

Référence en cas d'erreur


java.lang.AssertionError: 
  Unexpected method call findGreet(2):
    findGreet(1): expected: 1, actual: 0
	at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:32)
	at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:61)
	at com.sun.proxy.$Proxy4.findGreet(Unknown Source)
	at org.seasar.sastruts.example.service.HelloJunit.sayGreeting(HelloJunit.java:17)
	at org.seasar.sastruts.example.service.HelloJunitTest.Test d'argument(HelloJunitTest.java:52)
--Omis par la suite--

Définissez la valeur de retour et testez le traitement ultérieur

J'ai essayé d'insérer une branche par la valeur de retour de findGreet. Dans ce test, je voudrais tester le modèle dans lequel les pâtes sont retournées.

Code de production


public String sayGreeting() {
	String greet = greetDao.findGreet(1);
	if (greet.contentEquals("Hello")) {
		return "Pâtes";
	} else {
		return "ramen";
	}
}

Pour définir la valeur de retour, spécifiez la valeur de retour dans ʻandReturn`. Dans ce cas, "Hello" sera retourné.

Code de test


@Test
test d'argument public void() throws Exception {
	//installer
	expect(greetDao.findGreet(1)).andReturn("Hello");
	replay(greetDao);

	//exercice
	String actual = helloJunit.sayGreeting();

	//Vérifier
	String expected = "Pâtes";
	assertThat(actual, is(expected));
	verify(greetDao);
}

Test des méthodes de type Void

Comment tester une méthode de type void.

Code de production (GreetDao)


public interface GreetDao {
	public void insertGreet(String greet);
}

Code de production (HelloJunit)


public String addGreet() {
	greetDao.insertGreet("Good Night");
	return "OK";
}

Si vous souhaitez tester le type void, exécutez en fait la méthode void dans votre code de test. Puis appelez ʻexpectLastCall () `. Cela vous permet de vous souvenir et de vérifier la dernière exécution de la méthode.

Par ailleurs, lorsque la même méthode void est exécutée deux fois, on vérifie si elle a été exécutée le nombre de fois en ajoutant times () comme ʻexpectLastCall (). Time (2); `.

Code de test


@Test
test de la méthode public void void() throws Exception {
	//installer
	greetDao.insertGreet("Good Night");
	expectLastCall();
	replay(greetDao);

	//exercice
	String actual = helloJunit.sayGreeting();

	//Vérifier
	verify(greetDao);
}

Notes simulées faciles

EasyMock ne peut simuler que des classes d'interface. Si vous voulez vous moquer d'une classe concrète, essayez la méthode d'héritage ci-dessous.

Hériter et vérifier

Si la classe utilisée dans la méthode testée n'est pas une interface ou si elle n'est pas pratique à tester, vous pouvez en hériter pour la tester et préparer votre propre classe fictive.

Par exemple, si les beans retournés par la méthode ne remplacent pas la méthode equals et qu'il est difficile de vérifier chaque champ, héritez de equals et testez comme suit.

Code de production à tester


public TransferResultDto transfer(String payerAccountId,
										String payeeAccountId,
										String payerName,
										long transferAmount) {

		Balance payerBalance = balanceDao.findByAccountId(payerAccountId);
		if (payerBalance == null)
			throw new BusinessLogicException("Je ne peux pas trouver l'équilibre");

		if (transferAmount > payerBalance.amount)
			throw new BusinessLogicException("Pas assez d'équilibre");

		Balance payeeBalance = balanceDao.findByAccountId(payeeAccountId);
		if (payeeBalance == null)
			throw new BusinessLogicException("Le compte de transfert n'existe pas");

		LocalDateTime transferDate = LocalDateTime.now();

		Transfer peyerTransaction = new Transfer();
		peyerTransaction.accountId = payerAccountId;
		peyerTransaction.name = payerBalance.name;
		peyerTransaction.transferAmount = -transferAmount;
		peyerTransaction.transferDate = transferDate;

		Transfer payeeTransaction = new Transfer();
		payeeTransaction.accountId = payeeAccountId;
		payeeTransaction.name = payeeBalance.name;
		payeeTransaction.transferAmount = transferAmount;
		payeeTransaction.transferDate = transferDate;

		transferDao.insertTransfer(peyerTransaction);
		transferDao.insertTransfer(payeeTransaction);
		balanceDao.updateAmount(payerAccountId, -transferAmount);
		balanceDao.updateAmount(payeeAccountId, transferAmount);

		Balance updatedPayerBalance = balanceDao.findByAccountId(payerAccountId);

		return new TransferResultDto(payerAccountId, payeeAccountId,
									 payerBalance.name, payeeBalance.name,
									 transferAmount,
									 updatedPayerBalance.amount);
	}
}

Retour Bean


public class TransferResultDto {

	private final String payerAccountId;
	private final String payeeAccountId;
	private final String payerName;
	private final String payeeName;
	private final long transferAmount;
	private final long amount;

	public TransferResultDto(String payerAccountId,
							 String payeeAccountId,
							 String payerName,
							 String payeeName,
							 long transferAmount,
							 long amount) {

		this.payerAccountId = payerAccountId;
		this.payeeAccountId = payeeAccountId;
		this.payerName = payerName;
		this.payeeName = payeeName;
		this.transferAmount = transferAmount;
		this.amount = amount;
	}

	public String getPayerAccountId() {
		return payerAccountId;
	}

	public String getPayeeAccountId() {
		return payeeAccountId;
	}

	public String getPayerName() {
		return payerName;
	}

	public String getPayeeName() {
		return payeeName;
	}

	public long getTransferAmount() {
		return transferAmount;
	}

	public long getAmount() {
		return amount;
	}

}

Pour tester cela, créez une classe TransferResultDtoMock qui hérite de TransferResultDto dans la classe de test. Dans cette classe TransferResultDtoMock, préparez une méthode equals qui compare tous les champs, et lors du test, vous pouvez comparer avec est comme ʻassertThat (réel, est (attendu));`.

python


public class TransferServiceTest {
	@Test
public void essayer l'héritage simulé() {
		// setup
		TransferResultDtoMock expected = 
						new TransferResultDtoMock("1", "2",
												ACCOUNT1_BEFORE_BALANCE.name, ACCOUNT2_BALANCE.name,
												2000, ACCOUNT1_AFTER_BALANCE.amount);

		TransferResultDto transferResultDto = transferService.transfer("1", "2", "Taro Tanaka", 1000);
		TransferResultDtoMock actual = new TransferResultDtoMock(transferResultDto);
		assertThat(actual, is(expected));
	}


	@EqualsAndHashCode(callSuper = true)
	private class TransferResultDtoMock extends TransferResultDto {
		public TransferResultDtoMock(String payerAccountId,
									 String payeeAccountId,
									 String payerName,
									 String payeeName,
									 long transferAmount,
									 long amount) {

			super(payerAccountId, payeeAccountId, payerName,
					payeeName, transferAmount, amount);

		}

		public TransferResultDtoMock(TransferResultDto transferResultDto) {
			super(transferResultDto.getPayerAccountId(),
				  transferResultDto.getPayeeAccountId(),
				  transferResultDto.getPayerName(),
				  transferResultDto.getPayeeName(),
				  transferResultDto.getTransferAmount(),
				  transferResultDto.getAmount());
		}
				

	}
}

Points à prendre en compte lors de l'écriture du code de test

Faire de la partie liée à la logique métier une implémentation indépendante

Tout d'abord, je pense que vous avez changé la logique en DI GreetDao dans HelloJunit de l'annotation au type de constructeur. La raison en est que lorsqu'il est exprimé par des annotations, HelloJunit devient une forme ** dépendante </ font> ** en fonction du framework (Seasar2).

Code produit


public class HelloJunit {

	GreetDao greetDao;
	
	public HelloJunit(GreetDao greetDao) {
		this.greetDao = greetDao;
	}

	public String sayGreeting() {
		return greetDao.getGreet();
	}
}

Par exemple, si vous décidez de passer à Spring à l'avenir, vous utiliserez @ Autowired to DI avec des annotations. Par conséquent, il est facile de modifier le framework par DI dans le constructeur.

Aussi, du point de vue du code de test, s'il est réalisé par annotation, il faut spécifier @ RunWith (Seasar2.class) lors de la déclaration de la classe, mais si c'est DI dans le constructeur, nouveau Si vous le faites, vous pouvez facilement tester.

Article de référence

Architecture propre pratique

Écrire du code qui est facile à écrire des tests

Un code facile à écrire est important pour les tests.

Par exemple, le code suivant crée une branche conditionnelle en fonction du résultat de la méthode statique TimeJunit.getTime (), mais il est difficile de remplacer la méthode statique par une simulation.

Utilisation de la méthode statique


public class ExampleJunit {
	public String sayGreeting() {
		int term = TimeJunit.getTime();
		switch(term) {
		case 1:
			return "Pâtes";
		case 2:
			return "ramen";
		default:
			return "riz";
		}
	}
}

Le reste du code est difficile à tester même s'il est hérité et utilise des méthodes de superclasse. Par exemple, ce type de logique qui est souvent utilisé dans les systèmes plus anciens.

Super classe


public abstract class AbstractJunit {
	public String sayGreeting() {
		return this.getMessage();
	}
	abstract protected String getMessage();
}

Classe d'héritage


public class ExampleJunit extends AbstractJunit {
	protected String getMessage() {
		int term = 1;
		switch(term) {
		case 1:
			return "Pâtes";
		case 2:
			return "ramen";
		default:
			return "riz";
		}
	}
}

Dans cet exemple, il peut être testé, mais dans le cas où l'héritage est réellement utilisé dans les affaires, la super classe est une classe divine (une classe pratique qui met de la logique dans n'importe quoi), et c'est une source de spaghetti. C'est gênant car je dois faire une réflexion dans le test.

Demandez-vous si vous devez vraiment utiliser l'héritage et l'implémenter.

Article de référence

Le code qui est facile à écrire des tests est un bon code

Autres références

http://s2container.seasar.org/2.4/ja/easyMock.html

Recommended Posts

Tester le code à l'aide d'une maquette avec JUnit (centre EasyMock)
Faites un test unitaire avec Junit.
[Compatible JUnit 5] Ecrire un test en utilisant JUnit 5 avec Spring boot 2.2, 2.3
Tester l'API Web avec junit
J'ai une exception InvalidUseOfMatchersException lors de l'utilisation de l'un dans le simulateur de JUnit
Contrôleur de cadre de test Spring avec Junit
Contrôler l'ordre de test dans Junit4 avec un type d'énumération
Essayez d'utiliser Spring Boot avec VS Code
Comment tester l'étendue privée avec JUnit
Le test JUnit 5 Gradle génère une erreur avec l'annotation Lombok
Présentation du test Java automatisé avec JUnit 5 + Gradle
[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
Testez le contrôleur avec Mock MVC dans Spring Boot
Comment exécuter une méthode et simuler avec JUnit
Procédure de construction d'environnement pour l'utilisation de PowerMock avec JUnit
Présentation des tests Java automatisés avec JUnit 5 + Apache Maven
Comment tester les interruptions pendant Thread.sleep avec JUnit
Test JUnit facile de la version Elasticsearch 2018 avec Embedded-elasticsearch
Comment écrire du code de test avec la certification de base
Rendre System.out Mock avec Spock Test Framework
Conseils pour tester avec mock pour les classes utilisant @value
Mélanger les cas de test avec JUnit 5 et les méthodes par défaut
Prise en main des programmes Java à l'aide de Visual Studio Code
Comment faire un test unitaire avec JVM sur une source à l'aide de RxAndroid
Implémenter le test double dans JUnit sans utiliser de bibliothèques externes
Test unitaire de l'API Web et test d'intégration avec SpringBoot + Junit5, 4 modèles
J'ai écrit un test avec Spring Boot + JUnit 5 maintenant