使用测试Spring MVC时JunitTest引发InvocationTargetException

时间:2019-01-29 07:21:18

标签: java spring unit-testing spring-mvc mockito

我使用JUnitTest和Mock测试Spring MVC测试getAll Employee,但是当它在以下行中运行时,它向我抛出错误“ InvocationTargetException”:当(customerService.findAllCustomer())。thenReturn(Arrays.asList(customer,customer1) ); 。我不知道为什么?以下是我的测试。

CustomerControllerTest。

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)

更新:

当我删除注释@Autowired并添加注释@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)

1 个答案:

答案 0 :(得分:0)

我怀疑问题在于测试代码中使用CustomerService。这是一个实际的bean,而不是一个被嘲笑的bean。您需要模拟它,以建立期望。

@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;
    }

    ...
}

解决步骤:

  1. CustomerController不应将CustomerServiceImpl用作注入的Bean,而应该是CustomerService。
  2. 按照上面的建议更改CustomerServiceImpl的定义。
  3. 定义一个TestConfiguration.java,在其中定义CustomerService的模拟实例。 公共类TestConfiguration {     @豆角,扁豆     public CustomerService customerService(){        返回Mockito.mock(CustomerService.class);     } }
  4. 将测试类@ContextConfiguration更新为@ContextConfiguration(classes = {TestConfiguration.class, PersistenceJPAConfig.class, WebConfig.class})

执行此操作并验证。