How to use Mockito when we cannot pass a mock object to an instance of a class

Suppose I have a class like that:

public class MyClass {

    Dao dao;

    public String myMethod(Dao d) {

        dao = d;

        String result = dao.query();

        return result;
    } 
}

I want to test it with mockito. So I create a mock object and I call the method to test in that way:

Dao mock = Mockito.mock(Dao.class);

Mockito.when(mock.myMethod()).thenReturn("ok");

new MyClass().myMethod(mock);

But, suppose instead I have a class like that:

public class MyClass {

    Dao dao = new Dao();

    public String myMethod() {

        String result = dao.query();

        return result;
    } 
}

Now I cannot pass my mock as an argument, so how I gonna test my method? Can someone show an example?


Fundamentally, you're trying to replace a private field with an alternative implementation, which means you'd violate encapsulation. Your only other option is to restructure the class or method, to make it better-designed for testing.

There are a lot of short answers in the comments, so I'm aggregating them here (and adding a couple of my own) as Community Wiki. If you have any alternatives, please feel free to add them here.

Restructure the class

  • Create a setter for the field in question, or relax the field's visibility.

  • Create a dependency-injecting override or static method that takes a DAO, and make the public instance method delegate to it. Test the more-flexible method instead.

    public String myMethod() { return myMethod(dao); }
    String myMethod(Dao dao) { /* real implementation here */ }
    
  • Add a constructor overload or static factory method that replaces private fields for the sake of testing.

  • Fully structure the class for dependency injection. (Sotirios Delimanolis, EJK)

Note that some of these can be package-private for testing, if you put your tests in the same Java package (possibly in a separate source tree). In all cases, good names and documentation are helpful to make your intentions clear.

Violate encapsulation

  • Use reflection to set private fields in the class. (kinbiko - see answer)
  • Use PowerMockito to replace the Dao constructor with a mock of your choice. (Dave Newton)

how about this?

public class MyClassTest {

    MyClass myClass = new MyClass();
    Dao dao = Mockito.mock(Dao.class);

    public void testMyMethod() {

        Field field = myClass.getClass().getDeclaredField("dao");
        field.setAccessible(true);
        field.set(myClass, dao);
        //Do the test...
    } 
}

EDIT: As mentioned in the comments this assumes that you don't change the name of the dao field. It might be a good idea then to get all the fields, Field[] fields = myClass.getClass().getDeclaredFields(); and iterate over them, getting the field(s) of type Dao. Then proceeding as above. This way your test doesn't depend on the name of your field anymore.