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

org.xwiki.test.jmock.AbstractMockingComponentTestCase Maven / Gradle / Ivy

There is a newer version: 16.10.0-rc-1
Show newest version
/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.test.jmock;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.Before;
import org.slf4j.Logger;
import org.xwiki.component.annotation.ComponentAnnotationLoader;
import org.xwiki.component.annotation.ComponentDeclaration;
import org.xwiki.component.annotation.ComponentDescriptorFactory;
import org.xwiki.component.descriptor.ComponentDependency;
import org.xwiki.component.descriptor.ComponentDescriptor;
import org.xwiki.component.descriptor.DefaultComponentDescriptor;
import org.xwiki.component.internal.RoleHint;
import org.xwiki.component.manager.ComponentLookupException;
import org.xwiki.component.util.ReflectionUtils;
import org.xwiki.test.annotation.AllComponents;
import org.xwiki.test.annotation.ComponentList;
import org.xwiki.test.jmock.annotation.MockingRequirement;
import org.xwiki.test.jmock.annotation.MockingRequirements;

/**
 * To use this class, annotate your test class with {@link org.xwiki.test.jmock.annotation.MockingRequirement},
 * passing the implementation class you're testing.
 * Then in your test code, do a lookup of your component under test and you'll get a component instance which has all
 * its injected dependencies mocked automatically. For example:
 * 
{@code
 * @MockingRequirement(MyComponentImplementation.class)
 * public class MyComponentTest extends AbstractMockingComponentTestCase
 * {
 *     @Test
 *     public void someTest() throws Exception
 *     {
 *         MyComponent myComponent = getMockedComponent();
 *         ...
 *     }
 * ...
 * }
 * }
* * Note that by default there are no component registered against the component manager except those mocked * automatically by the {@code @MockingRequirement} annotation. This has 2 advantages: *
    *
  • This is the spirit of AbstractMockingComponentTestCase since they're supposed to mock all dependencies and * define their behaviors
  • *
  • It makes the tests up to 10 times faster
  • *
* If you really need to register some components, use the {@link ComponentList} annotation and if you really really * need to register all components (it takes time) then use {@link AllComponents}. * * @version $Id: 9ecc709aac92cdaa77e83c05843a3c51c3e72128 $ * @since 2.2M1 * @deprecated use {@link org.xwiki.test.junit5.mockito.ComponentTest} instead */ @Deprecated(since = "4.3.1") public abstract class AbstractMockingComponentTestCase extends AbstractMockingTestCase { private MockingComponentManager componentManager; private ComponentAnnotationLoader loader = new ComponentAnnotationLoader(); private ComponentDescriptorFactory factory = new ComponentDescriptorFactory(); private Map mockLoggers = new HashMap<>(); private Map> mockedComponents = new HashMap<>(); /** * Extend EmbeddableComponentManager in order to mock Loggers since they're handled specially and are not * components. */ private final class LogSpecificMockingComponentManager extends MockingComponentManager { private List> mockedComponentClasses = new ArrayList<>(); public void addMockedComponentClass(Class mockedComponentClass) { this.mockedComponentClasses.add(mockedComponentClass); } @Override protected Object createLogger(Class instanceClass) { Object logger; if (this.mockedComponentClasses.contains(instanceClass)) { logger = getMockery().mock(Logger.class, instanceClass.getName()); AbstractMockingComponentTestCase.this.mockLoggers.put(instanceClass, (Logger) logger); } else { logger = super.createLogger(instanceClass); } return logger; } } /** * @return the first component mocked by a {@link MockingRequirement} annotation * @since 4.2M3 */ public T getMockedComponent() throws ComponentLookupException { if (this.mockedComponents.size() == 0) { throw new RuntimeException("You need to have at least one @MockingRequirement annotation!"); } if (this.mockedComponents.size() > 1) { throw new RuntimeException("When there are several @MockingRequirement annotations you muse use the " + "getMockedComponent(mockedComponentClass) signature!"); } else { return getMockedComponent(this.mockedComponents.keySet().iterator().next()); } } /** * @param mockedComponentClass the class of the mocked component to return * @return the component mocked by a {@link MockingRequirement} annotation that is of the passed class type * @since 4.2M3 */ public T getMockedComponent(Class mockedComponentClass) throws ComponentLookupException { RoleHint roleHint = this.mockedComponents.get(mockedComponentClass); return this.componentManager.getInstance(roleHint.getRoleType(), roleHint.getHint()); } /** * @return the Mock Logger if the Component under Test has requested an Injection of a Logger or null otherwise */ public Logger getMockLogger(Class mockedComponentClass) throws Exception { // Note: the test class must get the mocked component instance before calling this method since this is this // action that injects the mock loggers... Note that we cannot call the mocked component here since the // component can be per-lookup and we would get a different instance... return this.mockLoggers.get(mockedComponentClass); } /** * @return the Mock Logger if the Component under Test has requested an Injection of a Logger or null otherwise */ public Logger getMockLogger() throws Exception { // Note: the test class must get the mocked component instance before calling this method since this is this // action that injects the mock loggers... Note that we cannot call the mocked component here since the // component can be per-lookup and we would get a different instance... if (this.mockLoggers.size() == 1) { return this.mockLoggers.values().iterator().next(); } else if (this.mockLoggers.size() == 0) { throw new RuntimeException("You have excluded the Logger from being mocked in your @MockingRequirement!"); } else { throw new RuntimeException("When there are several @MockingRequirement annotations you muse use the " + "getMockLogger(mockRequirementInstanceClass) signature!"); } } @Before public void setUp() throws Exception { this.componentManager = new LogSpecificMockingComponentManager(); // Step 1: Register the components that are needed by the tests. registerComponents(); // Step 2: Create MockRequirement instances and register them as components for (MockingRequirement mockingRequirement : getMockingRequirements()) { List> exclusions = Arrays.asList(mockingRequirement.exceptions()); // Mark the component class having its deps mocked so that our MockingEmbeddableComponentManager will // serve a mock Logger (but only if the Logger class is not in the exclusion list) if (!exclusions.contains(Logger.class)) { ((LogSpecificMockingComponentManager) this.componentManager) .addMockedComponentClass(mockingRequirement.value()); } // Handle component fields Type componentRoleType = findComponentRoleType(mockingRequirement); for (ComponentDescriptor descriptor : this.factory.createComponentDescriptors(mockingRequirement.value(), componentRoleType)) { // Only use the descriptor for the specified hint if ((mockingRequirement.hint().length() > 0 && mockingRequirement.hint().equals( descriptor.getRoleHint())) || mockingRequirement.hint().length() == 0) { registerMockDependencies(descriptor, exclusions); getComponentManager().registerComponent(descriptor); // Save the mocked component information so that the test can get an instance of this component // easily by calling getMockedComponent(...) this.mockedComponents.put(mockingRequirement.value(), new RoleHint<>(descriptor.getRoleType(), descriptor.getRoleHint())); break; } } } } private List getMockingRequirements() { MockingRequirements mockingRequirementsAnnotation = getClass().getAnnotation(MockingRequirements.class); List mockingRequirements; if (mockingRequirementsAnnotation != null) { mockingRequirements = Arrays.asList(mockingRequirementsAnnotation.value()); } else { MockingRequirement mockingRequirementAnnotation = getClass().getAnnotation(MockingRequirement.class); if (mockingRequirementAnnotation != null) { mockingRequirements = Collections.singletonList(mockingRequirementAnnotation); } else { throw new RuntimeException("When extending " + AbstractMockingComponentTestCase.class.getSimpleName() + " you must have at least one @" + MockingRequirement.class.getSimpleName() + "."); } } return mockingRequirements; } /** * If the user has specified the {@link org.xwiki.test.annotation.AllComponents} annotation then all components * are lodaded; however this is not recommended since it slows down the execution time and makes the test less * controlled; we recommend instead to use the {@link ComponentList} annotation which only registers the component * implementation you pass to it; or even better don't use any annotation which means no component is registered * explicitly which should be the default since you're mocking all component dependencies with the * {@link MockingRequirement} annotation. */ private void registerComponents() { AllComponents allComponentsAnnotation = this.getClass().getAnnotation(AllComponents.class); if (allComponentsAnnotation != null) { this.loader.initialize(this.componentManager, getClass().getClassLoader()); } else { ComponentList componentListAnnotation = this.getClass().getAnnotation(ComponentList.class); if (componentListAnnotation != null) { List componentDeclarations = new ArrayList<>(); for (Class componentClass : componentListAnnotation.value()) { componentDeclarations.add(new ComponentDeclaration(componentClass.getName())); } this.loader.initialize(this.componentManager, getClass().getClassLoader(), componentDeclarations); } } } private void registerMockDependencies(ComponentDescriptor descriptor, List> exceptions) throws Exception { Collection> dependencyDescriptors = descriptor.getComponentDependencies(); for (ComponentDependency dependencyDescriptor : dependencyDescriptors) { Class roleTypeClass = ReflectionUtils.getTypeClass(dependencyDescriptor.getRoleType()); // Only register a mock if it isn't: // - An explicit exception specified by the user // - A logger // - A collection of components, we want to keep them as Java collections. Those collections are later // filled by the component manager with available components. Developers can register mocked components // in an override of #setupDependencies(). // TODO: Handle multiple roles/hints. if (!exceptions.contains(roleTypeClass) && Logger.class != roleTypeClass && !roleTypeClass.isAssignableFrom(List.class) && !roleTypeClass.isAssignableFrom(Map.class)) { DefaultComponentDescriptor cd = new DefaultComponentDescriptor(); cd.setRoleType(dependencyDescriptor.getRoleType()); cd.setRoleHint(dependencyDescriptor.getRoleHint()); this.componentManager.registerComponent( cd, getMockery().mock(ReflectionUtils.getTypeClass(dependencyDescriptor.getRoleType()), dependencyDescriptor.getName())); } } } private Type findComponentRoleType(MockingRequirement mockingRequirement) { Type componentRoleType; Set componentRoleTypes = this.loader.findComponentRoleTypes(mockingRequirement.value()); Class role = mockingRequirement.role(); if (!Object.class.getName().equals(role.getName())) { if (!componentRoleTypes.contains(role)) { throw new RuntimeException("Specified Component Role not found in component"); } else { componentRoleType = role; } } else { if (componentRoleTypes.isEmpty()) { throw new RuntimeException( String.format("Couldn't find roles for component [%s]", mockingRequirement.value())); } else if (componentRoleTypes.size() > 1) { throw new RuntimeException("Components with several roles must explicitly specify which role to use."); } else { componentRoleType = componentRoleTypes.iterator().next(); } } return componentRoleType; } /** * @return a configured Component Manager */ @Override public MockingComponentManager getComponentManager() throws Exception { return this.componentManager; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy