我使用JUnitTest和Mock测试Spring MVC测试getAll Employee,但当它在以下行运行时,它会向我抛出错误"InvocationTargetException":when(customerService.findAllCustomer()).thenReturn(Arrays.asList(customer,customer1))。我不知道为什么?下面是我的测试。
客户控制器测试
import com.baotrung.config.PersistenceJPAConfig;
import com.baotrung.config.WebConfig;
import com.baotrung.domain.Customer;
import com.baotrung.service.CustomerService;
import org.hamcrest.collection.IsCollectionWithSize;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {PersistenceJPAConfig.class, WebConfig.class})
@WebAppConfiguration
public class CustomerControllerTest {
private MockMvc mockMvc;
private List<Customer> customers = new ArrayList<>();
@Mock
private CustomerService customerService;
@Test
public void findAll() throws Exception {
Customer customer = new Customer();
customer.setId(1L);
customer.setFirstName("Nguyen Van");
customer.setLastName("A");
customer.setEmail("nguyenvana@gmail.com");
Customer customer1 = new Customer();
customer1.setId(2L);
customer1.setFirstName("Nguyen Van");
customer1.setLastName("A");
customer1.setEmail("nguyenvana@gmail.com");
customers.add(customer);
customers.add(customer1);
when(customerService.findAllCustomer()).thenReturn(Arrays.asList(customer,customer1));
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("customers/findAll"))
.andExpect(forwardedUrl("/WEB-INF/views/list.jsp"))
.andExpect(model().attribute("customers", IsCollectionWithSize.hasSize(2)))
.andExpect(model().attribute("customers", hasItem(
allOf(
hasProperty("id", is(1L)),
hasProperty("firstName", is("Nguyen Van")),
hasProperty("lastName", is("A"))
)
)))
.andExpect(model().attribute("customers", hasItem(
allOf(
hasProperty("id", is(1L)),
hasProperty("firstName", is("Nguyen Van")),
hasProperty("lastName", is("A"))
)
)));
verify(customerService, times(1)).findAllCustomer();
verifyNoMoreInteractions(customerService);
}
}
控制器。
@Controller
@RequestMapping("customers")
public class CustomerController {
@Autowired
private CustomerServiceImpl customerService;
@GetMapping("/findAll")
public String findAllCustomer(Model model) {
List<Customer> customers = customerService.findAllCustomer();
if (customers.isEmpty()) {
throw new ResourceNotFoundException("Can't find anything customer");
}
model.addAttribute("customers", customers);
return "list";
}
}
客户。
package com.baotrung.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Objects;
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String email;
public Customer() {
}
public Customer(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
@Override
public int hashCode() {
return Objects.hash(id, firstName, lastName, email);
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", firstName='" + firstName + ''' +
", lastName='" + lastName + ''' +
", email='" + email + ''' +
'}';
}
}
CustomerRepository。
public interface CustomerRepository extends CrudRepository<Customer, Long> {
}
模型。
package com.baotrung.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Objects;
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String email;
public Customer() {
}
public Customer(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
@Override
public int hashCode() {
return Objects.hash(id, firstName, lastName, email);
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", firstName='" + firstName + ''' +
", lastName='" + lastName + ''' +
", email='" + email + ''' +
'}';
}
}
服务。
package com.baotrung.service;
import com.baotrung.domain.Customer;
import com.baotrung.exception.ResourceNotFoundException;
import com.baotrung.repository.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class CustomerServiceImpl implements CustomerService {
@Autowired
private CustomerRepository customerRepository;
@Override
@Transactional(readOnly = true)
public List<Customer> findAllCustomer() {
return (List<Customer>) customerRepository.findAll();
}
@Override
@Transactional
public void saveCustomer(Customer customer) {
customerRepository.save(customer);
}
@Override
@Transactional(readOnly = true)
public Customer getCustomer(Long id) {
return customerRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Can't find any customer with id:" + id));
}
@Override
@Transactional
public void deleteCustomer(Long id) {
customerRepository.deleteById(id);
}
}
当我运行它时,它在以下行中引发异常:When(customerService.findAllCustomer()).thenReturn(Arrays.asList(customer,customer1))错误:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
2. inside when() you don't call method on mock but on some other object.
3. the parent of the mocked class is not public.
It is a limitation of the mock engine.
at com.baotrung.controller.CustomerControllerTest.findAll(CustomerControllerTest.java:54)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
更新:
当我删除anotation@Autowired并添加anotation@Mock时,它会抛出错误:
java.lang.NullPointerException
at com.baotrung.controller.CustomerControllerTest.findAll(CustomerControllerTest.java:55)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
我怀疑问题出在测试代码中对CustomerService的使用上。这是一颗真正的豆子,而不是一颗被嘲笑的豆子。你需要模仿它,设定期望值。
@Autowired
private CustomerService customerService;
我想@MockBean
(如果使用spring-boot),否则您需要定义一个模拟版本的CustomerService,这将解决您的目的,但请尝试一下。
你怎么能做到您可以在SpringConfiguration类中定义一个新的模拟CustomerService实例,并在测试类中使用它,@ContextConfiguration
允许您提及要使用的任何配置类。此外,对于CustomerServiceImpl中的可维护代码,您还需要从属性注入转向基于构造函数的注入。
@Service
public class CustomerServiceImpl implements CustomerService {
@Autowired
private CustomerRepository customerRepository;
...
}
类似于:
@Service
public class CustomerServiceImpl implements CustomerService {
private CustomerRepository customerRepository;
@Autowired
public CustomerServiceImpl(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
...
}
解决步骤:
- CustomerController不应使用CustomerServiceImpl作为注入的bean,而应使用CustomerService
- 按照上面的建议更改CustomerServiceImpl的定义
- 定义一个TestConfiguration.java,在其中定义CustomerService的模拟实例。公共类TestConfiguration{@Bean公共CustomerService CustomerService(){return Mockito.mock(CustomerService.class);}}
- 将测试类@ContextConfiguration更新为
@ContextConfiguration(classes = {TestConfiguration.class, PersistenceJPAConfig.class, WebConfig.class})
执行此操作并进行验证。