首页 > 解决方案 > 无法测试 SimpleJdbcCall 执行调用

问题描述

我需要测试以下课程

@Repository
public class CustomerContactDaoImpl implements CustomerContactDao {
    @Autowired
    @Qualifier(value = "rcx_jdbc_template")
    private JdbcTemplate jdbcTemplate;
    @Override
    public void insertCustomerContact(Contact contact) {
        SimpleJdbcCall call = new SimpleJdbcCall(jdbcTemplate).withProcedureName("insert_contact");
        try {
            call.execute(new MapSqlParameterSource().addValue("customerid", contact.getCustomerID()));

        } catch (Exception e) {
            e.printStackTrace();
        }


    }

}

我的测试类

@RunWith(MockitoJUnitRunner.class)
public class CustContDaoImplTest {
     @Mock
       private SimpleJdbcCallFactory simpleJdbcCallFactory;
     @Mock
       private DataSource dataSource;
     @Mock
       private JdbcTemplate jdbcTemplate;
     @Autowired
     @InjectMocks
       private CustomerContactDaoImpl customerContactDaoImpl;
     @Before
     public void setUp() throws SQLException {
      MockitoAnnotations.initMocks(this);
     }

     @Test
       public void testinsertCustomerContact() throws Exception {
           Contact contact = Mockito.mock(Contact.class);
            SimpleJdbcCall mockedSimpleJdbcCall  = Mockito.mock(SimpleJdbcCall.class);
            mockedSimpleJdbcCall = new SimpleJdbcCall(jdbcTemplate).withProcedureName("insert_contact");
            Mockito.when(mockedSimpleJdbcCall.execute(any(MapSqlParameterSource.class))).thenReturn(any(Map.class));
            customerContactDaoImpl.insertCustomerContact(contact);
       }
}

@Component
class SimpleJdbcCallFactory {
   public SimpleJdbcCall create(JdbcTemplate template) {
       return new SimpleJdbcCall(template);
   }
}

得到以下异常

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Invalid use of argument matchers!
0 matchers expected, 1 recorded:
-> dao.test.CustContDaoImpl.testinsertCustomerContact(CustContDaoImpl.java:68)



    at org.springframework.jdbc.core.simple.AbstractJdbcCall.compile(AbstractJdbcCall.java:283)
    at org.springframework.jdbc.core.simple.AbstractJdbcCall.checkCompiled(AbstractJdbcCall.java:348)
    at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:363)
    at org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:198)
    atdao.test.CustContDaoImpl.testinsertCustomerContact(CustContDaoImpl.java:68)
    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.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    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.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
    at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)

我试过了

@RunWith(MockitoJUnitRunner.class)
public class CustContDaoImplTest {
     @Mock
       private SimpleJdbcCallFactory simpleJdbcCallFactory;
     @Mock
       private DataSource dataSource;
     @Mock
       private JdbcTemplate jdbcTemplate;

     @InjectMocks
       private CustomerContactDaoImpl customerContactDaoImpl;

     @Before
     public void setUp() throws SQLException {
      MockitoAnnotations.initMocks(this);
     }

     @Test
       public void testinsertCustomerContact() throws Exception {
           Contact contact = Mockito.mock(Contact.class);
            Map<String, Object> resMap = new HashMap<>();
            Mockito.when(jdbcTemplate.call(Mockito.any(), Mockito.any())).thenReturn(resMap);
            customerContactDaoImpl.insertCustomerContact(contact);
     }
}

java.lang.IllegalArgumentException: No DataSource specified
    at org.springframework.util.Assert.notNull(Assert.java:115)
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:97)
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
    at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:289)
    at org.springframework.jdbc.core.metadata.CallMetaDataProviderFactory.createMetaDataProvider(CallMetaDataProviderFactory.java:73)
    at org.springframework.jdbc.core.metadata.CallMetaDataContext.initializeMetaData(CallMetaDataContext.java:286)
    at org.springframework.jdbc.core.simple.AbstractJdbcCall.compileInternal(AbstractJdbcCall.java:303)
    at org.springframework.jdbc.core.simple.AbstractJdbcCall.compile(AbstractJdbcCall.java:288)
    at org.springframework.jdbc.core.simple.AbstractJdbcCall.checkCompiled(AbstractJdbcCall.java:348)
    at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:363)
    at org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:198)
    at com.nrg.bccd.dao.impl.CustomerContactDaoImpl.insertCustomerContact(CustomerContactDaoImpl.java:65)
     dao.test.CustContDaoImplTest.testinsertCustomerContact(CustContDaoImplTest.java:44)
    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)

标签: mockingmockito

解决方案


正如评论中所暗示的那样,您的异常可能来自试图在非模拟的东西上定义行为。

假设您的代码看起来像这样(更改后):

@Test
public void testinsertCustomerContact() throws Exception {
    Contact contact = Mockito.mock(Contact.class);

    SimpleJdbcCall mockedSimpleJdbcCall = Mockito.mock(SimpleJdbcCall.class);

    // this line is causing the problem
    mockedSimpleJdbcCall = new SimpleJdbcCall(jdbcTemplate).withProcedureName("insert_contact");

    Map<String, Object> resMap = Mockito.mock(Map.class);
    Mockito.when(mockedSimpleJdbcCall.execute(any(MapSqlParameterSource.class))).thenReturn(resMap); 

    customerContactDaoImpl.insertCustomerContact(contact);
}

SimpleJdbcCall那么当您创建一个新对象时,您仍然覆盖了模拟。


但是,所有这些似乎都不是正确的方法,因为它与您的测试类不匹配。

由于不知道这些注释,因此将@Autowired/@Component与 the 一起使用MockitoJUnitRunner不会产生任何影响。Mockito

您需要做的是在您的JdbcTemplate模拟上定义行为,以便它 SimpleJdbcCall可以正常工作。我简要查看了 spring 源代码,并根据我所看到的(至少)定义call方法中使用的AbstractJdbcCall#executeCallInternal方法的行为。

所以把测试改成这样可能会奏效......

@RunWith(MockitoJUnitRunner.class)
public class CustContDaoImplTest {

    @Mock
    private JdbcTemplate jdbcTemplate;

    @InjectMocks
    private CustomerContactDaoImpl customerContactDaoImpl;

    @Before
    public void setUp() throws SQLException {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testinsertCustomerContact() throws Exception {

        Contact contact = Mockito.mock(Contact.class);

        Map<String, Object> resMap = new HashMap<>();
        Mockito.when(jdbcTemplate.call(Mockito.any(), Mockito.any())).thenReturn(resMap);

        customerContactDaoImpl.insertCustomerContact(contact);
    }
}

注意:模拟 aMap从来都不是真正必要的。相反,只需创建一个地图并将所需的内容添加到其中。


然而,更简单的方法是重构类,以便您可以简单地替换新创建的SimpleJdbcCall. 您的示例代码显示了一个SimpleJdbcCallFactory创建此类对象的类,但似乎与您的测试类没有任何关系。

如果重构不是使用PowerMockito.whenNew功能的选项,我们可能也值得考虑。


就是说-最后我建议您宁愿专注于带有real数据库的集成测试,而不是在这里使用模拟。


推荐阅读