1
votes

Les champs auto-câblés de l'objet simulé retournent null dans Mockito

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?


0 commentaires

4 Réponses :


1
votes

Essayez de déclarer l'objet studentInstitutionMapper comme ceci dans votre classe de test.

@Mock
StudentInstitutionMapper studentInstitutionMapper;


0 commentaires

1
votes

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


0 commentaires

0
votes

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>


4 commentaires

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é



1
votes

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:

  1. 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).

  2. 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.


0 commentaires