/* * Copyright 2008, Unitils.org * * 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 org.unitils.core; import java.lang.reflect.Method; import java.util.List; import java.util.Properties; /** * Core class of the Unitils library, and the main entry point that gives access to the {@link TestContext} and the * different {@link Module}s. * <p/> * An instance of Unitils is configured with a certain configuration using the {@link #init(Properties)} method. Normally, * only one instance of Unitils exists at any time. The default instance can be obtained using the {@link #getInstance()} method. * This default instance can be set to a custom initialized instance or instance of a custom subclass using * {@link #setInstance(Unitils)}. * <p/> * If not set, the singleton instance is initialized by default using {@link #initSingletonInstance()}. This method uses * the {@link ConfigurationLoader} to load the configuration. An instance of {@link ModulesRepository} is used to * initialize and maintain the modules. * <p/> * Unitils itself is also implemented as a module. In fact, an instance of Unitils behaves like a module who's behaviour * is defined by the added behaviour of all modules. */ public class Unitils { /* The singleton instance */ private static Unitils unitils; /** * Returns the singleton instance * * @return the singleton instance, not null */ public static synchronized Unitils getInstance() { if (unitils == null) { initSingletonInstance(); } return unitils; } /** * Sets the singleton instance to the given object * * @param unitils the singleton instance */ public static void setInstance(Unitils unitils) { Unitils.unitils = unitils; } /** * Initializes the singleton instance to the default value, loading the configuration using the {@link * ConfigurationLoader} */ public static void initSingletonInstance() { unitils = new Unitils(); unitils.init(); } /* Listener that observes the execution of tests */ private TestListener testListener; /* Repository for all modules that are currently active in Unitils */ private ModulesRepository modulesRepository; /* Configuration of Unitils, made up of different properties files */ private Properties configuration; /* Object keeping track of the unit test that is currently running */ private TestContext testContext; /** * Creates a new instance. */ public Unitils() { testContext = new TestContext(); } /** * Initializes unitils with the configuration files. */ public void init() { ConfigurationLoader configurationLoader = new ConfigurationLoader(); Properties properties = configurationLoader.loadConfiguration(); init(properties); } /** * Initializes Unitils with the given configuration. All the modules that are configured in the given configuration * are also created and initialized with this configuration. * * @param configuration The config, not null */ public void init(Properties configuration) { //verifyPackaging(configuration); this.configuration = configuration; modulesRepository = createModulesRepository(configuration); testListener = new UnitilsTestListener(); afterInitModules(); } /** * Gives all modules the opportunity to performs initialization that * can only work after all other modules have been initialized */ protected void afterInitModules() { for (Module module : modulesRepository.getModules()) { module.afterInit(); } } /** * Verifies that we're not working with a distribution that includes the necessary classes from spring, * while spring is in the classpath anyway. * * @param configuration The configuration */ protected void verifyPackaging(Properties configuration) { String springCoreClassName = configuration.getProperty("spring.core.someClass.name"); String unitilsPackagedWithSpring = "org.unitils.includeddeps." + springCoreClassName; if (isClassAvailable(springCoreClassName) && isClassAvailable(unitilsPackagedWithSpring)) { throw new IllegalStateException("It appears that you're using the unitils distribution that is packaged with " + "its dependency to spring, while spring is also in your classpath. This is not supported. The spring-packaged " + "distribution can only be used when you're not using spring at all. Please replace unitils-spring-included-version.jar " + "with unitils-version.jar"); } } /** * Utility method that verifies whether the class with the given fully qualified classname is available * in the classpath. * * @param className The name of the class * @return True if the class with the given name is available */ protected boolean isClassAvailable(String className) { try { Thread.currentThread().getContextClassLoader().loadClass(className); return true; } catch (ClassNotFoundException e) { return false; } } /** * Returns the single instance of {@link TestListener}. This instance provides hook callback methods that enable intervening * during the execution of unit tests. * * @return The single {@link TestListener} */ public TestListener getTestListener() { return testListener; } /** * Returns the {@link ModulesRepository} that provides access to the modules that are configured in unitils. * * @return the {@link ModulesRepository} */ public ModulesRepository getModulesRepository() { return modulesRepository; } /** * Returns the {@link TestContext} that, during the execution of the test suite, keeps track of the current test * object, class and test method that are executing. * * @return the {@link TestContext} */ public TestContext getTestContext() { return testContext; } /** * Returns all properties that are used to configure unitils and the different modules. * * @return a <code>Properties</code> object */ public Properties getConfiguration() { return configuration; } /** * Configures all unitils modules using the given <code>Properties</code> object, and stores them in a {@link * ModulesRepository}. The configuration of the modules is delegated to a {@link ModulesLoader} instance. * * @param configuration The config, not null * @return a new {@link ModulesRepository} */ protected ModulesRepository createModulesRepository(Properties configuration) { ModulesLoader modulesLoader = new ModulesLoader(); List<Module> modules = modulesLoader.loadModules(configuration); return new ModulesRepository(modules); } /** * Implementation of {@link TestListener} that ensures that at every point during the run of a test, every {@link * Module} gets the chance of performing some behavior, by calling the {@link TestListener} of each module in turn. * Also makes sure that the state of the instance of {@link TestContext} returned by {@link Unitils#getTestContext()} * is correctly set to the current test class, test object and test method. */ private class UnitilsTestListener extends TestListener { @Override public void beforeTestClass(Class<?> testClass) { TestContext testContext = getTestContext(); testContext.setTestClass(testClass); testContext.setTestObject(null); testContext.setTestMethod(null); List<Module> modules = modulesRepository.getModules(); for (Module module : modules) { modulesRepository.getTestListener(module).beforeTestClass(testClass); } } @Override public void afterCreateTestObject(Object testObject) { TestContext testContext = getTestContext(); testContext.setTestClass(testObject.getClass()); testContext.setTestObject(testObject); testContext.setTestMethod(null); List<Module> modules = modulesRepository.getModules(); for (Module module : modules) { modulesRepository.getTestListener(module).afterCreateTestObject(testObject); } } @Override public void beforeTestSetUp(Object testObject, Method testMethod) { TestContext testContext = getTestContext(); testContext.setTestClass(testObject.getClass()); testContext.setTestObject(testObject); testContext.setTestMethod(testMethod); List<Module> modules = modulesRepository.getModules(); for (Module module : modules) { modulesRepository.getTestListener(module).beforeTestSetUp(testObject, testMethod); } } @Override public void beforeTestMethod(Object testObject, Method testMethod) { TestContext testContext = getTestContext(); testContext.setTestClass(testObject.getClass()); testContext.setTestObject(testObject); testContext.setTestMethod(testMethod); List<Module> modules = modulesRepository.getModules(); for (Module module : modules) { modulesRepository.getTestListener(module).beforeTestMethod(testObject, testMethod); } } @Override public void afterTestMethod(Object testObject, Method testMethod, Throwable throwable) { TestContext testContext = getTestContext(); testContext.setTestClass(testObject.getClass()); testContext.setTestObject(testObject); testContext.setTestMethod(testMethod); List<Module> modules = modulesRepository.getModules(); for (Module module : modules) { modulesRepository.getTestListener(module).afterTestMethod(testObject, testMethod, throwable); } } @Override public void afterTestTearDown(Object testObject, Method testMethod) { TestContext testContext = getTestContext(); testContext.setTestClass(testObject.getClass()); testContext.setTestObject(testObject); testContext.setTestMethod(null); List<Module> modules = modulesRepository.getModules(); for (Module module : modules) { modulesRepository.getTestListener(module).afterTestTearDown(testObject, testMethod); } } @Override public boolean shouldInvokeTestMethod(Object testObject, Method testMethod) { List<Module> modules = modulesRepository.getModules(); for (Module module : modules) { if(!modulesRepository.getTestListener(module).shouldInvokeTestMethod(testObject, testMethod)) { return false; // there is a module that wants us to prevent execution of the method. } } return true; } } }