问题
Is there any way of replacing the logic within a private method when running a JUnit test?
A bit of background: we have some private methods which interact with bundles within an OSGi container. This is not available in the unit test therefore the methods will fail.
We have looked at JMockIt but the method replace functionality seems to want to force you to replace all the methods in the class which call one another.
The implementation would be something like this:
public final doSomething() {
firstThing();
secondThing();
}
private firstThing() {
// normal code
}
private secondThing() {
// code which is unavailable in a unit test
}
And the unit test would specify the new implementation of secondThing():
// replace secondThing() in impl with this secondThing()
private secondThing() {
// dummy code
}
// run tests
回答1:
You certainly can solve this situation with JMockit. One way would be to define a "mock-up" class, for example:
public class MyTest
{
@Test
public void testDoSomething()
{
new MockUp<ClassWhichDependsOnOtherBundles>()
{
@Mock
void secondThing()
{
// do anything here
}
};
new ClassWhichDependsOnOtherBundles().doSomething();
}
}
Only the secondThing() method in the mocked class will be replaced by JMockit.
The JMockit Expectations API could also be used, with partial mocking.
回答2:
You are coupling your implementation to the creation of the osgi object (doing it inside secondThing() or the class itself). If you passed the implementation into your class from the outside, you could use a stub/mock when testing instead.
回答3:
I also think dependency injection would solve this problem. If you don't want another framework in your project and this is the only place which makes trouble, you could define an interface for secondThing and have 2 implementations for that, one for the original code and an empty one for the unittest.
回答4:
My advice - redesign your application. If you want to change the behaviour of a private method:
- make it
protected/publicand override it in a mock-like object - move the functionality out of the method into a helper class, which is injectable (via dependency injection). Then mock that helper an inject the mock into the class-under-test, instead of the original heloper.
A workaround may be some byte-code manipulation technique, but I don't recommend such.
回答5:
there is a nice stub-pattern
ProductionClass.java:
public class ProductionClass {
...
//default visibility to make it available for stubbing
void firstThing(){...}
...
}
BlaTest.java (same package as production class):
public class BlaTest {
@Test
void test_xx(){
//use stubbed impl
ProductionClass sut = new ProductionClassStubbed();
sut.doSomething();
}
}
class ProductionClassStubbed extends ProductionClass{
@Override
void firstThing(){
//stub out fill in what you want (like dummy behaviour)
}
}
One different thing. I saw a final modifier in your sample-code. Beware of using final modifier. They are evil for testability. Only use if really really necessary.
来源:https://stackoverflow.com/questions/2020254/java-unit-test-replace-a-private-method-under-test