[JAVA] Résolvez les pointeurs nuls pour diverses sessions lors des tests MVC de Spring Boot.

Dans l'application Spring Boot, nous développons Quand je testais mvc avec MockMvc Puisqu'une exception est levée avec un pointeur nul lors de l'obtention de la variable de session, la solution dans ce cas est résumée.

Je pense qu'il peut y avoir des malentendus et d'autres méthodes. Nous le corrigerons dès que nous trouverons une nouvelle méthode ou une meilleure méthode. Nous vous serions reconnaissants de bien vouloir signaler toute erreur dans la description ou la reconnaissance. Merci pour votre précieux temps à ce moment-là.

N'écris pas

· Configuration du test (ensemble de gradle et mvn) -En premier lieu, c'est une autre question de savoir si la description utilisant la session et comment la faire sont correctes (je pense qu'il y a une autre solution), donc je ne toucherai pas à cette partie. Étudiera. -La méthode de description autour de l'authentification n'est pas écrite.

.with(user(mockuser))Authentifiez-vous et exécutez à.



### environnement
spring boot 1.3.x
java 1.8


## Contexte

 Lors des tests MVC, la méthode du contrôleur
 Après exécution, il devient un pointeur nul lors du référencement d'une instance de type session.
 J'étais en difficulté parce que je ne pouvais pas tester.
 Quand vous y pensez, vous ne vous connectez pas et ne l'exploitez pas.
 Aucune instance de session n'est censée se connecter et fonctionner
 De toute évidence, je ne savais pas quoi faire.
 À la suite de diverses enquêtes, j'ai réussi à poursuivre, alors je l'ai rédigé sous forme de mémo
 Il a fallu beaucoup de temps pour le résoudre, alors j'espère que cela sera utile pour ceux qui ont des problèmes avec la même pièce.


## À quoi le test MVC fait-il référence dans cet article?
 Tout d'abord, je vais vous montrer ce qu'est le test MVC dans ce cas.
 Spécifiez la méthode HTTP en spécifiant l'URL comme indiqué ci-dessous
 La valeur de retour et d'autres éléments qui testent divers états.

