Mockito :: 如何在 Dao 中模拟 SimpleDateFormat.parse()



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

这是道码:

public Template saveTemplate(Template template) throws SQLException {
logger.debug("Saving template details into the db ", template.getTemplateName());
SimpleDateFormat dt = new SimpleDateFormat("yyyyy-mm-dd hh:mm:ss");
Long date = 0L;
try {
date = dt.parse(template.getStartTime()).getTime();
} catch (ParseException e) {
throw new SQLException("Error while processing date " + template.getTemplateName());
}
Long finalDate = date;

我的策略是模拟SimpleDateFormat.parse()调用,以便它抛出 ParseException,但这不起作用。甚至不确定这是一个好的策略...

首先我尝试了:

@InjectMocks private SimpleDateFormat simpleDateformat;

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

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

然后我尝试了这个:

@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.

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

编辑:建议的新方法,模拟模板.getStartTime((: @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); }

现在可以使用修复程序。

在我看来,你在这里甚至不需要Mockito,你可以简单地执行以下操作:

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

然后SQL感知将被抛出。

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

可能性:

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

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

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

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

也就是说:您正在设置一个名为templateMock的对象,该对象具有Template类型,并配置了您想要的行为,但您实际传递到templateDAOImpl.saveTemplate的对象不是该对象!

这意味着所有设置templateMock的代码实际上是死代码:由于您传递给saveTemplate的值是使用new Template()构造的,因此它不是模拟。

更一般地说:Mockito从不做任何你自己做不到的事情。例如,在这种情况下,您可以简单地创建自己的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);

这就是莫克蒂托在你嘲笑Template时所做的一切。Mockito只是以一种"更花哨"的方式做到这一点,这样你就有更多的样板代码要写了。但是,如果您不了解Mockito在做什么,那么您就不应该被迫使用它。始终编写您理解的代码,然后如果出现问题,您将始终能够对其进行调试。

(题外话:有一些肮脏的黑客可以让new运算符实例化一些与编译时不同的东西——这就是PowerMockito发挥其"魔力"的方式——但这从来都不是必需的,我永远不会推荐它。

只是为了记录,我找到了另一种实现此要求的方法,我使用了间谍并嘲笑了几乎所有内容; 它可能会帮助其他人。

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

相关内容

  • 没有找到相关文章

最新更新