mock or stub for chained call

JavaUnit TestingMockingMockito

Java Problem Overview


protected int parseExpire(CacheContext ctx) throws AttributeDefineException {
	Method targetMethod = ctx.getTargetMethod();
	CacheEnable cacheEnable = targetMethod.getAnnotation(CacheEnable.class);
	ExpireExpr cacheExpire = targetMethod.getAnnotation(ExpireExpr.class);
	// check for duplicate setting
	if (cacheEnable.expire() != CacheAttribute.DO_NOT_EXPIRE && cacheExpire != null) {
		throw new AttributeDefineException("expire are defined both in @CacheEnable and @ExpireExpr");
	}
	// expire time defined in @CacheEnable or @ExpireExpr
	return cacheEnable.expire() != CacheAttribute.DO_NOT_EXPIRE ? cacheEnable.expire() : parseExpireExpr(cacheExpire, ctx.getArgument());
}

that is the method to test ,

Method targetMethod = ctx.getTargetMethod();
CacheEnable cacheEnable = targetMethod.getAnnotation(CacheEnable.class);

I have to mock three CacheContext,Method and CacheEnable. Is there any idea to make the test case much simpler?

Java Solutions


Solution 1 - Java

Mockito can handle chained stubs:

Foo mock = mock(Foo.class, RETURNS_DEEP_STUBS);

// note that we're stubbing a chain of methods here: getBar().getName()
when(mock.getBar().getName()).thenReturn("deep");

// note that we're chaining method calls: getBar().getName()
assertEquals("deep", mock.getBar().getName());

AFAIK, the first method in the chain returns a mock, which is set up to return your value on the second chained method call.

Mockito's authors note that this should only be used for legacy code. A better thing to do otherwise is to push the behavior into your CacheContext and provide any information it needs to do the job itself. The amount of information you're pulling from CacheContext suggests that your class has feature envy.

Solution 2 - Java

Just in case you are using Kotlin. MockK doesn't say anything about chaining being a bad practice and easily allows you to do this.

val car = mockk<Car>()

every { car.door(DoorType.FRONT_LEFT).windowState() } returns WindowState.UP

car.door(DoorType.FRONT_LEFT) // returns chained mock for Door
car.door(DoorType.FRONT_LEFT).windowState() // returns WindowState.UP

verify { car.door(DoorType.FRONT_LEFT).windowState() }

confirmVerified(car)

Solution 3 - Java

My suggestion to make your test case simpler is to refactor your method.

Anytime I find myself having trouble testing a method, it's a code smell for me, and I ask why is it hard to test. And if code is hard to test, it's probably hard to use and maintain.

In this case it's because you have a method chain that goes several levels deep. Perhaps pass in ctx, cacheEnable, and cacheExpire as parameters.

Solution 4 - Java

I found JMockit easier to use ans switched to it completely. See test cases using it:

https://github.com/ko5tik/andject/blob/master/src/test/java/de/pribluda/android/andject/ViewInjectionTest.java

Here I mock away Activity base class, which is coming from Android SKD and completely stubbed. With JMockit you can mock thingis tha are final, private, abstract or whatever else.

In your testcase it would look like:

public void testFoo(@Mocked final Method targetMethod, 
                    @Mocked  final CacheContext context,
                    @Mocked final  CacheExpire ce) {
    new Expectations() {
       {
           // specify expected sequence of infocations here

           context.getTargetMethod(); returns(method);
       }
    };

    // call your method
    assertSomething(objectUndertest.cacheExpire(context))

Solution 5 - Java

Expanding on Lunivore's answer, for anyone injecting a mocked bean, use:

@Mock(answer=RETURNS_DEEP_STUBS)
private Foo mockedFoo;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjilenView Question on Stackoverflow
Solution 1 - JavaLunivoreView Answer on Stackoverflow
Solution 2 - JavayuranosView Answer on Stackoverflow
Solution 3 - JavaDoug RView Answer on Stackoverflow
Solution 4 - JavaKonstantin PribludaView Answer on Stackoverflow
Solution 5 - JavaJoseph CassView Answer on Stackoverflow