/* * Copyright(c) 2005 Center for E-Commerce Infrastructure Development, The * University of Hong Kong (HKU). All Rights Reserved. * * This software is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.0 [1] * * [1] http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt */ package hk.hku.cecid.piazza.commons.test; import hk.hku.cecid.piazza.commons.test.utils.FixtureStore; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.jmock.Mockery; import org.jmock.lib.legacy.ClassImposteriser; import org.junit.Before; import org.junit.After; /** * The <code>UnitTest</code> is top level class for performing unit-test. * * @author Twinsen Tsang * @version 1.0.0 * @since JDK5.0, H2O 0908 */ public abstract class UnitTest<T> { // Instance logger. protected final Logger logger = LoggerFactory.getLogger(this.getClass()); // The resource loader for this class, For detail, read protected ClassLoader FIXTURE_LOADER; // Mock context protected Mockery mockContext; // private boolean mockRequired; // The testing target. protected T target; private List<DependencyEntry> dependencies = new ArrayList<DependencyEntry>(); private static class DependencyEntry { /** * @param fixtureOnly * @param testClass */ public DependencyEntry(UnitTest<?> testClass, boolean fixtureOnly) { this.fixtureOnly = fixtureOnly; this.testClass = testClass; } private boolean fixtureOnly; private UnitTest<?> testClass; public boolean isFixtureOnly() { return fixtureOnly; } public void setFixtureOnly(boolean fixtureOnly) { this.fixtureOnly = fixtureOnly; } public UnitTest<?> getTestClass() { return testClass; } public void setTestClass(UnitTest<?> testClass) { if (testClass == null) { throw new NullPointerException("Missing 'testClass' in the arugments."); } this.testClass = testClass; } } /** * Create an instance of <code>UnitTest</code>. */ public UnitTest() { this(true); } /** * Create an instance of <code>UnitTest</code>. * * @param noMocking */ public UnitTest(boolean noMocking) { this.mockRequired = noMocking; } /** * @return Get the testing target. */ public T getTestingTarget() { return this.target; } /** * Compatible to JUnit3-style. */ @Before public void setUp() throws Exception { this.initTestDependency(); for (DependencyEntry e: dependencies) { if (!e.isFixtureOnly()) { e.getTestClass().setUp(); // Setup the test dependency. } } this.initFixtureLoader(); if (this.mockRequired) { this.initTestMockObjects(); } this.initTestTarget(); this.initTestDependencyInjection(); } /** * Initialize all test-class dependency used for testing. */ public void initTestDependency() throws Exception { this.logger.debug("Initialize Test-case dependency"); } /** * Initialize the resource class loader for loading resource at test-case class folders under res/. */ public void initFixtureLoader() throws Exception { this.logger.debug("Initialize Fixture Loader"); Class<?>[] dependenciesClass = new Class[dependencies.size()+1]; Class<?> testClass = null; int i = 0; for (DependencyEntry e: dependencies) { testClass = e.getTestClass().getClass(); dependenciesClass[i++] = testClass; this.logger.debug("Loaded external fixture class : {}", testClass.getName()); } Class<?> thisClass = this.getClass(); if (thisClass.isMemberClass() || thisClass.isLocalClass()) { thisClass = thisClass.getEnclosingClass(); } dependenciesClass[i] = thisClass; this.logger.debug("Loaded self fixture class : {}", thisClass); FIXTURE_LOADER = FixtureStore.createFixtureLoader(false, dependenciesClass); } /** * Initialize the mock object used in the test-case. */ public void initTestMockObjects() throws Exception { this.logger.debug("Initialize Mock objects"); mockContext = new Mockery() { { // So that test-case can mock abstract class. setImposteriser(ClassImposteriser.INSTANCE); } }; } /** * Initialize the test target for this test-case. */ public void initTestTarget() throws Exception { this.logger.debug("Initialize Testing Target"); } /** * Initialize the testing dependency injection for this test-case */ public void initTestDependencyInjection() throws Exception {}; /** * Add a testClass dependency to this UnitTest. * * @param testClass * @param fixtureOnly */ public void addTestDependency(UnitTest<?> testClass, boolean fixtureOnly) { this.logger.debug("Adding Test-case dependency {}", testClass.getClass()); this.dependencies.add(new DependencyEntry(testClass, fixtureOnly)); } public <C extends UnitTest<?>> void addTestDependency(Class<C> testClass, boolean fixtureOnly) { if (testClass == null) { throw new NullPointerException("Missing 'testClass' in the arguments"); } this.logger.debug("Adding Test-case dependency {}", testClass); try { this.dependencies.add(new DependencyEntry(testClass.newInstance(), fixtureOnly)); } catch(Throwable t) { this.logger.error("Unable to create test dependency", t); } } /** * * * @param <T> * @return */ @SuppressWarnings("unchecked") public <T extends UnitTest<?>> T getTestDependency(Class<T> testClass) { if (testClass == null) { throw new NullPointerException("Missing 'testClass' in the arguments"); } for (DependencyEntry e: dependencies) { if (testClass.isAssignableFrom(e.getTestClass().getClass())) { return (T) e.getTestClass(); } } return null; } /** * Check the mock whether the expectations is correct or not. */ @After public void checkMonk() { if (mockContext != null) { mockContext.assertIsSatisfied(); } } /** * Tear-down all resource loaded. */ @After public void tearDown() throws Exception { for (DependencyEntry e: dependencies) { if (!e.isFixtureOnly()) { e.getTestClass().tearDown(); } } } }