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

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

Mocking a Predicate in Java using Mockito

问题

  1. public class 验证器 {
  2. public Predicate<String> 是否存在行 = 文件名 -> 创建Dao().是否返回行(行号);
  3. public AlertFileDAO 创建数据Dao(){
  4. return new 数据Dao();
  5. }
  6. public boolean 验证(String 行号){
  7. return 是否存在行.test(行号);
  8. }
  9. }
  10. //测试
  11. public class 验证器测试{
  12. @setup
  13. void 前置操作(){
  14. mock验证器 = spy(new 验证器());
  15. doReturn(mock数据Dao)
  16. .when(mock验证器)
  17. .创建数据Dao();
  18. }
  19. @Test
  20. test_当行存在时(){
  21. new 验证器.验证("1-abc-34");
  22. }
  23. }
英文:

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.

  1. public class validator{
  2. public Predicate&lt;String&gt; doesRowExists = fileName -&gt; makeDao().isRowReturned(RowId);
  3. public AlertFileDAO makeDataDao(){
  4. return new DataDao();
  5. }
  6. public boolean validate(String RowId){
  7. return doesRowExists.test(rowId)
  8. }
  9. }
  10. //Test
  11. public class ValidatorTest{
  12. @setup
  13. void beforeAll(){
  14. mockValidator = spy(new Validator());
  15. doReturn(mockDataDao)
  16. .when(mockValidator)
  17. .makeDataDao();
  18. }
  19. @Test
  20. test_whenRowExists(){
  21. new Validator.validate(&quot;1-abc-34&quot;);
  22. }

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 更清晰:方法调用与获取器相对于条件,以及在条件测试中您最终使用的条件。

根据您接受的答案,用户必须使用以下代码:

  1. validator.doesRowExist().test(rowId);

我认为以下用法更加简单:

  1. validator.doesRowExist(rowId);

甚至可以这样写:

  1. validator.validate(rowId);

让我们进行一系列的重构来实现这一点:

步骤 1:

您使用条件来实现 validate 函数。没有其他调用,也没有传递给其他函数(接受条件的高阶函数是它们的典型用途)。让我们将条件更改为方法:

  1. public class Validator {
  2. public DataDao makeDataDao(){
  3. return new DataDao();
  4. }
  5. public boolean validate(String rowId){
  6. return doesRowExist(rowId);
  7. }
  8. private boolean doesRowExist(String rowId) {
  9. return makeDataDao().isRowReturned(rowId);
  10. }
  11. }

步骤 2:

Dao 通常是单例的(一个实例足够)。根据您使用的框架,创建 Dao 可能比在其上调用方法更昂贵。让我们应用依赖注入原则(类接收其依赖项,而不是创建它们):

  1. public class Validator {
  2. private final DataDao dataDao;
  3. Validator(DataDao dataDao) {
  4. this.dataDao = dataDao;
  5. }
  6. public boolean validate(String rowId){
  7. return doesRowExist(rowId);
  8. }
  9. private boolean doesRowExist(String rowId) {
  10. return dataDao.isRowReturned(rowId);
  11. }
  12. }

如果您真的需要每次创建 Dao,您可以在构造函数中提供一个工厂。

结果:

您的类:

  • 具有更好的 API
  • 可能更加高效
  • 可以轻松进行测试:
  1. @ExtendWith(MockitoExtension.class)
  2. public class ValidatorTest {
  3. @Mock
  4. DataDao mockDataDao;
  5. @InjectMocks
  6. Validator validator;
  7. @Test
  8. void whenValidateReturnsValueFromIsRowReturned(){
  9. var rowId = "1-abc-34";
  10. doReturn(false)
  11. .when(mockDataDao)
  12. .isRowReturned(rowId);
  13. assertEquals(false, validator.validate(rowId));
  14. }
  15. }
英文:

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:

  1. validator.doesRowExist().test(rowId);

I believe the following would be easier to use:

  1. validator.doesRowExist(rowId);

or even:

  1. 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:

  1. public class Validator {
  2. public DataDao makeDataDao(){
  3. return new DataDao();
  4. }
  5. public boolean validate(String rowId){
  6. return doesRowExist(rowId);
  7. }
  8. private boolean doesRowExist(String rowId) {
  9. return makeDataDao().isRowReturned(rowId);
  10. }
  11. }

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):

  1. public class Validator {
  2. private final DataDao dataDao;
  3. Validator(DataDao dataDao) {
  4. this.dataDao = dataDao;
  5. }
  6. public boolean validate(String rowId){
  7. return doesRowExist(rowId);
  8. }
  9. private boolean doesRowExist(String rowId) {
  10. return dataDao.isRowReturned(rowId);
  11. }
  12. }

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:
  1. @ExtendWith(MockitoExtension.class)
  2. public class ValidatorTest {
  3. @Mock
  4. DataDao mockDataDao;
  5. @InjectMocks
  6. Validator validator;
  7. @Test
  8. void whenValidateReturnsValueFromIsRowReturned(){
  9. var rowId = &quot;1-abc-34&quot;;
  10. doReturn(false)
  11. .when(mockDataDao)
  12. .isRowReturned(rowId);
  13. assertEquals(false, validator.validate(rowId));
  14. }
  15. }

答案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:

确定