org.uniknow.agiledev.junitbdd.BDDRunner Maven / Gradle / Ivy
/**
* 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 extends Throwable> 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 extends Throwable> 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 extends Throwable> 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