我是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]
现在可以使用修复程序了。
答案 0 :(得分:1)
在我看来,你甚至不需要Mockito,你可以简单地做到以下几点:
Template template = new Template();
template.setStartTime("THIS IS AN INVALID DATE");
template.setTemplateId(1);
templateDAOImpl.saveTemplate(template);
然后抛出SQLException。
答案 1 :(得分:1)
SimpleDateFormat根本无法模拟,因为您在方法中创建了一个新实例,因此将永远不会应用模拟。
的可能性:
答案 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);
}