8

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?

user1883212
  • 7,539
  • 11
  • 46
  • 82
  • 2
    Reflection or you'd need to use some instrumentation I believe . That one of the reasons Inversion of Control and Dependency Injection are so popular. They make testing very easy. – Sotirios Delimanolis Dec 18 '14 at 17:39
  • 1
    http://stackoverflow.com/questions/130794/what-is-dependency-injection – Sotirios Delimanolis Dec 18 '14 at 17:40
  • Have a look at reflection - it's great for testing, and it allows you to set any field of a class. – kinbiko Dec 18 '14 at 17:42
  • 1
    Use PowerMockito, stuff like MockConstructor, @PrepareForTest, etc., e.g., https://code.google.com/p/powermock/wiki/MockConstructor, http://stackoverflow.com/q/25160761/438992, https://code.google.com/p/powermock/wiki/MockitoUsage13, etc. – Dave Newton Dec 18 '14 at 17:45
  • @user1883212 - Do you have the option of modifying MyClass? If so then Dependency Injection is a good option as mentioned in the first 2 comments. If not, then Dave Newton's suggestion of using PowerMockito is an option. – EJK Dec 18 '14 at 17:47

2 Answers2

13

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

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
2

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.

kinbiko
  • 2,066
  • 1
  • 28
  • 40
  • Not a bad approach, but has the obvious limitation that if the name of the field ever changes, the compiler will not catch this. This test would then fail at runtime. – EJK Dec 18 '14 at 17:54