Dnyaneshwar Pote
Dnyaneshwar Pote

Reputation: 77

How to mock method's local variables/variable methods using Mockito

I have a class named A and method method1() in it:

Class A{

    boolean method1(String path){
        File procDir = new File(path);
        if(!procDir.exists()){
            return false`;
        }
        if(!procDir.canRead()){
            return false;
        }
    }
}

Considering above code would anyone suggest, the way to mack the method1 and inner method/variables (procDir.canRead()) .

Upvotes: 0

Views: 15232

Answers (3)

GhostCat
GhostCat

Reputation: 140457

The first answer is: you can't with Mockito alone.

You need a mocking framework capable of mocking calls to new. Such as PowerMock(ito) or JMockit. You can find documentation for how to do that using PowerMock here. In that sense, technically this is a solved problem - it only requires some twiddling to get it to work (if you get one of the pre-reqs wrong, it simply will not work).

But beyond that, the other answers are spot on: you created hard to test code, and now you are looking for a band-aid to work around the consequences of your inflexible design.

So instead of spending valuable time on twiddling with PowerMock(ito) - follow the advise you already got and fix the root cause of your test problem. For example by using some sort of dependency injection to provide a File object to this code (instead of having this code call new itself).

Upvotes: 3

glytching
glytching

Reputation: 47905

You could use JUnit's TemporaryFolder rule (or, if using JUnit5, its equivalent extension) to create an input file for your test(s).

You would then ...

  • Pass the path to this file into method1() - to test the happy path
  • Pass a non existent file path into method1() to test the !procDir.exists() sad path

On completion of the test JUnit will discard the temporary folder, thereby adhering to the test principle of self containment.

This approach allows you to test your code without any mocking whilst still being a self contained unit test.

Alternatively you could hide the File procDir = new File(path); call behind an interface:

public interface FileCreator {
    File create(String path);
}

With a simple implementation:

public class FileCreatorImpl implements FileCreator {
    public File create(String path) {
        return new File(path);    
    }
}

When testing, you could inject a Mock(FileCreator.class) into the instance of the class which contains method1() and set expectations on that instance as follows:

String filePath = "...";
File file = Mockito.mock(File.class);
Mockito.when(fileCreator.create(filePath)).thenReturn(file);

// happy path
Mockito.when(file.exists()).thenReturn(true);
Mockito.when(file.canRead()).thenReturn(true);
assertThat(sut.method1(filePath), is(true));

// sad path 1
Mockito.when(file.exists()).thenReturn(false);
assertThat(sut.method1(filePath), is(false));

// sad path 2
Mockito.when(file.exists()).thenReturn(true);
Mockito.when(file.canRead()).thenReturn(false);
assertThat(sut.method1(filePath), is(false));

Upvotes: 0

Nkosi
Nkosi

Reputation: 247133

This is a matter of design as the method is tightly coupled to File

The Explicit Dependencies Principle states:

Methods and classes should explicitly require (typically through method parameters or constructor parameters) any collaborating objects they need in order to function correctly.

With that in mind, consider refactoring the method to explicitly depend on the File

boolean method1(File procDir){    
    if(!procDir.exists()){
      return false`;
    }

    if(!procDir.canRead()){
      return false;
    }

    return true;
}

Which also inverts the creation of the dependency to a delegate external to the class/method. This also lets the method state explicitly what it actually needed.

Now that the method is decoupled the method can be tested by passing a proper File or mock.

You can also consider abstracting File

public interface FileInfo {
    boolean exists();
    boolean canRead()
}

as classes should depend on abstractions and not concretions.

boolean method1(FileInfo procDir){    
    if(!procDir.exists()){
      return false`;
    }

    if(!procDir.canRead()){
      return false;
    }

    return true;
}

The implementation of FileInfo can then encapsulate an actual File and expose the desired behavior.

For testing is should easier now to mock/stub/fake the abstraction either directly by inheritance or a mocking framework.

FileInfo file = mock(FileInfo.class);

when(file.exists()).thenReturn(true);
when(file.exists()).thenReturn(true);

subject.method1(file);

Upvotes: 0

Related Questions