英文:
Mocking a Predicate in Java using Mockito
问题
public class 验证器 {
public Predicate<String> 是否存在行 = 文件名 -> 创建Dao().是否返回行(行号);
public AlertFileDAO 创建数据Dao(){
return new 数据Dao();
}
public boolean 验证(String 行号){
return 是否存在行.test(行号);
}
}
//测试
public class 验证器测试{
@setup
void 前置操作(){
mock验证器 = spy(new 验证器());
doReturn(mock数据Dao)
.when(mock验证器)
.创建数据Dao();
}
@Test
test_当行存在时(){
new 验证器.验证("1-abc-34");
}
}
英文:
I have a Predicate which checks for a row existence in Database.I am not sure if this is a good use of predicate but it made my code clean and concise.But when I am Tesing this code I am not able to mock the DAO class and not sure why is the case.
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");
}
When Im triggering the test it is hitting the actual DB and not using the mocked DAO class.Im not sure what exactly I am missing here.Please suggest.
答案1
得分: 2
为什么不直接将条件内联并将 dao 作为构造函数参数传递呢?这将使您的 API 更清晰:方法调用与获取器相对于条件,以及在条件测试中您最终使用的条件。
根据您接受的答案,用户必须使用以下代码:
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:
Dao 通常是单例的(一个实例足够)。根据您使用的框架,创建 Dao 可能比在其上调用方法更昂贵。让我们应用依赖注入原则(类接收其依赖项,而不是创建它们):
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,您可以在构造函数中提供一个工厂。
结果:
您的类:
- 具有更好的 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));
}
}
英文:
Why don’t you simply inline the predicate and deliver the dao as constructor argument? This makes your api cleaner: method call vs getter for predicate and test on predicate you ended up with.
With your accepted answer, the user has to use the following:
validator.doesRowExist().test(rowId);
I believe the following would be easier to use:
validator.doesRowExist(rowId);
or even:
validator.validate(rowId);
Lets make a series of refactorings to achieve that:
Step 1:
You use your predicate to implement validate
function. There are no other calls, nor passing to another functions (higher-order functions accepting a predicate are a typical use for them). Let's change the predicate to a method:
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);
}
}
Step 2:
Daos are typically singletons (one instance of them is enough). Depending on the frameworks you use, creating a Dao may be more costly than calling a method on it. Let's apply dependency injection principles (class receives it dependencies, not creates them):
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);
}
}
If you really need to create Dao each time, you can provide a fecory in the constructor.
Result:
Your class:
- has nicer api
- is likely more efficient
- is trivially testable:
@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));
}
}
答案2
得分: 1
我将您的问题视为更常见任务的示例:如何对字段进行存根。在您的情况下,您需要对字段 doesRowExists
进行存根。
这个常见任务有一个通用解决方案:使用 getter 方法代替:public Predicate<String> getDoesRowExists() { return doesRowExists;}
或者,按照通用的代码风格,public Predicate<String> isRowExists() { return doesRowExists;}
因此,在您的生产代码中,您调用 getter 方法而不是字段:return isRowExists().test(rowId)
。
在您的测试代码中,您只需模拟这个 getter 方法:when(isRowExists).thenReturn(true)
英文:
I see your problem as example of more common task: how to stub a field. In your case, you need to stub field doesRowExists
.
The common task has common solution: use getter instead: public Predicate<String> getDoesRowExists() { return doesRowExists;}
or, with common code style, public Predicate<String> isRowExists() { return doesRowExists;}
So, in your production code you call getter instead field: return isRowExists().test(rowId)
In your test code you just mock this getter: when(isRowExists).thenReturn(true)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论