2 votes

L'annotation de méthode de spring mocking lance NullPointerException dans les cas de test

J'ai une annotation de validation personnalisée dans un projet Spring.

L'objectif de l'annotation est d'accepter les noms des paramètres qui sont censés être des valeurs id et dept.

Le but de l'aspect est de récupérer les noms de paramètres de l'annotation, de trouver la position du paramètre correspondant dans la signature de la méthode, d'obtenir les valeurs des positions identifiées et d'effectuer la validation avec les valeurs.

Voici les cours que j'ai écrits jusqu'à présent.

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ValidateDeptAnnotation {
    String id();
    String dept();
}

L'aspect qui effectue la validation lorsqu'une méthode est annotée.

@Aspect
@Component
public class ValidateDeptAspect {

    @Before("@annotation(<package>.ValidateDeptAnnotation)")
    public void runValidation(JoinPoint joinPoint) throws MemberIdentityException {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        ValidateDeptAnnotation annotation = method.getAnnotation(ValidateDeptAnnotation.class); 
        String id = null;
        String dept = null;
        for (int index = 0; index < signature.getParameterNames().length; index++) {
            String paramterName = signature.getParameterNames()[index];
            if (annotation.dept().equals(paramterName)) {
                dept = joinPoint.getArgs()[index].toString();
            } else if (annotation.id().equals(paramterName)) {
                id = joinPoint.getArgs()[index].toString();
            }
        }
        //....further processing...throw appropriate error msgs logic
    }

}

La classe de test

@RunWith(PowerMockRunner.class)
@PrepareOnlyThisForTest({Method.class})
public class ValidateDeptAspectTestMockedMethod {
    @InjectMocks
    private ValidateDeptAspect validationAspect;
    @Mock
    private JoinPoint joinPoint;

    @Mock
    private MethodSignature methodSignature;
    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }
    @Test
    public void testIdParamNotFound() {
        String[] methodSignatureParams = {"id","dept"};
        String[] argumentValues= {"789","dept-789-pacman"};

        Mockito.doReturn(methodSignature).when(joinPoint).getSignature();
        Mockito.doReturn(methodSignatureParams).when(methodSignature).getParameterNames();
        Mockito.doReturn(argumentValues).when(joinPoint).getArgs();
        ValidateDeptAnnotation annotation = Mockito.mock(ValidateDeptAnnotation.class);

        Method mockedMethod = PowerMockito.mock(Method.class);
        Mockito.doReturn(mockedMethod).when(methodSignature).getMethod();
        PowerMockito.doReturn(annotation).when(mockedMethod).getAnnotation(Mockito.any());
        //        PowerMockito.when(mockedMethod.getAnnotation(ValidateDept.class)).thenReturn(annotation); --didnot work, same error.
        Mockito.doReturn("iiiiid").when(annotation).id();
        Mockito.doReturn("dept").when(annotation).dept();

        validationAspect.runValidation(joinPoint);

        ///...further assertion logic to check for error message as iiiid != id
        //but never gets here.

   }
}

Lorsque j'exécute le scénario de test, il échoue avec NullPointerException à la ligne dans l'Aspect.

if (annotation.dept().equals(paramterName))  

Lorsque je débogue le scénario de test, l'annotation est obtenue correctement ici.

PowerMockito.doReturn(annotation).when(mockedMethod).getAnnotation(Mockito.any());

Cependant, l'appel à la méthode d'aspect génère un NPE. Toute aide est la bienvenue.

Merci d'avance.

1voto

Vijay Kalidindi Points 80

Pour ceux qui se retrouvent dans cette situation. Je n'ai pas encore trouvé comment résoudre le problème ou quel est le problème.

J'ai opté pour l'approche suivante : créer des méthodes fictives dans le scénario de test qui reflètent votre scénario de test et procéder aux scénarios de test normalement.

Code :

@RunWith(MockitoJUnitRunner.class)
public class ValidateDeptAspectTestMockedMethod {

    @InjectMocks
    private ValidateDeptAspect validationAspect;
    @Mock
    private JoinPoint joinPoint;

    @Mock
    private MethodSignature methodSignature;
    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }
    @Test
    public void testIdParamNotFound() {
        String[] methodSignatureParams = {"id","dept"};
        String[] argumentValues= {"789","dept-789-pacman"};

        Mockito.doReturn(methodSignature).when(joinPoint).getSignature();
        Mockito.doReturn(methodSignatureParams).when(methodSignature).getParameterNames();
        Mockito.doReturn(argumentValues).when(joinPoint).getArgs();

        Method method = myMethod("noMemberId");
        Mockito.doReturn(method).when(methodSignature).getMethod();

        validationAspect.runValidation(joinPoint);

        ///...further assertion logic to check for error message
        // The test is successful.
    }

    @ValidateDeptAnnotation(memberId = "",accessToken = "accessToken")
    private void noMemberId(String memberId, String accessToken) {}
}

0voto

Luc S. Points 654

Je suis actuellement dans une situation similaire mais je crois que le test de l'aspect est d'inclure le déclencheur de point de jonction. J'ai créé une fausse classe et je valide le comportement et le déclencheur de mon aspect. Cela rend le test beaucoup plus complet et valide également l'exactitude du point de jonction.

2 remarques sur le code ci-dessous :

  • J'utilise Spring, principalement parce que j'utilise toujours AOP avec Spring, mais conceptuellement cela pourrait fonctionner sans.
  • Proxy et AOP ne font pas bon ménage. La prudence est donc de mise avec l'espionnage et le mock.

T

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestConfig.class})
public class ValidateDeptAspectTestMockedMethod {

    @Autowired
    private SampleService sampleService;

    @Test
    public void testAllWell() {
        sampleService.noMemberId("123", "heroquest");
        // validation etc..
    }

    @Test
    public void testNopeDidntWork() {
        sampleService.noMemberId("123", "heroquest");
        // validation etc..
    }

    @EnableAspectJAutoProxy
    public static class TestConfig {

        @Bean
        public SampleService sampleService() {
            // WARNING - SPYING CAN NO LONGER TRIGGER ASPECT BECAUSE OF PROXY
            return new SampleService();
        }

    }

    public static class SampleService {

        @ValidateDeptAspect(id="789", dept ="dept-789-pacman") 
        public void noMemberId(String id, String dept) {
            // maybe some state you want to save to check after

        }
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X