J'ai une classe StudentService
. Et j'ai écrit une classe pour les méthodes de test unitaire de la classe StudentService
. Mon code est le suivant: -
@Test public void testStudentService_getPresentStudentCount1() { StudentService service=new StudentService(); StudentParams studentParam=mock(StudentParams.class); Integer institutionTestId=3539; when(studentParam.getInstitutionTestId()).thenReturn(institutionTestId); int i=service.getPresentStudentCount(studentParam); assertEquals(0,i); }
Et dans ma classe de test unitaire, j'ai écrit la méthode suivante.
@Component @EnableAutoConfiguration public class StudentService { @Autowired StudentInstitutionMapper studentInstitutionMapper; public Integer getPresentStudentCount(StudentParams studentParam) { // TODO Auto-generated method stub StudentInstitutionExample example = new StudentInstitutionExample(); StudentInstitutionExample.Criteria criteria = example.createCriteria(); criteria.andActiveYnEqualTo("Y"); criteria.andDeleteYnEqualTo("N"); criteria.andIsPresentEqualTo("Y"); criteria.andInstitutionTestIdEqualTo(studentParam.getInstitutionTestId()); List<StudentInstitution> studentInstitutionList = studentInstitutionMapper.selectByExample(example);//line 8 in method return studentInstitutionList.size(); } }
quand j'exécute le classe de test, j'obtiens une erreur. En effet, dans la classe StudentService
, dans la méthode getPresentStudentCount ()
, à la ligne 8, le champ studentInstitutionMapper
est null
. Cela ne se produit que pour les objets moqués. Comment obtenir des champs d'objet simulé automatiquement?
4 Réponses :
Essayez de déclarer l'objet studentInstitutionMapper
comme ceci dans votre classe de test.
@Mock StudentInstitutionMapper studentInstitutionMapper;
Vous pouvez injecter une classe auto-câblée avec l'annotation @Mock. Dans de nombreux cas, vous devriez créer votre instance de classe de test avec l'annotation @InjectMocks, grâce à cette annotation vos simulacres peuvent injecter directement.
@RunWith(PowerMockRunner.class) @PrepareForTest({StudentService.class}) public class StudentServiceTest { @InjectMocks StudentService service; @Mock StudentInstitutionMapper studentInstitutionMapper; @Test public void testStudentService_getPresentStudentCount1() { MockitoAnnotations.initMocks(this); StudentParams studentParam=mock(StudentParams.class); Integer institutionTestId=3539; when(studentParam.getInstitutionTestId()).thenReturn(institutionTestId); int i=service.getPresentStudentCount(studentParam); assertEquals(0,i); }
Ce serait une aide pour une meilleure explication: Différence entre @Mock et @InjectMocks
Vous devez utiliser l'annotation @InjectMocks dans votre test unitaire:
@ExtendWith(MockitoExtension.class) class StudentServiceTest { @Mock private StudentInstitutionMapper studentInstitutionMapper; @InjectMocks private StudentService studentService; @Test public void testStudentService_getPresentStudentCount1() { StudentParams studentParam = mock(StudentParams.class); Integer institutionTestId = 3539; when(studentParam.getInstitutionTestId()).thenReturn(institutionTestId); int i = studentService.getPresentStudentCount(studentParam); assertEquals(0, i); } }
Vous devez également configurer le comportement de studentInstitutionMapper dans le test unitaire pour qu'il renvoie le résultat attendu. p>
C'est aussi une excellente solution. Mais comme j'utilise JUnit4, la réponse cochée a fonctionné pour moi. Merci.
Pour junit 4, vous pouvez remplacer @ExtendWith (MockitoExtension.class)
par @RunWith (MockitoJUnitRunner.class)
La réponse cochée utilise powerMock, cela ne semble pas utile dans votre cas . L'appel à MockitoAnnotations.initMocks (this);
n'est pas nécessaire.
Salut Fabien !! Merci de votre aide. J'ai essayé cette réponse. Comme vous l'avez dit, j'ai également remplacé l'annotation. J'ai un problème. Je ne parviens pas à importer @InjectMock. Y a-t-il une faute d'orthographe. Est-ce @InjectMocks?
Merci de votre aide. Cela m'a beaucoup sauvé
Il existe une solution simple qui n'implique pas d'annotations avancées de mockito:
Vous pouvez refactoriser le StudentService
comme ceci:
public class StudentService { private final StudentInstitutionMapper studentInstitutionMapper; public StudentService(StudentInstitutionMapper studentInstitutionMapper) { this.studentInstitutionMapper = studentInstitutionMapper; } }
Cette solution est ma préférée car lorsque je crée le StudentService
en test, je vois exactement quelles dépendances il nécessite, leur type. Je peux donc fournir des simulations / implémentation réelle même sans ouvrir la source de la classe StudentService
.
Autre avantage de ce type d'injection (injection de constructeur par opposition à l'injection de champ que vous utilisé dans la question) est que rien ne rompt l'encapsulation des champs.
Remarques:
Je n'ai pas mis @Autowired
sur le constructeur car dans la version récente de spring, ce n'est pas nécessaire tant qu'il y a un seul constructeur (et pour les tests unitaires, ce n'est pas du tout pertinent).
Si vous êtes préoccupé par le code standard du constructeur, vous pouvez utiliser Lombok et mettre une annotation pour générer le constructeur all-args pour vous. En conjonction avec la note 1, cela permet de supprimer complètement le code du constructeur
P.S. Je n'ai pas l'intention de commencer la «guerre sainte» de l'injection de champ contre l'injection de constructeur ici, j'énonce simplement cette approche parce que personne ne l'a mentionnée auparavant dans d'autres réponses et techniquement, cela résout le problème soulevé dans la question. N'hésitez pas à consulter Google sur ce sujet.