All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.foreach.common.test.MockedLoader Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014 the original author or authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.foreach.common.test;

import org.mockito.internal.stubbing.InvocationContainer;
import org.mockito.internal.util.MockUtil;
import org.mockito.invocation.Invocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.test.context.support.DelegatingSmartContextLoader;
import org.springframework.test.util.ReflectionTestUtils;

import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;

// Intellij has the bad idea to create TestClasses if you use AnnotationConfigContextLoaderDecorator
// So we'll just extend DelegatingSmartContextLoader like SpringJUnit4ClassRunner does

/**
 * 

MockedLoader can be used in unit tests to automatically create mocks for all autowired * beans that are not yet defined in the spring context. To be used in combination with * TestConfig classes. Example use:

*
 *  {@literal @}RunWith(SpringJUnit4ClassRunner.class)
 *  {@literal @}DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
 *  {@literal @}ContextConfiguration(classes = MyTest.TestConfig.class, loader = MockedLoader.class)
 *  public class MyTest {
 *      {@literal @}Autowired
 *      private ClassBeingTested classBeingTested;
 *      ...
 *
 *     {@literal @}Configuration
 *     public static class TestConfig {
 *       // Only the ClassBeingTested should be manually created with the actual implementation.
 *       // All autowired beans that the ClassBeingTested depends on, will be replaced by a mock
 *       // by the MockedLoader.  Only beans for which a specific implementation should be used
 *       // need to be specified in the TestConfig.
 *       {@literal @}Bean
 *       public ClassBeingTested classBeingTested() {
 *         return new ClassBeingTested();
 *       }
 *     }
 *  }
 * 
*

Note: MockedLoader is based on Mockito mocking.

*/ public class MockedLoader extends DelegatingSmartContextLoader { private static final Logger LOG = LoggerFactory.getLogger( MockedLoader.class ); private AnnotationConfigContextLoaderDecorator loader = new AnnotationConfigContextLoaderDecorator(); @SuppressWarnings("all") @Override public ApplicationContext loadContext( MergedContextConfiguration mergedConfig ) throws Exception { // Overwrite the annotationConfigLoader with out implementation ReflectionTestUtils.setField( this, "annotationConfigLoader", loader ); return super.loadContext( mergedConfig ); } @Override public void processContextConfiguration( ContextConfigurationAttributes configAttributes ) { Class[] classes = configAttributes.getClasses(); if( classes.length == 0 ) { // Allow empty loaders, in this case everything should be mocked configAttributes.setClasses( new Class[]{Object.class} ); } super.processContextConfiguration( configAttributes ); } private class AnnotationConfigContextLoaderDecorator extends AnnotationConfigContextLoader { @Override protected void prepareContext( GenericApplicationContext context ) { QualifierAnnotationAutowireCandidateResolver qualifierAnnotationAutowireCandidateResolver = new QualifierAnnotationAutowireCandidateResolver(); BeanFactoryDecorator beanFactoryDecorator = new BeanFactoryDecorator(); beanFactoryDecorator.setAutowireCandidateResolver( qualifierAnnotationAutowireCandidateResolver ); // Override the beanFactory with our custom implementation ReflectionTestUtils.setField( context, "beanFactory", beanFactoryDecorator ); super.prepareContext( context ); } } private static class BeanFactoryDecorator extends DefaultListableBeanFactory { private final Map mockedBeans = new HashMap(); @Override public void destroySingletons() { super.destroySingletons(); int mockedBeansWithoutInvocations = 0; int mockedBeansWithInvocations = 0; MockUtil mockUtil = new MockUtil(); for( Map.Entry entry : mockedBeans.entrySet() ) { Object mock = entry.getValue(); InvocationContainer container = mockUtil.getMockHandler( mock ).getInvocationContainer(); List invocations = container.getInvocations(); if( invocations.isEmpty() ) { mockedBeansWithoutInvocations++; } else { mockedBeansWithInvocations++; } reset( mock ); } LOG.debug( "*** MockedLoader stats: [" + mockedBeans.size() + "] mocked beans of which [" + mockedBeansWithInvocations + "] with invocations and [" + mockedBeansWithoutInvocations + "] without invocations" ); mockedBeans.clear(); } @Override public Object resolveDependency( DependencyDescriptor descriptor, String beanName, Set autowiredBeanNames, TypeConverter typeConverter ) { try { return super.resolveDependency( descriptor, beanName, autowiredBeanNames, typeConverter ); } catch ( NoSuchBeanDefinitionException noSuchBeanDefinitionException ) { Class dependencyType = descriptor.getDependencyType(); if( Modifier.isFinal( dependencyType.getModifiers() ) ) { throw new NoSuchBeanDefinitionException( dependencyType, "Cannot create an automatic mock for final type: " + dependencyType ); } Object mockedBean = mockedBeans.get( dependencyType ); if( mockedBean == null ) { LOG.debug( "Did not find a mocked bean for type {}", dependencyType ); mockedBean = mock( dependencyType ); // We could actually also try to instantiate the Impl if we feel the need mockedBeans.put( dependencyType, mockedBean ); } else { LOG.debug( "returning mocked bean for type {}", dependencyType ); } return mockedBean; } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy