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

org.uniknow.agiledev.junitbdd.BDDRunner Maven / Gradle / Ivy

There is a newer version: 0.1.14
Show newest version
/**
 * Copyright (C) 2014 uniknow. All rights reserved.
 * 
 * This Java class is subject of the following restrictions:
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * 
 * 3. The end-user documentation included with the redistribution, if any, must
 * include the following acknowledgment: "This product includes software
 * developed by uniknow." Alternately, this acknowledgment may appear in the
 * software itself, if and wherever such third-party acknowledgments normally
 * appear.
 * 
 * 4. The name ''uniknow'' must not be used to endorse or promote products
 * derived from this software without prior written permission.
 * 
 * 5. Products derived from this software may not be called ''UniKnow'', nor may
 * ''uniknow'' appear in their name, without prior written permission of
 * uniknow.
 * 
 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WWS OR ITS
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package org.uniknow.agiledev.junitbdd;

import static org.uniknow.agiledev.junitbdd.internal.BDDStringUtils.*;

import java.util.*;

import junitparams.internal.*;

import org.junit.Test;
import org.junit.internal.runners.model.*;
import org.junit.runner.*;
import org.junit.runner.notification.*;
import org.junit.runners.*;
import org.junit.runners.model.*;

import org.uniknow.agiledev.junitbdd.internal.*;

/**
 * JUnit runner. Represents stories and scenarios as sentences in JUnit GUIs.
 * May also generate scenarios' statuses report in .scenarios or html format if
 * -DgenerateReport=[scenario|html] is passed while running
 * scenarios. The output folder for the report can be configured by setting
 * -DoutputFolder property.
 * 
 * If -DcamelCase is passed, IDEs will show class and method names instead of
 * scenario and story names.
 * 
 * @author Pawel Lipinski
 */
public class BDDRunner extends BlockJUnit4ClassRunner {

    private ScenarioListener scenarioListener;
    private Result result;
    private RunListener runListener;
    private ParameterisedTestClassRunner parameterisedRunner;

    public BDDRunner(Class klass) throws InitializationError {
        super(klass);
        parameterisedRunner = new ParameterisedTestClassRunner(getTestClass()) {
            @Override
            protected void computeTestMethods(TestClass testClass) {
                testMethodsList = new ArrayList();

                // Include all methods which are annotated with Scenario
                List annotatedMethods = getTestClass()
                    .getAnnotatedMethods(Scenario.class);

                for (final FrameworkMethod frameworkMethod : annotatedMethods) {
                    testMethodsList.add(new TestMethod(frameworkMethod,
                        getTestClass()) {
                        @Override
                        public String name() {
                            return testName(frameworkMethod);
                        }
                    });
                }
            }
        };
        computeTestMethods();
    }

    protected void collectInitializationErrors(List errors) {
    }

    @Override
    public void run(RunNotifier notifier) {
        setupScenariosListener(notifier);
        if (getTestClass().getJavaClass().getPackage() != null)
            scenarioListener.getStory().withPackage(
                getTestClass().getJavaClass().getPackage().getName());

        super.run(notifier);
        removeScenariosListener(notifier);
    }

    @Override
    protected void runChild(FrameworkMethod method, RunNotifier notifier) {
        if (handleIgnored(method, notifier))
            return;

        System.out.println("Processing method " + method);

        Statement methodBlock = methodBlock(method);

        Description description = describeMethod(method);

        // Determine expectedException exception
        Class expectedException = expectedException(method);
        if (isPending(method))
            runScenario(true, description, notifier, methodBlock,
                expectedException);
        else {
            TestMethod testMethod = parameterisedRunner.testMethodFor(method);
            if (parameterisedRunner.shouldRun(testMethod))
                parameterisedRunner.runParameterisedTest(testMethod,
                    methodBlock, notifier);
            else
                runScenario(false, description, notifier, methodBlock,
                    expectedException);
        }
    }

    private boolean handleIgnored(FrameworkMethod method, RunNotifier notifier) {
        TestMethod testMethod = parameterisedRunner.testMethodFor(method);
        if (testMethod.isIgnored())
            notifier.fireTestIgnored(describeMethod(method));

        return testMethod.isIgnored();
    }

    private boolean isPending(FrameworkMethod method) {
        return method.getAnnotation(Scenario.class).pending();
    }

