【问题标题】:JunitTest throws InvocationTargetException when using test Spring MVCJunit Test 在使用测试 Spring MVC 时抛出 InvocationTargetException
【发布时间】:2019-01-29 07:21:17
【问题描述】:

我使用 JUnitTest 和 Mock 测试 Spring MVC 测试 getAll Employee 但它在运行时抛出错误“InvocationTargetException”:when(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 + '\'' +
                '}';
    }
}

客户存储库。

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)

【问题讨论】:

  • 你没有模拟 customerService,但你试图像模拟一样使用它
  • 但我自动连线了???它是一种豆子。我认为 spring 理解它是一个 bean 并控制它。
  • 是的,它是一个豆子。但你需要一个模拟。一个 bean 和一个 mock 远非一回事。将 bean 视为实际实现,而对于模拟,您可以决定任何调用将返回什么
  • 我已经更改了它,但它抛出了空指针异常:(
  • 你是怎么改的?

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


【解决方案1】:

我怀疑问题出在测试代码中使用了 CustomerService。它是一个真正的 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 { @豆 公共客户服务客户服务(){ 返回 Mockito.mock(CustomerService.class); } }
  4. 将测试类@ContextConfiguration 更新为@ContextConfiguration(classes = {TestConfiguration.class, PersistenceJPAConfig.class, WebConfig.class})

执行此操作并验证。

【讨论】:

  • 您应该使用新代码更新问题,然后我们可能会提供进一步的帮助
  • 我不使用弹簧靴我只使用弹簧
  • 我已经解释了这两种方式,通过How can you do it?它对spring和springboot都是不可知的
  • 我已经列出了你应该如何解决它的步骤,通过并申请。
  • 好的。谢谢你。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-21
  • 2017-09-01
  • 2013-05-16
  • 2013-09-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多