简体   繁体   中英

Mock static function of a class

I am implementing one class X which has one method xyz() which uses static method abc() of a class A. This static method abc() takes return value of another static method of same class which takes 2 arguments. One is HTTPServletRequest object and other is value of one enum. I want to test functionality of my method xyz() of class X, for which I thought of using PowerMockRunner to mock xyz() method of class A. But I am facing problem here and not able to do so. 'Except' clause is not working and thus it is not returning value that I want it to return for normal test to proceed. Please advise.

@RunWith(PowerMockRunner.class)
@PrepareForTest(A.class)
public class DataProviderIntegrationTest {
@Mock
 private HttpServletRequest request;

 @TestSubject
 X x= new X();

 @Test
 public void testXyz() throws Exception {

 long customerId = 1;
 expect(A.abc(A.pqr(request, B.CUSTOMER_ID.getAttributeValue()))).andReturn(customerId);
 replayAll();
 Output output = x.xyz();

 Assert.assertNotNull(output);
 }
}

Although you are asking for how to mock a static method, I will give you an answer for your real problem. I think, you have in fact an XY problem .

So, as you are testing one of your methods, you have the opportunity to greatly improve your code under test. You obviously already realized that a static method can be a pain in testing. What to do?

Simple answer: Express the needed functionality with an interface!

I'll give you an example with some static methods from the Java runtime library. You can easily transport the mechanism to your code. Let's use the Math.random() method which is static. The method that shall be tested:

final class MyClass {
    double myMethod(double multiplier) {
        double result = Math.random() * multiplier;
        return result;
    }
}

The call to the random functionality is in fact a dependency of your class where this code belongs to. So we first create an interface carrying this functionality:

interface Randomizer {
    double random();
}

Additionally, we create a first implementation for it:

final class DefaultRandomizer implements Randomizer {
    @Override
    public double random() {
        return Math.random();
    }
}

Now we can rewrite the class to:

final class MyClass {
    private final Randomizer randomizer;

    MyClass() {
        this(new DefaultRandomizer());
    }

    MyClass(Randomizer randomizer) {
        this.randomizer = Objects.requireNonNull(randomizer);
    }

    double myMethod(double multiplier) {
        double result = randomizer.random() * multiplier;
        return result;
    }
}

As you can see object of that class are now instantiated by providing them the needed Randomizer . The parameterless constructor is a simple way for providing a default one. It could be omitted, but then some other parts of your code must be changed, too.

And the test now becomes very easy. You simply create a test implementation of the interface Randomizer with which you instantiate the class. Doing so gives you full control over every aspect in the test method.

An example for such a test:

public final class MyClassTest {

    @Test
    public void zeroRandomShouldReturnZeroForPositiveMultiplier() {
        Randomizer zeroRandomizer = new Randomizer() {
            @Override
            public double random() {
                return 0.0;
            }
        };
        MyClass testObject = new MyClass(zeroRandomizer);
        double multiplier = 42.0;
        assertThat(testObject.myMethod(multiplier), is(0.0));
    }

}

The creation of the test implementation can be easily done with Mockito (I do it also for these purposes). But ... a mocking framework is not necessary for this scenario. In fact, this isn't called mocking , it is simple stubbing .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM