这里的问题是您的 被测代码 实例化了它的依赖项(这里是 PreparedStatementCreator 的实例)本身。
你应该注入它的一个实例。在这种情况下,您可以注入 PreparedStatementCreator 的模拟并捕获传递给该模拟的参数。
我对 Junit 和 Mocking 还很陌生。
这不是关于嘲笑,而是关于单一职责/关注点分离。它提高了代码的可重用性。可测试性是可重用代码的标志。
注入模拟是什么意思。可以举个例子吗?
这里的问题是接口PreparedStatementCreator 没有提供合适的接口与Item 类一起作为参数使用。因此引入一个工厂类是很有用的:
public class ItemPreparedStatementCreatorFactory{
public PreparedStatementCreator createFor(Item item){
return new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection) {
final PreparedStatement ps = connection.prepareStatement( "SET NAME = ? where ID = ?");
ps.setString(1, item.getName());
ps.setInt(1, item.getId());
return ps;
}
})
}
}
您可以将该类的实例作为构造函数参数传递给您的测试代码:
您的测试代码可能如下所示:
public class YourDaoClass {
private final JdbcTemplate jdbcTemplate;
private final ItemPreparedStatementCreatorFactory preparedStatementCreatorFactory;
public YourDaoClass(ItemPreparedStatementCreatorFactory preparedStatementCreatorFactory, JdbcTemplate jdbcTemplate){
this.preparedStatementCreatorFactory = preparedStatementCreatorFactory;
this.jdbcTemplate = jdbcTemplate;
}
那么被测方法会变成:
public void update(Item item) {
jdbcTemplate.update(preparedStatementCreatorFactory.createFor(item));
}
而且您将对被测代码进行单独的测试。
public class YourDaoClassTest{
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock
private JdbcTemplate jdbcTemplate;
@Mock
private ItemPreparedStatementCreatorFactory preparedStatementCreatorFactory;
@Mock
private PreparedStatementCreator preparedStatementCreator;
YourDaoClass yourDaoClass;
@Before
public void setup(){
// I prefer direct object creation over @InjectMocks since the latter does not raise compile errors on missing constructor arguments...
yourDaoClass = new YourDaoClass(preparedStatementCreatorFactory,jdbcTemplate);
}
@Test
public void passesItemToStatementFactory(){
Item item = new Item();
doReturn(preparedStatementCreator)
.when(preparedStatementCreatorFactory)
.createFor(item);
yourDaoClass-update(item);
InOrder inOrder= inOrder(preparedStatementCreatorFactory,jdbcTemplate);
inOrder.verify(preparedStatementCreatorFactory).createFor(item);
inOrder.verify(jdbcTemplate).update(preparedStatementCreator);
}
}
public class ItemPreparedStatementCreatorFactoryTest{
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock
private PreparedStatement preparedStatement;
@Mock
private Connection connection;
@Before
public void setup(){
// maybe exchange anyString() with an ArgumentCaptor
doReturn(preparedStatement).when(connection).prepareStatement(anyString());
}
@Test
public void passesNameAndIdToPreparedStatement(){
Item item = new Item();
item.setName("an valid name");
item.setID(ANY_VALID_ID);
ItemPreparedStatementCreatorFactory itemPreparedStatementCreatorFactory =
new ItemPreparedStatementCreatorFactory();
PreparedStatement createdPreparedStatement = itemPreparedStatementCreatorFactory.createFor(item);
verify(createdPreparedStatement).setString(1, item.getName());
verify(createdPreparedStatement).setInt(1, item.getId());
}
}
结论
当您在测试生产代码时遇到困难时,很可能不是以违反 SRP/SoC 原则的可重用方式编写的。
另一方面,显示的测试是愚蠢的,因为没有真正的逻辑可以验证,因为生产代码“太简单而不会失败”,并且测试基本上重复了代码所做的事情。通常这样的测试并没有真正的用处,因为它们与实现紧密耦合,并在实现发生变化时中断。