3

I have a class called MyClass. I have a method in that class, public int getSpacing().

I am testing a method from a different class. public static ArrayList<SpecialObject> getSpecialObjects().

getSpecialObjects() calls getSpacing() on an instance of MyClass. I want this method to always return 0 when I am running a test with Mockito. How can I do this? All the examples I have found require a particular instance of an object before you can mock a method.

This is what I know how to do.

MyClass myInstance = new MyClass();        
Mockito.when(myInstance.getSpacing()).thenReturn(0);

However, I want to override the behavior of getSpacing() for ALL instances of MyClass, because the instances are created dynamically in getSpecialObjects(). How can I do this?

This is what I want to say but don't know how.

Mockito.when((Any instance of MyClass).getSpacing()).thenReturn(0);
Tyler Pfaff
  • 4,900
  • 9
  • 47
  • 62

2 Answers2

1

You can mock the MyClass constructor using powermock like so:

MyClass myClassSpy = spy(new MyClass());
whenNew(MyClass.class).withAnyArguments().thenReturn(myclassSpy);

Then you can return whatever you like from that method using the usual overrides of the spy. Don't forget to PrepareForTest the MyClass

Slava Shpitalny
  • 3,965
  • 2
  • 15
  • 22
1

You can't do this purely in Mockito as you described. Mockito doesn't control all objects of type MyClass, but instead makes it very easy to create and instantiate a proxy subclass it can control. This is why Mockito counts on classes and methods being non-final, and why you won't be able to use Mockito to affect behavior of all instances.

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

One workaround is to switch libraries: As Slava describes, you can use PowerMock, which has a custom classloader that modifies bytecode, which includes replacing calls to the new MyClass() constructor so they can return your mocks instead.

As an alternative, you can restructure your class to make it easier to test. You could fully structure the class for dependency injection and inject a Provider<MyClass>, but there's a more-local solution with most of the benefits:

public class ClassThatConsumesMyClass {
  /** Visible for testing. Override to customize MyClass instances. */
  MyClass createMyClass() {
    return new MyClass();
  }

  public ReturnValue getSpecialObjects(int n) {
    for (int i = 0; i < n; i++) {
      // Instead of calling "new MyClass()", call your method.
      MyClass myClassInstance = createMyClass();
      // ...
    }
    // ...
  }
}

public class TestForClassThatConsumesMyClass {
  ClassThatConsumesMyClass consumer;

  @Before public void createMyClass() {
    consumer = new ClassThatConsumesMyClass() {
      @Override MyClass createMyClass() {
        MyClass myInstance = Mockito.mock(MyClass.class);
        Mockito.when(myInstance.getSpacing()).thenReturn(0);
        return myInstance;
      }
    }
  }

  // Test as usual, now that you've mocked every newly-created
  // instance of MyClass in your consumer.
}
Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251