我正在尝试运行一些单元测试,并且遇到一个我确定是由于对自动装配的误解而引起的问题。我有一个单元测试类,其中我试图在MockMvc和REST控制器上使用@Autowired
,但最终都为空。
我已经看到一些资料试图解释为什么会发生这种情况(包括More of Less post和一个有用的StackOverflow post,它给了我一些见识,但并没有完全帮助我解决问题)。 / p>
下面是我为重现此问题而制作的示例项目中的相关源代码。
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());
}
}
controllerNotNull()
测试结果
java.lang.AssertionError:期望实际值不为空
奇怪的是,当我用Java创建一个新的Gradle项目并将示例代码从这篇文章导入到其中时,controllerNotNull()
传递了。
然后testStoreSomething()
导致
java.lang.NullPointerException在 com.example.sandbox.rest.ManagerControllerTest.testStoreSomething(ManagerControllerTest.java:46)
这里有一个问题:我在误解什么?我究竟做错了什么?我可以从控制器中删除@Autowired
并仅用new ManagerController()
实例化它,但是我遇到了MockMvc问题。
ManagerController.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
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
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
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"
}
答案 0 :(得分:1)
class Chat extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
protected $casts = [
'participantIds' => 'json',
];
public function creator()
{
return $this->belongsTo(User::class, 'participantIds->creator');
}
public function recipient()
{
return $this->belongsTo(User::class, 'participantIds->recipient');
}
}
class User extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
public function createdChats()
{
return $this->hasMany(Chat::class, 'participantIds->creator');
}
public function receivedChats()
{
return $this->hasMany(Chat::class, 'participantIds->recipient');
}
}
测试类:
@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);
}
}
答案 1 :(得分:0)
将注释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())
}
或者,您也可以将TestRestTemplate与@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)一起使用