mockito / mockito

Most popular Mocking framework for unit tests written in Java
http://mockito.org
MIT License
14.88k stars 2.56k forks source link

Mocks are not injected in Spring AOP proxies #209

Closed eulogi closed 9 years ago

eulogi commented 9 years ago

Mocks are not injected in fields that are Spring AOP proxies. To solve it you can modify PropertyAndSetterInjection:

public boolean processInjection(Field injectMocksField, Object injectMocksFieldOwner, Set<Object> mockCandidates) {
    // Set<Object> mocksToBeInjected = new HashSet<Object>(mockCandidates);
    FieldInitializationReport report = initializeInjectMocksField(injectMocksField, injectMocksFieldOwner);

    // for each field in the class hierarchy
    boolean injectionOccurred = false;
    Class<?> fieldClass = report.fieldClass();
    Object fieldInstanceNeedingInjection = report.fieldInstance();
    // Unwrap proxy to allow field injection
    fieldInstanceNeedingInjection = unwrapProxy(fieldInstanceNeedingInjection);
    fieldClass = fieldInstanceNeedingInjection.getClass();
    while (fieldClass != Object.class) {
        injectionOccurred |= injectMockCandidates(fieldClass, newMockSafeHashSet(mockCandidates), fieldInstanceNeedingInjection);
        fieldClass = fieldClass.getSuperclass();
    }
    return injectionOccurred;
}

public static Object unwrapProxy(Object bean) {
    if (AopUtils.isAopProxy(bean) && bean instanceof Advised) {
        Advised advised = (Advised) bean;
        try {
            bean = advised.getTargetSource().getTarget();
        } catch (Exception e) {
            LOG.error("Exception unwrapping proxy object", e);
        }
    }
    return bean;
}
bric3 commented 9 years ago

Hi,

Thanks for providing a solution however we won't include Spring specific code in mockito. This seems to be a problem quite specific to how is the test crafted. This is probably wrong to create an object then inject mocks (then overriding exisiting wired dependencies), a better approach would probably be to let spring wire the mocks.

elreydetodo commented 8 years ago

FYI, I've run into this too and have filed an upstream issue with Spring:

https://jira.spring.io/browse/SPR-14083

ghost commented 8 years ago

I have also run into this issue, and while it is rare, rewriting the test to no use @InjectMocks does make the configuration quite a bit more verbose. It appears like the fundamental flaw in the injection filtering is that there is an assumption that there is a one-to-one between a field and a getter/setter pair. When a proxy is in usage, particularly a CGLIB proxy, this is obviously not the case.

A solution could be provided that did not couple to Spring, but I believe it would require adjusting the filtering strategy for setter injection to find candidate injectables but setter method declaration, not field declaration.

marcingrzejszczak commented 8 years ago

You can use the new Spring Boot feature - http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-mocking-beans