简体   繁体   中英

Mocking a Predicate in Java using Mockito

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.

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));
    }

}

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM