使用 Mockito 在 Java 中模拟谓词
Mocking a Predicate in Java using Mockito
我有一个 Predicate,它检查 Database.I 中是否存在行我不确定这是否是 predicate 的一个很好的用途,但它使我的代码干净,concise.But 当我测试这段代码时我无法模拟 DAO class 并且不确定为什么会这样。
public class validator{
public Predicate<String> doesRowExists = fileName -> makeDao().isRowReturned(RowId);
public AlertFileDAO makeDataDao(){
return new DataDao();
}
public boolean validate(String RowId){
return doesRowExists.test(rowId)
}
}
//Test
public class ValidatorTest{
@setup
void beforeAll(){
mockValidator = spy(new Validator());
doReturn(mockDataDao)
.when(mockValidator)
.makeDataDao();
}
@Test
test_whenRowExists(){
new Validator.validate("1-abc-34");
}
当我触发测试时,它击中了实际的数据库,而不是使用模拟的 DAO class.Im 不确定我到底缺少什么 here.Please 建议。
我将您的问题视为更常见任务的示例:如何存根字段。在您的情况下,您需要存根字段 doesRowExists
.
常见任务有常见的解决方案:改用getter:public Predicate<String> getDoesRowExists() { return doesRowExists;}
或者,使用常见的代码风格,public Predicate<String> isRowExists() { return doesRowExists;}
因此,在您的生产代码中,您调用 getter 而不是字段:return isRowExists().test(rowId)
在你的测试代码中你只是模拟这个 getter: when(isRowExists).thenReturn(true)
为什么不简单地内联谓词并将 dao 作为构造函数参数传递?这使您的 api 更清晰:方法调用与 getter 的谓词和测试您最终得到的谓词。
对于您接受的答案,用户必须使用以下内容:
validator.doesRowExist().test(rowId);
我相信以下会更容易使用:
validator.doesRowExist(rowId);
甚至:
validator.validate(rowId);
让我们进行一系列重构来实现这一点:
第 1 步:
您使用谓词来实现 validate
功能。没有其他调用,也没有传递给另一个函数(接受谓词的高阶函数是它们的典型用途)。让我们将谓词更改为方法:
public class Validator {
public DataDao makeDataDao(){
return new DataDao();
}
public boolean validate(String rowId){
return doesRowExist(rowId);
}
private boolean doesRowExist(String rowId) {
return makeDataDao().isRowReturned(rowId);
}
}
第 2 步:
Daos 通常是单例(一个实例就足够了)。根据您使用的框架,创建 Dao 可能比在其上调用方法的成本更高。让我们应用依赖注入原则(class 接收它的依赖,而不是创建它们):
public class Validator {
private final DataDao dataDao;
Validator(DataDao dataDao) {
this.dataDao = dataDao;
}
public boolean validate(String rowId){
return doesRowExist(rowId);
}
private boolean doesRowExist(String rowId) {
return dataDao.isRowReturned(rowId);
}
}
如果确实需要每次创建Dao,可以在构造函数中提供一个fecory
结果:
你的class:
- 更好api
- 可能更有效率
- 可简单测试:
@ExtendWith(MockitoExtension.class)
public class ValidatorTest {
@Mock
DataDao mockDataDao;
@InjectMocks
Validator validator;
@Test
void whenValidateReturnsValueFromIsRowReturned(){
var rowId = "1-abc-34";
doReturn(false)
.when(mockDataDao)
.isRowReturned(rowId);
assertEquals(false, validator.validate(rowId));
}
}
我有一个 Predicate,它检查 Database.I 中是否存在行我不确定这是否是 predicate 的一个很好的用途,但它使我的代码干净,concise.But 当我测试这段代码时我无法模拟 DAO class 并且不确定为什么会这样。
public class validator{
public Predicate<String> doesRowExists = fileName -> makeDao().isRowReturned(RowId);
public AlertFileDAO makeDataDao(){
return new DataDao();
}
public boolean validate(String RowId){
return doesRowExists.test(rowId)
}
}
//Test
public class ValidatorTest{
@setup
void beforeAll(){
mockValidator = spy(new Validator());
doReturn(mockDataDao)
.when(mockValidator)
.makeDataDao();
}
@Test
test_whenRowExists(){
new Validator.validate("1-abc-34");
}
当我触发测试时,它击中了实际的数据库,而不是使用模拟的 DAO class.Im 不确定我到底缺少什么 here.Please 建议。
我将您的问题视为更常见任务的示例:如何存根字段。在您的情况下,您需要存根字段 doesRowExists
.
常见任务有常见的解决方案:改用getter:public Predicate<String> getDoesRowExists() { return doesRowExists;}
或者,使用常见的代码风格,public Predicate<String> isRowExists() { return doesRowExists;}
因此,在您的生产代码中,您调用 getter 而不是字段:return isRowExists().test(rowId)
在你的测试代码中你只是模拟这个 getter: when(isRowExists).thenReturn(true)
为什么不简单地内联谓词并将 dao 作为构造函数参数传递?这使您的 api 更清晰:方法调用与 getter 的谓词和测试您最终得到的谓词。
对于您接受的答案,用户必须使用以下内容:
validator.doesRowExist().test(rowId);
我相信以下会更容易使用:
validator.doesRowExist(rowId);
甚至:
validator.validate(rowId);
让我们进行一系列重构来实现这一点:
第 1 步:
您使用谓词来实现 validate
功能。没有其他调用,也没有传递给另一个函数(接受谓词的高阶函数是它们的典型用途)。让我们将谓词更改为方法:
public class Validator {
public DataDao makeDataDao(){
return new DataDao();
}
public boolean validate(String rowId){
return doesRowExist(rowId);
}
private boolean doesRowExist(String rowId) {
return makeDataDao().isRowReturned(rowId);
}
}
第 2 步:
Daos 通常是单例(一个实例就足够了)。根据您使用的框架,创建 Dao 可能比在其上调用方法的成本更高。让我们应用依赖注入原则(class 接收它的依赖,而不是创建它们):
public class Validator {
private final DataDao dataDao;
Validator(DataDao dataDao) {
this.dataDao = dataDao;
}
public boolean validate(String rowId){
return doesRowExist(rowId);
}
private boolean doesRowExist(String rowId) {
return dataDao.isRowReturned(rowId);
}
}
如果确实需要每次创建Dao,可以在构造函数中提供一个fecory
结果:
你的class:
- 更好api
- 可能更有效率
- 可简单测试:
@ExtendWith(MockitoExtension.class)
public class ValidatorTest {
@Mock
DataDao mockDataDao;
@InjectMocks
Validator validator;
@Test
void whenValidateReturnsValueFromIsRowReturned(){
var rowId = "1-abc-34";
doReturn(false)
.when(mockDataDao)
.isRowReturned(rowId);
assertEquals(false, validator.validate(rowId));
}
}