com.fitbur.jestify.junit.spring.IntegrationTestRunner.java Source code

Java tutorial

Introduction

Here is the source code for com.fitbur.jestify.junit.spring.IntegrationTestRunner.java

Source

/*
 * Copyright 2015 Sharmarke Aden <saden1 at gmail.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 com.fitbur.jestify.junit.spring;

import com.fitbur.jestify.api.Cut;
import com.fitbur.jestify.core.TestContext;
import com.fitbur.jestify.core.analyzer.CutAnalyzer;
import com.fitbur.jestify.core.analyzer.TesAnalyzer;
import com.fitbur.jestify.core.descriptor.TestDescriptor;
import static com.google.common.base.Preconditions.checkState;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static java.util.stream.Collectors.toSet;
import static java.util.stream.Stream.of;
import org.junit.internal.AssumptionViolatedException;
import org.junit.internal.runners.model.EachTestNotifier;
import org.junit.runner.Description;
import static org.junit.runner.Description.createTestDescription;
import org.junit.runner.notification.RunNotifier;
import org.junit.runner.notification.StoppedByUserException;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
import org.objectweb.asm.ClassReader;
import static org.objectweb.asm.ClassReader.EXPAND_FRAMES;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 *
 * @author Sharmarke Aden <saden1 at gmail.org>
 */
public class IntegrationTestRunner extends BlockJUnit4ClassRunner {

    Map<Class, TestDescriptor> testClassContexts = new ConcurrentHashMap<>();
    Map<Class, AnnotationConfigApplicationContext> applicationContexts = new ConcurrentHashMap<>();
    private RunNotifier notifier;

    /**
     * Create a new test runner instance for the class under test.
     *
     * @param testClass the test class type
     * @throws InitializationError thrown if the test class is malformed.
     */
    public IntegrationTestRunner(Class<?> testClass) throws InitializationError {
        super(testClass);
    }

    /**
     * Describe the test class.
     *
     * @return
     */
    @Override
    public Description getDescription() {
        TestClass testClass = getTestClass();
        Class<?> javaClass = testClass.getJavaClass();
        String name = javaClass.getSimpleName();

        return createTestDescription(javaClass, name, testClass.getAnnotations());
    }

    @Override
    protected Object createTest() throws Exception {
        try {
            TestClass testClass = getTestClass();
            Class<?> javaClass = testClass.getJavaClass();
            String name = javaClass.getSimpleName();

            TestDescriptor testDescriptor = testClassContexts.computeIfAbsent(javaClass, p -> {
                Object instance;
                try {
                    instance = super.createTest();
                    TestContext context = new TestContext(name, javaClass, instance);
                    Set<Field> candidatesFields = of(javaClass.getDeclaredFields()).parallel()
                            .filter(f -> f.isAnnotationPresent(Cut.class)).collect(toSet());

                    if (candidatesFields.isEmpty()) {
                        checkState(false, "Class under test not defined in %s. "
                                + "Please annotated a single field with @Cut.", name);
                    } else if (candidatesFields.size() != 1) {
                        checkState(false, "Found more than one class under test in %s. "
                                + "Please annotated only a single field with @Cut.", name);
                    }

                    Field cutField = candidatesFields.iterator().next();
                    ClassReader testReader = new ClassReader(javaClass.getName());
                    ClassReader cutReader = new ClassReader(cutField.getType().getName());

                    testReader.accept(new TesAnalyzer(context), EXPAND_FRAMES);
                    cutReader.accept(new CutAnalyzer(context), EXPAND_FRAMES);

                    return new TestDescriptor(instance, context);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });

            AnnotationConfigApplicationContext appContext = new AnnotationConfigApplicationContext();
            appContext.setAllowBeanDefinitionOverriding(true);
            appContext.setId(name);
            appContext.setDisplayName(name);

            Object instance = testDescriptor.getInstance();
            TestContext context = testDescriptor.getContext();
            IntegrationTestReifier reifier = new IntegrationTestReifier(appContext, instance);
            IntegrationTestCreator integrationTestCreator = new IntegrationTestCreator(context, reifier,
                    appContext);
            integrationTestCreator.create();

            applicationContexts.put(javaClass, appContext);
            return instance;
        } catch (IllegalStateException e) {
            notifier.pleaseStop();
            throw e;
        }
    }

    @Override
    public void run(RunNotifier notifier) {
        this.notifier = notifier;
        Description description = getDescription();
        TestClass testClass = getTestClass();
        Class<?> javaClass = testClass.getJavaClass();
        String name = javaClass.getSimpleName();
        IntegrationTestRunListener listener = new IntegrationTestRunListener(name, testClassContexts,
                applicationContexts);
        notifier.addListener(listener);
        EachTestNotifier testNotifier = new EachTestNotifier(notifier, description);
        try {
            Statement statement = classBlock(notifier);
            statement.evaluate();

            // invoke here the run started method
            notifier.fireTestRunStarted(description);
        } catch (AssumptionViolatedException e) {
            testNotifier.fireTestIgnored();
        } catch (StoppedByUserException e) {
            throw e;
        } catch (Throwable e) {
            testNotifier.addFailure(e);
        }
    }

}