-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue 23 fix verify in advised beans (#29)
- Loading branch information
1 parent
1e9be00
commit 20fbc5d
Showing
9 changed files
with
223 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 15 additions & 2 deletions
17
src/main/java/com/teketik/test/mockinbean/BeanFieldState.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,34 @@ | ||
package com.teketik.test.mockinbean; | ||
|
||
import org.springframework.test.context.TestContext; | ||
import org.springframework.util.ReflectionUtils; | ||
|
||
import java.lang.reflect.Field; | ||
|
||
class BeanFieldState extends FieldState { | ||
|
||
private Object bean; | ||
final Object bean; | ||
|
||
final Object originalValue; | ||
|
||
public BeanFieldState(Object bean, Field field, Object originalValue, Definition definition) { | ||
super(field, originalValue, definition); | ||
super(field, definition); | ||
this.bean = bean; | ||
this.originalValue = originalValue; | ||
} | ||
|
||
@Override | ||
public Object resolveTarget(TestContext testContext) { | ||
return bean; | ||
} | ||
|
||
public void rollback(TestContext testContext) { | ||
final Object target = resolveTarget(testContext); | ||
ReflectionUtils.setField(field, target, originalValue); | ||
} | ||
|
||
public Object createMockOrSpy() { | ||
return definition.create(originalValue); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
src/main/java/com/teketik/test/mockinbean/ProxiedBeanFieldState.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.teketik.test.mockinbean; | ||
|
||
import org.springframework.aop.TargetSource; | ||
import org.springframework.test.context.TestContext; | ||
import org.springframework.test.util.ReflectionTestUtils; | ||
|
||
import java.lang.reflect.Field; | ||
|
||
/** | ||
* Special kind of {@link BeanFieldState} handling proxied beans (like aspects).<br> | ||
* The mock is not injected into the <code>field</code> but into the <code>target</code> of its {@link TargetSource}. | ||
* @author Antoine Meyer | ||
* @see https://github.com/antoinemeyer/mock-in-bean/issues/23 | ||
*/ | ||
class ProxiedBeanFieldState extends BeanFieldState { | ||
|
||
private static void setTargetSourceValue(TargetSource targetSource, Object value) { | ||
ReflectionTestUtils.setField(targetSource, "target", value); | ||
} | ||
|
||
final TargetSource proxyTargetSource; | ||
|
||
final Object proxyTargetOriginalValue; | ||
|
||
public ProxiedBeanFieldState(Object inBean, Field beanField, Object beanFieldValue, TargetSource proxyTargetSource, Definition definition) throws Exception { | ||
super(inBean, beanField, beanFieldValue, definition); | ||
this.proxyTargetSource = proxyTargetSource; | ||
this.proxyTargetOriginalValue = proxyTargetSource.getTarget(); | ||
} | ||
|
||
@Override | ||
public void rollback(TestContext testContext) { | ||
setTargetSourceValue(proxyTargetSource, proxyTargetOriginalValue); | ||
} | ||
|
||
@Override | ||
public Object createMockOrSpy() { | ||
Object applicableMockOrSpy = definition.create(proxyTargetOriginalValue); | ||
setTargetSourceValue(proxyTargetSource, applicableMockOrSpy); | ||
return originalValue; //the 'mock or spy' to operate for proxied beans are the actual proxy | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 113 additions & 0 deletions
113
src/test/java/com/teketik/test/mockinbean/test/VerifyAdvisedSpyInBeanTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package com.teketik.test.mockinbean.test; | ||
|
||
import static org.mockito.Mockito.verify; | ||
|
||
import com.teketik.test.mockinbean.SpyInBean; | ||
import com.teketik.test.mockinbean.test.VerifyAdvisedSpyInBeanTest.Config.AnAspect; | ||
import com.teketik.test.mockinbean.test.VerifyAdvisedSpyInBeanTest.Config.LoggingService; | ||
import com.teketik.test.mockinbean.test.VerifyAdvisedSpyInBeanTest.Config.ProviderService; | ||
|
||
import org.aspectj.lang.annotation.Aspect; | ||
import org.aspectj.lang.annotation.Before; | ||
import org.junit.jupiter.api.Assertions; | ||
import org.junit.jupiter.api.Test; | ||
import org.mockito.Mockito; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.context.ApplicationContext; | ||
import org.springframework.core.Ordered; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.test.context.TestContext; | ||
import org.springframework.test.context.TestExecutionListener; | ||
import org.springframework.test.context.TestExecutionListeners; | ||
import org.springframework.test.context.TestExecutionListeners.MergeMode; | ||
import org.springframework.test.util.ReflectionTestUtils; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
/** | ||
* Covering test case from https://github.com/inkassso/mock-in-bean-issue-23/blob/master/src/test/java/com/github/inkassso/mockinbean/issue23/service/BrokenLoggingServiceTest1_SpyInBean.java | ||
*/ | ||
@TestExecutionListeners(value = {VerifyAdvisedSpyInBeanTest.class}, mergeMode = MergeMode.MERGE_WITH_DEFAULTS) | ||
@SpringBootTest | ||
public class VerifyAdvisedSpyInBeanTest implements TestExecutionListener, Ordered { | ||
|
||
@org.springframework.boot.test.context.TestConfiguration | ||
static class Config { | ||
|
||
@Aspect | ||
@Component | ||
public class AnAspect { | ||
|
||
private final AtomicInteger invocationCounter = new AtomicInteger(); | ||
|
||
@Before("execution(* com.teketik.test.mockinbean.test.VerifyAdvisedSpyInBeanTest.Config.ProviderService.provideValue())") | ||
public void run() { | ||
invocationCounter.incrementAndGet(); | ||
} | ||
} | ||
|
||
@Service | ||
public class ProviderService { | ||
public String provideValue() { | ||
return ""; | ||
} | ||
} | ||
|
||
@Component | ||
public class LoggingService { | ||
|
||
@Autowired | ||
private ProviderService providerService; | ||
|
||
public String logCurrentValue() { | ||
return providerService.provideValue(); | ||
} | ||
} | ||
} | ||
|
||
@Autowired | ||
protected LoggingService loggingService; | ||
|
||
@Autowired | ||
protected AnAspect anAspect; | ||
|
||
@SpyInBean(LoggingService.class) | ||
private ProviderService providerService; | ||
|
||
@Test | ||
void testAspectInvocation() { | ||
int initialCounterValue = anAspect.invocationCounter.get(); | ||
loggingService.logCurrentValue(); | ||
Assertions.assertEquals(initialCounterValue + 1, anAspect.invocationCounter.get()); | ||
verify(providerService).provideValue(); | ||
Assertions.assertEquals(initialCounterValue + 2, anAspect.invocationCounter.get()); | ||
} | ||
|
||
@Test | ||
void testSpyAnswer() { | ||
Mockito.doAnswer(i -> "value").when(providerService).provideValue(); | ||
Assertions.assertEquals("value", loggingService.logCurrentValue()); | ||
} | ||
|
||
@Override | ||
public void afterTestClass(TestContext testContext) throws Exception { | ||
final ApplicationContext applicationContext = testContext.getApplicationContext(); | ||
|
||
//ensure context clean | ||
final Object loggingServiceBean = applicationContext.getBean(LoggingService.class); | ||
final Object providerServiceInBean = ReflectionTestUtils.getField(loggingServiceBean, "providerService"); | ||
Assertions.assertFalse(TestUtils.isMockOrSpy(providerServiceInBean)); | ||
Assertions.assertSame(applicationContext.getBean(ProviderService.class), providerServiceInBean); | ||
|
||
//ensure aspect invoked | ||
final AnAspect anAspect = applicationContext.getBean(AnAspect.class); | ||
Assertions.assertEquals(4, anAspect.invocationCounter.get()); | ||
} | ||
|
||
@Override | ||
public int getOrder() { | ||
return Integer.MAX_VALUE; | ||
} | ||
} |