问题
Here is the scenario, I have something like this in one of my method of a class MyClass
public class MyClass{
public Object build(Map map) {
BaseClass cls;
if(SomeconditionTrue) {
cls = new ChildClass1(new ABC());
} else {
cls = new ChildClass2(new ABC());
}
cls.callMethod();
}
}
For the above scenario, I am writing a test case using PowerMockito, I want to mock this method call, cls.callMethod()
. When i am trying to mock, it calls the actual Method callMethod()
which is failing. can some body please help me to mock that method call? Tried using couple of scenarios using PowerMockito PowerMockito.stub and some of other options, but its always calling the actual method. The reason to mock this method is, it has a different logic all together and it makes calls to different APIs, the method is quite complex, hence we need to mock this method.
回答1:
You can turn to Powermock for this, but that is not necessarily the "best" answer to such problems.
In essence, the fact that you are calling new in your code gives you grief - you created hard-to-test code by that. You might watch these videos to understand what I am talking about.
Long story short: instead of turning to the big Powermock hammer you could instead rework your code; to use dependency injection. So instead of creating these objects itself, your class under test could use some factory object to provide that object it needs. And then you can use any "ordinary" mocking framework such as EasyMock or Mockito to create a mocked version of that factory.
Edit: I think you might have over-complicated the whole issue. You see, it doesn't really matter that there is a base class, or two child classes. Thing is: for each of your test methods you should exactly understand which path will be taken. You either want that a child1 is created, or a child. Thus I created a simplified solution for you:
package ghostcat.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
class Abc {}
abstract class Base {
void callMethod() {
System.out.println("Base::callMethod");
}
}
class ChildClass1 extends Base {
ChildClass1(Abc abc) {}
}
class MyClass {
public Object build() {
System.out.println("build1");
Base cls = new ChildClass1(new Abc());
System.out.println("build2");
cls.callMethod();
System.out.println("build3");
return null;
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest(MyClass.class)
public class MockNewTest {
@Test
public void test() throws Exception {
ChildClass1 mock = Mockito.mock(ChildClass1.class);
PowerMockito.whenNew(ChildClass1.class).withArguments(Mockito.any(Abc.class)).thenReturn(mock);
new MyClass().build();
}
}
Prints:
build1
build2
build3
So you can see - nothing in base is called; simply because that child object is "fully" mocked.
The whole point is: your code under test simply requires one object of a certain type; and you know in advance whether that will be child1 or child2. So you just create a mock for that class; and use PowerMockito in order return that mock you just created.
For the record: I played with spies for some time; but they dont help here; and they are also not required!
来源:https://stackoverflow.com/questions/41169684/mocking-of-a-constructor-which-gives-the-base-class-reference-using-powermockito