在Java中使用Mockito模拟一个断言条件。

huangapple go评论82阅读模式
英文:

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&lt;String&gt; doesRowExists = fileName -&gt; 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(&quot;1-abc-34&quot;);
}

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 = &quot;1-abc-34&quot;;
        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&lt;String&gt; getDoesRowExists() { return doesRowExists;} or, with common code style, public Predicate&lt;String&gt; 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)

huangapple
  • 本文由 发表于 2020年4月7日 04:07:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/61068096.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定