```java
        @Test
        public void getTest() throws Exception {
    
            ResultActions resultActions =
                    mockMvc.perform(MockMvcRequestBuilders.get("/data/showList")                    
                            .with(user(mockAdminUser)))
                            .andExpect(MockMvcResultMatchers.status().is2xxSuccessful());
        }

Trois problèmes principaux

Le problème était principalement avec 3 pointeurs nuls.

-① Il devient nul car la session utilisée dans request.getSession.getAttribute n'est pas définie. --② L'instance est nulle dans la partie où @Autowired HttpServletRequest est instancié. --③ La variable définie dans le bean session est nulle lorsqu'elle est utilisée dans @Autowired.

La solution

À propos de ①

Ce problème se produit lors de l'accès à l'URL que vous testez Un pointeur nul se produisait lorsque la description suivante a été faite dans la logique.


    @Autowired
    HttpServletRequest request;
    
    public void f() {
        //la demande est nulle.Pointeur nul Exception lors de getSession
        Object o = request.getSession.getAttribute("abc");
    }

Ce problème peut être résolu en définissant comme suit lors du test Vous pouvez maintenant injecter HttpServletRequest.


    HttpServletRequest request;
    
    request = new MockHttpServletRequest();
    RequestContextHolder.setRequestAttributes(new ServletWebRequest(request));

En décrivant ce qui précède au moment de l'installation, comme @Before au moment de l'exécution du test, l'instance peut être utilisée dans l'appel dans la méthode à tester. Les membres offshore affectés au même projet l'ont utilisé dans d'autres tests (pas des tests Mvc), donc je l'ai utilisé comme référence.

Je ne suis pas bon en anglais, mais parce que c'est comme suit Sémantiquement "lier les attributs de requête à la menace actuelle" Est-ce que ça veut dire que

    /**
    	 * Bind the given RequestAttributes to the current thread,
    	 * <i>not</i> exposing it as inheritable for child threads.
    	 * @param attributes the RequestAttributes to expose
    	 * @see #setRequestAttributes(RequestAttributes, boolean)
    	 */
    	public static void setRequestAttributes(RequestAttributes attributes) {
    		setRequestAttributes(attributes, false);
    	}

À propos de ②

L'endroit où le pointeur nul se produit est le suivant.

    @Autowired
    HttpServletRequest request;
    
    public void f() {
        MyClass o = (MyClass)request.getSession.getAttribute("abc");
        //Puisque la valeur n'est pas définie par setAttribute
        o.getName();//null pointer exception
    }

Lorsqu'il y a une telle description, la variable définie dans request est quelque part avant d'entrer cette méthode On suppose qu'il s'agit de setAttribute, mais comme le test MVC spécifie une partie spécifique, il sera naturellement nul s'il n'est pas défini.

Cela crée une instance de MockHttpSession et Définir et résoudre lorsque mvc.perform.

Tout d'abord, créez une instance de MockHttpSessin en écrivant comme suit. Méthodisez afin de pouvoir spécifier plusieurs variables de session.


    public static MockHttpSession getMockHttpSession(Map<String, Object> sessions) {
            MockHttpSession mockHttpSession = new MockHttpSession();
            for (Map.Entry<String, Object> session: sessions.entrySet()) {
                mockHttpSession.setAttribute(session.getKey(), session.getValue());
            }
            return mockHttpSession;
        }

Puisque l'argument est une carte, utilisez-le comme suit. Transmettez MockHttpSession avec mockMvc.perform.


    Map<String,Object> sessionMap =  new LinkedHashMap<String, Object>(){{
    	            put("id", 123);
    	            put("userName", "taro");
    	        }};
    MockHttpSession mockSession = 
            getMockHttpSession(sessionMap);
    
    
    ResultActions resultActions =
                    mockMvc.perform(MockMvcRequestBuilders.get("/data/showList")                    
                            .session(mockSession)
                            .with(user(mockAdminUser)))
                            .andExpect(MockMvcResultMatchers.status().is2xxSuccessful());

Désormais, la variable de session a une valeur dans la méthode utilisée lors du test.

À propos de ③

Définissez la variable spécifiée comme RequestScope comme indiqué ci-dessous Il est devenu un pointeur nul dans la méthode qui a injecté cette variable.


    @Data//en utilisant lombok
    class MySession {
        MyClass me;
    }
   @Bean
   @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
   public MySession mySession(){
       MySession m = new MySession();
       return m;
   }
    

    @Autowired
    Mysession mySession
    
    
    public void calcService(){
        //null pointer exception
        mySession.getMe()
    }

C'est aussi naturel pour la même raison que ②, mais je cherchais une méthode car je ne pouvais pas définir la valeur avec la méthode ②. Je l'ai résolu en me référant à cet article. https://stackoverflow.com/questions/2411343/request-scoped-beans-in-spring-testing

Faites le cours suivant Décrivez pour enregistrer WebApplicationContext.SCOPE_SESSION.


    public class WebContextTestExecutionListener extends AbstractTestExecutionListener {
    
        @Override
        public void prepareTestInstance(TestContext testContext) {
            if (testContext.getApplicationContext() instanceof GenericApplicationContext) {
                GenericApplicationContext context = (GenericApplicationContext) testContext.getApplicationContext();
                ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
                beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST,
                        new RequestScope());
                beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION,
                        new SimpleThreadScope());
            }
        }
    }

Annotez ceci à la classe de test.

    
    @ActiveProfiles("test")
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = TestConfig.class)
    @WebIntegrationTest(randomPort = true)
    @TestExecutionListeners({WebContextTestExecutionListener.class,
            DependencyInjectionTestExecutionListener.class,
            DirtiesContextTestExecutionListener.class})
    @Transactional
    public class MvcTestSample {

Si vous définissez cette variable au moment du test avec ce Dans la méthode après avoir accédé à mockMvc.perform Il y a une valeur quand il y a une description qui fait référence à cette variable.

		@Autowired
		Mysession mySession
        
    
        @Before
        public void setUp() throws Exception {
            MyClass me = new MyClass();
            mySession.setMe(me);   
        }

Avec ce qui précède, j'ai pu résoudre ce problème pour le moment et continuer à écrire le code de test. Je pense que les tests sont absolument nécessaires, mais il est difficile de configurer diverses choses avant de tester.

J'ai également évoqué d'autres articles pour trouver la solution à (2). Je devrais le lier par courtoisie, mais je ne sais pas où. Je le lierai dès que je le comprendrai.

c'est tout.

Recommended Posts

Résolvez les pointeurs nuls pour diverses sessions lors des tests MVC de Spring Boot.
Mémorandum WebMvcConfigurer de Spring Boot 2.0 (printemps 5)
Divers tableau de correspondance de Spring Framework et Spring Boot
Accélérez les tests des validateurs qui nécessitent DI dans Spring Boot
Une introduction pratique pour les débutants de Spring 5 et Spring Boot 2 a été publiée
Diverses applications de commutation.properties pour chaque environnement au démarrage de Spring Boot
Spring Boot pour l'apprentissage des annotations