Mockito ::如何在Dao中使用mockSimpleDateFormat.parse()

时间:2018-05-17 13:00:23

标签: java mockito

我是Java / Mockito的新手并尝试测试Dao方法,特别是捕获ParseException并抛出SQLException的异常条件。

这是Dao代码:

SimpleDateFormat.parse()

我的策略是模拟@InjectMocks private SimpleDateFormat simpleDateformat; 调用,以便抛出ParseException,但这不起作用。甚至不确定这是一个好策略......

首先我尝试了:


org.mockito.exceptions.base.MockitoException: 
Cannot instantiate @InjectMocks field named 'simpleDateFormat' of type 'class java.text.SimpleDateFormat'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : null

但这不起作用,因为SimpleDateFormat构造函数需要一个参数,并得到错误:

@Mock private SimpleDateFormat simpleDateFormat; @Test(expected = SQLException.class) public void test_save_template_date_parse_error() throws ParseException, SQLException { initMocks(this); Mockito.mockingDetails(simpleDateFormat); Mockito.when(simpleDateFormat.parse(anyString(),new ParsePosition(anyInt()))).thenThrow(new ParseException(anyString(),anyInt())); Template template = new Template(); template.setStartTime("2017-01-02 12:12:12"); template.setTemplateId(1); given(jdbcTemplate.getJdbcOperations()).willReturn(jdbcOperations); templateDAOImpl.saveTemplate(template); }

然后我尝试了这个:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Misplaced or misused argument matcher detected here:

-> at com.macys.etap.ee.dao.TemplateDAOImplTest.test_save_template_date_parse_error(TemplateDAOImplTest.java:77)
-> at com.macys.etap.ee.dao.TemplateDAOImplTest.test_save_template_date_parse_error(TemplateDAOImplTest.java:77)

You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
    when(mock.get(anyInt())).thenReturn(null);
    doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
    verify(mock).someMethod(contains("foo"))

This message may appear after an NullPointerException if the last matcher is returning an object 
like any() but the stubbed method signature expect a primitive argument, in this case,
use primitive alternatives.
    when(mock.get(any())); // bad use, will raise NPE
    when(mock.get(anyInt())); // correct usage use

Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
Mocking methods declared on non-public parent classes is not supported.

由此产生的错误对我不熟练的眼睛没有帮助:


    @Test(expected = SQLException.class)
    public void test_save_template_date_parse_error() throws ParseException, SQLException {
      initMocks(this);
      Template templateMock = Mockito.mock(Template.class);
      Mockito.when(templateMock.getStartTime()).thenReturn("invalid");
      Mockito.mockingDetails(templateMock.getStartTime());
      Template template = new Template();
      template.setStartTime("2017-01-02 12:12:12");
      template.setTemplateId(1);
      given(jdbcTemplate.getJdbcOperations()).willReturn(jdbcOperations);
      // Fixed per @Daniel Pryden : works now
      templateDAOImpl.saveTemplate(templateMock); 
    }

那么如何模拟这个东西并抛出错误?

编辑:建议的新方法,模拟Template.getStartTime(): org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: javax.persistence.spi.PersistenceUnitInfo.getValidationMode()Ljavax/persistence/ValidationMode; at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1702) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:579) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:501) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1089) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:859) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) [spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395) [spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) [spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1255) [spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE]

现在可以使用修复程序了。

4 个答案:

答案 0 :(得分:1)

在我看来,你甚至不需要Mockito,你可以简单地做到以下几点:

 Template template = new Template();
 template.setStartTime("THIS IS AN INVALID DATE");
 template.setTemplateId(1);
 templateDAOImpl.saveTemplate(template);

然后抛出SQLException。

答案 1 :(得分:1)

SimpleDateFormat根本无法模拟,因为您在方法中创建了一个新实例,因此将永远不会应用模拟。

的可能性:

  • 更改类结构(例如将SimpleDateFormat作为构造函数参数,然后InjectMocks注释将起作用)
  • 传递解析方法的无效数据以打破它
  • 当新方法使用PowerMockito时,它应该是最终的

答案 2 :(得分:0)

模拟并不神奇。它们只是对象,因此它们遵循与Java语言中的所有其他对象相同的规则。最重要的是:模拟物体不会神奇地取代真实物体。您需要传递对mock的引用,而不是对真实对象的引用。

在您更新的问题中,您会显示以下代码:

  Template templateMock = Mockito.mock(Template.class);
  Mockito.when(templateMock...)...
  Template template = new Template();
  ...
  templateDAOImpl.saveTemplate(template);

那就是:您正在设置一个名为templateMock的对象,其类型为Template,并且配置了您想要的行为,但是您实际传入的对象{{1}不是那个对象!

这意味着设置templateDAOImpl.saveTemplate的所有代码实际上都是死代码:因为传递给templateMock的值是使用saveTemplate构造的,所以它不是模拟的。

更一般地说:Mockito从不做任何你自己无法做到的事情。例如,在这种情况下,您可以简单地创建自己的new Template()的子类:

Template

当你嘲笑private static class FakeTemplate extends Template { @Override public String getStartTime() { return "invalid date"; } // override other methods as necessary/desired } // in your test: Template fakeTemplate = new FakeTemplate(); templateDAOImpl.saveTemplate(fakeTemplate); 时,这就是Mockito所做的一切。 Mockito只是在一个"鸽友"这样你就可以用更少的样板代码来编写。但如果你不明白Mockito正在做什么,那么你就不应该被迫使用它。始终编​​写您理解的代码,然后如果出现问题,您将始终能够对其进行调试。

(旁白: 脏黑客可以使Template运算符实例化与编译时不同的东西 - 这就是PowerMockito如何做到的#34;魔术" - 但这从来没有必要,我永远不会推荐它。)

答案 3 :(得分:0)

仅仅为了记录,我找到了另一种方法来实现这个要求,我使用了一个间谍并嘲笑了几乎所有东西;它可以帮助别人。

@InjectMocks
final Template partiallyMockedTemplate = spy(new Template());

@Test(expected = SQLException.class)
public void test_save_template_date_parse_error() throws SQLException {
  initMocks(this);
    Template template = new Template();
    doReturn("2018-05-44").when(partiallyMockedTemplate).getStartTime();
    partiallyMockedTemplate.setStartTime("2017-01-02 12:12:12");
    partiallyMockedTemplate.setTemplateId(1);
    given(jdbcTemplate.getJdbcOperations()).willReturn(jdbcOperations);
    templateDAOImpl.saveTemplate(partiallyMockedTemplate);
}