简体   繁体   English

Spring - 如何正确使用@Autowired 来防止控制器/MockMvc 为空?

[英]Spring - How to properly use @Autowired to prevent controller / MockMvc from being null?

I'm attempting to run some unit tests and encountering an issue that I am sure stems from a misunderstanding about autowiring.我正在尝试运行一些单元测试并遇到一个问题,我确信该问题源于对自动装配的误解。 I have a unit test class in which I am trying to use @Autowired on a MockMvc and a REST controller -- both of which end up being null.我有一个单元测试类,我试图在其中使用@Autowired在 MockMvc 和 REST 控制器上——这两个最终都是空的。

I have seen some sources try to explain why this can happen (including this More of Less post and a helpful StackOverflow post that has given me some insight but hasn't completely helped me solve my problem).我已经看到一些来源试图解释为什么会发生这种情况(包括这篇更多的更少的帖子和一个有用的StackOverflow 帖子,它给了我一些见解,但并没有完全帮助我解决我的问题)。

Below is relevant source code from a sample project I've made to recreate this problem.以下是我为重现此问题而制作的示例项目中的相关源代码。

ManagerControllerTest.java ManagerControllerTest.java

@RunWith(SpringRunner.class)
@WebMvcTest(ManagerController.class)
public class ManagerControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private Manager manager;

    @Autowired
    private ManagerController controller;

    @Test
    public void controllerNotNull() throws Exception {
        assertThat(controller).isNotNull();
    }

    @Test
    public void testStoreSomething() throws Exception {
        String path = "/manager/store-something/";

        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(path)
                    .characterEncoding("UTF-8")
                    .contentType(MediaType.APPLICATION_JSON);

        MvcResult result = mockMvc.perform(builder).andReturn();
        assertEquals(HttpStatus.CREATED, result.getResponse().getStatus());
    }
}

The controllerNotNull() test results in controllerNotNull()测试结果

java.lang.AssertionError: Expecting actual not to be null java.lang.AssertionError:期望实际不为空

Although, curiously, when I created a new Gradle project in Java and imported my sample code from this post into it, controllerNotNull() passes.虽然奇怪的是,当我用 Java 创建一个新的 Gradle 项目并将我的示例代码从这篇文章导入到其中时, controllerNotNull()通过了。

And testStoreSomething() results intestStoreSomething()结果

java.lang.NullPointerException at com.example.sandbox.rest.ManagerControllerTest.testStoreSomething(ManagerControllerTest.java:46) java.lang.NullPointerException 在 com.example.sandbox.rest.ManagerControllerTest.testStoreSomething(ManagerControllerTest.java:46)

And here in lies the question: What am I misunderstanding?问题就在这里:我误解了什么? What am I doing wrong?我究竟做错了什么? I can remove the @Autowired from the controller and just instantiate it with new ManagerController() but I am left with MockMvc issue.我可以从控制器中删除@Autowired并使用new ManagerController()实例化它,但我留下了 MockMvc 问题。

ManagerController.java管理器控制器.java

@Controller
@RequestMapping(value = "/manager/")
public class ManagerController {
    Manager manager = new Manager(new StringStorage());

    @PostMapping(value = "store-something")
    private ResponseEntity<?> storeSomething(String str) {
        manager.storeSomething(str);
        return new ResponseEntity<>(CREATED);
    }
}

Manager.java管理器

public class Manager {
    private final Storage storage;

    public Manager(Storage storage) {
        this.storage = storage;
    }

    public void storeSomething(String str) {
        storage.store(str);
    }
}

Storage.java存储.java

public interface Storage {
    void store(String str);
}

StringStorage.java字符串存储

public class StringStorage implements Storage {
    Map<String, String> stringMap;

    @Override
    public void store(String str) {
        stringMap.put(str, str);
    }
 }

Application.java应用程序.java

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

build.gradle Which has been edited from the original post (to use JUnit4) but the problem remains. build.gradle已从原始帖子(使用 JUnit4)中编辑,但问题仍然存在。

repositories {
    jcenter()
}

apply plugin: 'java'
apply plugin: 'eclipse'

dependencies {
 compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.0.0.RELEASE'
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-tomcat', version: '2.0.0.RELEASE'

    testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '2.0.0.RELEASE'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:4.0.0'
    testImplementation 'org.junit.jupiter:junit-jupiter-params:4.0.0'
    testCompile group: 'org.mockito', name: 'mockito-core', version: '2.17.0'
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: "4.0.0"
    testCompile group: 'org.junit.platform', name: 'junit-platform-launcher', version: "1.3.1"
}
@RestController
@RequestMapping(value = "/manager")
public class ManagerController {

    @Autowired
    Manager manager;

    @PostMapping(value = "/store-something")
    private ResponseEntity<?> storeSomething(String str) {
        manager.storeSomething(str);
        return new ResponseEntity<>(CREATED);
    }
}


@Component
public class Manager {

    @Autowired
    private Storage storage;

    public void storeSomething(String str) {
        storage.store(str);
    }
}


public interface Storage {
    void store(String str);
}

@Service
public class StringStorage implements Storage {
    Map<String, String> stringMap = new HashMap<>();

    @Override
    public void store(String str) {
        stringMap.put(str, str);
    }
 }




@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

The test class :测试类:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class ApplicationTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void shouldReturnDefaultMessage() throws Exception {
        this.mockMvc.perform(post("/manager/store-something")).andDo(print()).andExpect(status().isOk())
                .andExpect(content().string(containsString("Hello World")));
    }
}

add annotation AutoConfigureMockMvc to your test class将注释 AutoConfigureMockMvc 添加到您的测试类

@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
public class ManagerControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private Manager manager;

    @Autowired
    private ManagerController controller;

    @Test
    public void controllerNotNull() throws Exception {
        assertThat(controller).isNotNull();
    }

    @Test
    public void testStoreSomething() throws Exception {
        this.mvc.perform(get("/manager/store-something/"))
        .contentType(MediaType.APPLICATION_JSON)
        .andExpect(status().isOk())

    }

Alternatively you can also use TestRestTemplate with @SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)或者,您也可以将 TestRestTemplate 与 @SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT) 一起使用

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM