JUnit mocking with Mockito, EasyMock, etc

2019-05-06 16:51发布

问题:

I'm trying to mock a method of an object inside the class I'm testing.

For instance

class ClassToTest {
   public doSomething () {
       SomeObject a = new SomeObject ();
       a.doSomethingElse ();
   }
}

Is there a way to mock the methods of the variable "a"? I'd like doSomethingElse to do nothing during testing. I'm currently using Mockito but I'm open to any mocking framework.

Thanks

回答1:

Yes, there is a way, as shown by the following JMockit test:

public void testDoSomething(final SomeObject mock)
{
    new ClassToTest().doSomething();

    new Verifications() {{ mock.doSomethingElse(); }};
}

No need to refactor code under test to use a wrapper, DI, etc; simply mock whatever you need to be mocked.



回答2:

It's not possible to mock the reference "a" when it's declared as a local variable, as in your case. You could consider injecting the dependency to SomeObject, e.g. as a parameter of doSomething method. That way, you can inject a mock of SomeObject in your test instead.

One of the benefits of dependency injection is increased testability.



回答3:

With some refactoring it is possible, of course:

class SomeObject {
    public void doSomethingElse()
    {

    }
}

class ClassToTest
{
    private final SomeObject someObject;

    public void doSomething()
    {
        someObject.doSomethingElse();
    }

    public ClassToTest(SomeObject someObject)
    {
        this.someObject = someObject;
    }
}

class Test {
    @Test
    public void testDoSomething()
    {
        SomeObject someObject = Mockito.mock(SomeObject.class);
        new ClassToTest(someObject).doSomething();
        Mockito.verify(someObject, Mockito.atLeastOnce()).doSomethingElse();
    }
}


回答4:

I believe you can use EasyMock Class Extensions for EasyMock 2.5 or earlier, and apparently it is included in 3.0. See this part of the previous page for information on what you are trying to do. That said, I haven't personally tried to do that, so I don't know how well it will work.



回答5:

If you want a new instance in each call, I'd suggest refactoring in the following way:

class ClassToTest {
   public doSomething () {
      SomeObject a = getInstance();
      a.doSomethingElse ();
   }
   protected SomeObject getInstance() {
      return new SomeObject();
   }
}

Then you can create a testclass extending ClassToTest, overriding the getInstance() method, with one supplying a mock object.

This is of course only viable if you are ok with exposing the getInstance() method, so I don't recommend it if the class is part of a public API. If this is the case, consider supplying a factory class using dependency injection.



回答6:

class ClassToTest {
    private SomethingElseInterface somethingElseDoer ;

    public ClassToTest(SomethingElseInterface somethingElseDoer) {
        this.somethingElseDoer = somethingElseDoer;
    }

    public doSomething () {
        somethingElseDoer.doSomethingElse();
    }
}

And where you use it:

SomethingElseInterface somethingElseDoer = ...; // in a test, this is where you mock it
ClassToTest foo = new ClassToTest(somethingElseDoer); // inject through constructor
foo.doSomething();