    /**
     * Returns expected exception while executing scenario. If no exception is
     * expected returned class will be {@code Scenario.None}
     */
    private Class expectedException(FrameworkMethod method) {
        return method.getAnnotation(Scenario.class).expected();
    }

    /**
     * Execute scenario
     * 
     * @param pending
     *            boolean indicating whether scenario is pending or not
     * @param description
     *            description of scenario which is executed
     * @param notifier
     * @param methodInvoker
     * @param expectedException
     *            exception that is expected when executing scenario. If no
     *            exception is expected the value is Scenario.None
     */
    private void runScenario(boolean pending, Description description,
        RunNotifier notifier, Statement methodInvoker,
        Class expectedException) {
        EachTestNotifier eachNotifier = new EachTestNotifier(notifier,
            description);
        try {
            if (pending)
                scenarioListener.testStarted(description);
            else
                eachNotifier.fireTestStarted();

            methodInvoker.evaluate();

            if (expectedException != Scenario.None.class) {
                notifier.fireTestFailure(new Failure(description,
                    new Exception("Expected exception of type "
                        + expectedException)));
            }
        } catch (Throwable e) {
            if (!pending && !expectedException.isInstance(e))
                notifier.fireTestFailure(new Failure(description, e));
        } finally {
            eachNotifier.fireTestFinished();
        }

        if (pending) {
            eachNotifier.fireTestIgnored();
        }
    }

    @Override
    public Description getDescription() {
        Description description = Description.createSuiteDescription(getName(),
            getTestClass().getAnnotations());

        List resultMethods = new ArrayList();
        resultMethods.addAll(parameterisedRunner.returnListOfMethods());

        for (FrameworkMethod child : resultMethods) {
            Description describeMethod = describeMethod(child);
            description.addChild(describeMethod);
        }

        return description;
    }

    private Description describeMethod(FrameworkMethod method) {
        Description child = null;

        child = parameterisedRunner.describeParameterisedMethod(method);

        if (child == null)
            child = describeChild(method);

        return child;
    }

    private void removeScenariosListener(RunNotifier notifier) {
        notifier.fireTestRunFinished(result);

        notifier.removeListener(scenarioListener);
        notifier.removeListener(runListener);
    }

    private void setupScenariosListener(RunNotifier notifier) {
        result = new Result();
        runListener = result.createListener();
        notifier.addFirstListener(runListener);

        scenarioListener = new ScenarioListener();
        notifier.addListener(scenarioListener);
        notifier.fireTestRunStarted(getDescription());
    };

    @Override
    protected java.util.List computeTestMethods() {
        // ensureNothingAnnotatedWithTest(super.computeTestMethods());
        List resultMethods = new ArrayList();

        resultMethods.addAll(parameterisedRunner.computeFrameworkMethods());

        for (FrameworkMethod child : resultMethods)
            describeMethod(child);

        return resultMethods;
    }

    @Override
    protected String getName() {
        if (shouldSkipNameResolution())
            return super.getName();

        String name = super.getName();
        name = name.substring(name.lastIndexOf('.') + 1);
        if (name.endsWith("Test"))
            name = name.substring(0, name.lastIndexOf("Test"));
        if (name.endsWith("Scenarios"))
            name = name.substring(0, name.lastIndexOf("Scenarios"));
        return decamelise(name) + " should";
    };

    @Override
    protected String testName(FrameworkMethod method) {
        if (shouldSkipNameResolution())
            return super.testName(method);

        String scenarioName = decamelise(super.testName(method));
        String lowerCaseScenario = scenarioName.toLowerCase();
        if (lowerCaseScenario.startsWith("should")
            || lowerCaseScenario.startsWith("test"))
            scenarioName = skipFirstWord(scenarioName);

        return scenarioName;
    }

    private boolean shouldSkipNameResolution() {
        return System.getProperty("camelCase") != null;
    }

    private String skipFirstWord(String sentence) {
        int indexOfFirstWord = sentence.indexOf(" ");

        if (indexOfFirstWord > -1)
            return sentence.substring(indexOfFirstWord);
        else
            return sentence;
    }

    @Override
    protected Statement methodInvoker(FrameworkMethod method, Object test) {
        Statement methodInvoker = parameterisedRunner
            .parameterisedMethodInvoker(method, test);
        if (methodInvoker == null)
            methodInvoker = super.methodInvoker(method, test);

        return methodInvoker;
    }

    public static Object[] $(Object... params) {
        return params;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy