2

I want to mock a Class in Mockito. It will then have a .newInstance() call issued which will be expected to return an actual class instance (and will return a mock in my case).

If it was setup correctly then I could do:

ArrayList myListMock = mock(ArrayList.class);
when(myVar.newInstance()).thenReturn(myListMock);

I know I can set it up so that a new instance of class ArrayList will be a mock (using PowerMockito whenNew), just wondering if there was a way to mock this kind of a class object so I don't have to override instance creation...

Below is the real class I'm trying to mock, I can't change the structure it is defined by the interface. What I'm looking for is a way to provide cvs when initialize is called.

public class InputConstraintValidator 
    implements ConstraintValidator<InputValidation, StringWrapper> {

    Class<? extends SafeString> cvs;

    public void initialize(InputValidation constraintAnnotation) {
        cvs = constraintAnnotation.inputValidator();
    }

    public boolean isValid(StringWrapper value, 
                   ConstraintValidatorContext context) {

        SafeString instance;
        try {
             instance = cvs.newInstance();
        } catch (InstantiationException e) {
            return false;
        } catch (IllegalAccessException e) {
            return false;
    }
}
Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
TheZuck
  • 3,513
  • 2
  • 29
  • 36

2 Answers2

0

Mockito is designed exclusively for mocking instances of objects. Under the hood, the mock method actually creates a proxy that receives calls to all non-final methods, and logs and stubs those calls as needed. There's no good way to use Mockito to replace a function on the Class object itself. This leaves you with a few options:

  1. I don't have experience with PowerMock but it seems it's designed for mocking static methods.

  2. In dependency-injection style, make your static factory method into a factory instance. Since it looks like you're not actually working with ArrayList, let's say your class is FooBar instead:

    class FooBar {
      static class Factory {
        static FooBar instance;
        FooBar getInstance() {
          if (instance == null) {
            instance = new FooBar();
          }
          return instance;
        }
      }
      // ...
    }
    

    Now your class user can receive a new FooBar.Factory() parameter, which creates your real FooBar in singleton style (hopefully better and more threadsafe than my simple implementation), and you can use pure Mockito to mock the Factory. If this looks like it's a lot of boilerplate, it's because it is, but if you are thinking of switching to a DI solution like Guice you can cut down a lot of it.

  3. Consider making a field or method package-private or protected and documenting that it's visible for testing purposes. Then you can insert a mocked instance in test code only.

    public class InputConstraintValidator implements 
        ConstraintValidator<InputValidation, StringWrapper> {
      Class<? extends SafeString> cvs;
    
      public void initialize(InputValidation constraintAnnotation) {
        cvs = constraintAnnotation.inputValidator();
      }
    
      public boolean isValid(StringWrapper value,
          ConstraintValidatorContext context) {
        SafeString instance;
        try {
          instance = getCvsInstance();
        } catch (InstantiationException e) {
          return false;
        } catch (IllegalAccessException e) {
          return false;
        }
      }
    
      @VisibleForTesting protected getCvsInstance()
          throws InstantiationException, IllegalAccessException {
        return cvs.newInstance();
      }
    }
    
    public class InputConstaintValidatorTest {
      @Test public void testWithMockCvs() {
        final SafeString cvs = mock(SafeString.class);
        InputConstraintValidator validator = new InputConstraintValidator() {
          @Override protected getCvsInstance() {
            return cvs;
          }
        }
        // test
      }
    }
    
Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • After a couple of months experience, I can say that I'm using PowerMock to mock instance creation and it's pretty easy and straight forward. – TheZuck Jan 13 '13 at 17:22
-1

I think you just need to introduce an additional mock for Class:

ArrayList<?> myListMock = mock(ArrayList.class);
Class<ArrayList> clazz = mock(Class.class);
when(clazz.newInstance()).thenReturn(myListMock);

Of course the trick is making sure your mocked clazz.newInstance() doesn't end up getting called all over the place because due to type-erasure you can't specify that it's actually a Class<ArrayList>.

Also, be careful defining your own mock for something as fundamental as ArrayList - generally I'd use a "real one" and populate it with mocks.

millhouse
  • 9,817
  • 4
  • 32
  • 40