【问题标题】:How to test spring 5 controllers with Junit5如何使用 Junit5 测试 Spring 5 控制器
【发布时间】:2018-08-12 06:55:51
【问题描述】:

我正在尝试使用 JUnit 5 测试我的 Spring 5 Web 控制器。 测试控制器的两种方法(如 spring 文档中提到的)总是给我空指针。

这是我的测试课

import com.lacunasaurus.gamesexplorer.test.configuration.TestBackEndConfiguration;
import com.lacunasaurus.gamesexplorer.test.configuration.TestWebConfig;
import org.junit.Before;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;

@RunWith(JUnitPlatform.class)
@ExtendWith(SpringExtension.class)
@WebAppConfiguration()
@ContextConfiguration(classes = {TestWebConfig.class, TestBackEndConfiguration.class})
public class TestAuthenticationCreateAccountController {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        // this.mockMvc = MockMvcBuilders.standaloneSetup(new AuthenticationCreateAccountController()).build();

        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @Test
    public void getAccount() throws Exception {
        // Here i've got an null pointer
        mockMvc.perform(get("/"));
    }

}

这是我用于测试的 Web 配置

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@ComponentScan(basePackages = {"com.lacunasaurus.gamesexplorer.web"})
public class TestWebConfig implements WebMvcConfigurer, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

}

现在是我的控制器

  @Controller
  @RequestMapping("/create-account")
  public class AuthenticationCreateAccountController {

   @Autowired
   UserAccountValidator accountValidator;

   @Autowired
   AuthenticationService authenticationService;

   @GetMapping
   public String navigate() {
       return "authentication/create-account";
   }

   @ModelAttribute("userAccount")
   public UserAccount setDefaultAccount() {

       return new UserAccount();
   }

   @InitBinder
   protected void initBinder(WebDataBinder binder) {

       binder.addValidators(accountValidator);
   }

   @PostMapping
   public String createAccount(@Validated UserAccount userAccount, BindingResult bindingResult) {

       if (bindingResult.hasErrors()) {
           return "authentication/create-account";
       }

       authenticationService.createUserAccount(userAccount);

       return "authentication/create-account";
   }
  }

编辑:IDE 给出的堆栈跟踪

  java.lang.NullPointerException at com.lacunasaurus.gamesexplorer.test.controller.TestAuthenticationCreateAccountController.getAccount(TestAuthenticationCreateAccountController.java:41)

Results :

Tests in error: 
    TestAuthenticationCreateAccountController.getAccount:41 NullPointer

我已经用 junit 5 和 spring 测试了我的后端,一切正常。

感谢那些帮助我了解如何测试控制器的人:)

【问题讨论】:

  • 您在询问异常,但不要发布异常的堆栈跟踪。阅读它是解决问题的第一步。请注意,您正在混合使用 JUnit 4 和 JUnit 5 类和注释,这没有任何意义。阅读 JUnit 5 的文档。
  • 好的,现在我们知道 null 是 mockMvc。为什么它是空的?它应该在 setup() 方法中初始化。因此,如果它为空,则意味着 setup() 没有被调用。为什么不叫呢?因为它没有使用任何可以调用它的 JUnit 5 注释进行注释。 Before 是一个 JUnit 4,因此是无关紧要的注释。 RunWith也是如此。
  • 是的,对不起,我添加了堆栈跟踪,但这个不是太明确如果我在这里寻求帮助,那是因为我在文档中没有看到一些东西,并且邀请阅读解决这个问题对我没有太大帮助,特别是如果你的经验让你立即理解它:(无论如何,你强调使用 Junit 4(我在我的 pom.xml 中绝对没有提到)我可以看到问题来自注释@Before。谢谢你:)
  • 非常明确。它说某事是空的,因此在第 41 行,在 TestAuthenticationCreateAccountController 的方法 getAccount() 内导致 NullPointerException。在这一行中唯一可以为空的是 MockMvc。其余的都是由此推导出来的。我邀请人们阅读,因为我注意到大多数新手甚至不阅读文档,甚至一次也不阅读。他们尝试随机的事情,然后寻求帮助。阅读文档需要一些时间,但可以了解全局,发现功能,并且不会犯愚蠢的错误。最后,收获是巨大的。
  • 我们是同时写的,确实问题出在之前我改成BeforeAll。很好的分析;)

标签: spring-mvc junit junit5 spring-test-mvc


【解决方案1】:

如果您使用 Junit5,请参阅此reference

@ExtendWith(MockitoExtension.class)  // For Junit5
public class TestAuthenticationCreateAccountController {

    @Mock
    private WebApplicationContext wac;

    @InjectMocks
    private AuthenticationCreateAccountController ac;

    private MockMvc mockMvc;

    @BeforeEach // For Junit5
    public void setup() {
        mockMvc = MockMvcBuilders.standaloneSetup(ac).build();

    }

    @Test
    public void getAccount() throws Exception {
        mockMvc.perform(get("/"));
    }

}

【讨论】:

  • 这不是为每个测试都增加了新的 mockMvc 吗?不是只需要创建一次吗?
【解决方案2】:
@Autowired
private MockMvc mockMvc;

这应该可以解决您的问题。

【讨论】:

  • 请让您的答案更具描述性,并且链接也很好。
【解决方案3】:

为了使用 JUnit Jupiter,您必须使用自动配置 MockMvc。这有助于解决所有相关配置。

@ExtendWith(SpringExtension.class)
@WebAppConfiguration()
@AutoConfigureMockMvc

【讨论】:

  • 如果你使用注解@WebMvcTest你会自动得到AutoConfigureMockMvc和其他一些
【解决方案4】:

控制器的新测试:

@ExtendWith(SpringExtension.class)
@WebAppConfiguration()
@ContextConfiguration(classes = {TestWebConfig.class, TestBackEndConfiguration.class})
@TestInstance(Lifecycle.PER_CLASS)
public class TestAuthenticationCreateAccountController {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @BeforeEach
    void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @Test
    void getAccount() throws Exception {
        mockMvc.perform(get("/toto")).andExpect(status().isOk());
    }

}

【讨论】:

  • 删除@RunWith(JUnitPlatform.class)。它不是必需的。这在 JUnit5 中被 @ExtendWith(SpringExtension.class) 取代,你已经拥有了。
  • @BeforeAll 方法必须有一个 void 返回类型,不能是私有的,并且默认必须是静态的:junit.org/junit5/docs/5.0.3/api/index.html?org/junit/jupiter/…
  • 这也适用于 keycloak。只是提到帮助寻求者:D
猜你喜欢
  • 2021-02-08
  • 1970-01-01
  • 2021-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-17
  • 2013-02-19
  • 1970-01-01
相关资源
最近更新 更多