/*
* Copyright Technophobia Ltd 2012
*
* This file is part of Substeps.
*
* Substeps is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Substeps is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Substeps. If not, see <http://www.gnu.org/licenses/>.
*/
package com.technophobia.substeps.runner;
import static org.hamcrest.CoreMatchers.any;
import static org.hamcrest.CoreMatchers.is;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import com.technophobia.substeps.execution.node.IExecutionNode;
import com.technophobia.substeps.model.exception.SubstepsConfigurationException;
import com.technophobia.substeps.stepimplementations.MockStepImplementations;
import com.technophobia.substeps.steps.TestStepImplementations;
/**
* @author imoore
*
*/
public class JunitFeatureRunnerTest extends BaseJunitFeatureRunnerTest {
private static final String SUBSTEPS_RESOURCES_BASE_DIRECTORY = "./target/core-tests/";
@Test(expected = SubstepsConfigurationException.class)
public void testScenarioWithMissingStepsCausesFailure() {
final String feature = SUBSTEPS_RESOURCES_BASE_DIRECTORY + "features/scenario_missing_steps.feature";
final String tag = "@scenario_with_missing_steps";
final String substeps = SUBSTEPS_RESOURCES_BASE_DIRECTORY + "substeps/error.substeps";
final JunitFeatureRunner runner = new JunitFeatureRunner();
final List<Class<?>> stepImplsList = new ArrayList<Class<?>>();
stepImplsList.add(TestStepImplementations.class);
runner.init(this.getClass(), stepImplsList, feature, tag, substeps, null);
runner.run(null);
}
@Test(expected = SubstepsConfigurationException.class)
public void testMissingSubStepCausesFailure() {
final String feature = SUBSTEPS_RESOURCES_BASE_DIRECTORY + "features/error.feature";
final String tag = "@bug_missing_sub_step_impl";
final String substeps = SUBSTEPS_RESOURCES_BASE_DIRECTORY + "substeps/error.substeps";
final JunitFeatureRunner runner = new JunitFeatureRunner();
final List<Class<?>> stepImplsList = new ArrayList<Class<?>>();
stepImplsList.add(TestStepImplementations.class);
runner.init(this.getClass(), stepImplsList, feature, tag, substeps, null);
}
@Test
public void testTableParameterPassingWithOutlines() {
final String feature = SUBSTEPS_RESOURCES_BASE_DIRECTORY + "features/bugs.feature";
final String tag = "@table_params_and_outline";
final String substeps = null;
final JunitFeatureRunner runner = new JunitFeatureRunner();
final List<Class<?>> stepImplsList = new ArrayList<Class<?>>();
stepImplsList.add(TestStepImplementations.class);
runner.init(this.getClass(), stepImplsList, feature, tag, substeps, null);
final TestStepImplementations stepImpls = new TestStepImplementations();
final TestStepImplementations spy = spy(stepImpls);
final Map<Class<?>, Object> implsCache = getImplsCache(runner);
implsCache.put(TestStepImplementations.class, spy);
final RunNotifier notifier = mock(RunNotifier.class);
runner.run(notifier);
final List<Map<String, String>> expectedTableParameter = new ArrayList<Map<String, String>>();
final Map<String, String> row = new HashMap<String, String>();
expectedTableParameter.add(row);
row.put("column1", "one");
row.put("column2", "two");
verify(spy, times(1)).methodWithTableArgument(expectedTableParameter);
final IExecutionNode root = runner.getRootExecutionNode();
System.out.println(TreePrinter.asString(root));
}
@Test
public void testNestedParameterPassing() {
final JunitFeatureRunner runner = new JunitFeatureRunner();
final List<Class<?>> stepImplsList = new ArrayList<Class<?>>();
stepImplsList.add(TestStepImplementations.class);
runner.init(this.getClass(), stepImplsList, SUBSTEPS_RESOURCES_BASE_DIRECTORY
+ "features/paramsToSubSteps.feature", "nested_params_bug", SUBSTEPS_RESOURCES_BASE_DIRECTORY
+ "substeps/nested_params_substeps", null);
final TestStepImplementations stepImpls = new TestStepImplementations();
final TestStepImplementations spy = spy(stepImpls);
final Map<Class<?>, Object> implsCache = getImplsCache(runner);
implsCache.put(TestStepImplementations.class, spy);
final RunNotifier notifier = mock(RunNotifier.class);
runner.run(notifier);
verify(spy, times(2)).sendKeysById("Basic", "firstName");
}
@Test
public void testRunFeaturesInFolders() {
final JunitFeatureRunner runner = new JunitFeatureRunner();
final List<Class<?>> stepImplsList = new ArrayList<Class<?>>();
stepImplsList.add(TestStepImplementations.class);
runner.init(this.getClass(), stepImplsList, SUBSTEPS_RESOURCES_BASE_DIRECTORY + "features_dir", null,
SUBSTEPS_RESOURCES_BASE_DIRECTORY + "substeps_dir", null);
final TestStepImplementations stepImpls = new TestStepImplementations();
final TestStepImplementations spy = spy(stepImpls);
// get hold of the step runner
// implsCache.put(execImpl.implementedIn, target);
final Map<Class<?>, Object> implsCache = getImplsCache(runner);
implsCache.put(TestStepImplementations.class, spy);
final RunNotifier notifier = mock(RunNotifier.class);
runner.run(notifier);
verify(spy, times(1)).meth1("folder1");
verify(spy, times(1)).meth2("bob");
}
@Test
public void testRunWithNoTags() {
final JunitFeatureRunner runner = new JunitFeatureRunner();
final List<Class<?>> stepImplsList = new ArrayList<Class<?>>();
stepImplsList.add(MockStepImplementations.class);
// pass in the stuff that would normally be placed in the annotation
runner.init(this.getClass(), stepImplsList, SUBSTEPS_RESOURCES_BASE_DIRECTORY + "features/allFeatures.feature",
null, SUBSTEPS_RESOURCES_BASE_DIRECTORY + "substeps/allFeatures.substeps", null);
final MockStepImplementations stepImpls = new MockStepImplementations();
final MockStepImplementations spy = spy(stepImpls);
// get hold of the step runner
final Map<Class<?>, Object> implsCache = getImplsCache(runner);
implsCache.put(MockStepImplementations.class, spy);
final RunNotifier notifier = mock(RunNotifier.class);
runner.getDescription();
runner.run(notifier);
// now verify that what was run was indeed run
verify(spy, times(8)).meth1();
verify(spy, times(1)).meth12();
verify(spy, times(3)).meth2();
verify(spy, times(1)).meth4("quoted parameter");
verify(spy, times(1)).meth4("bob");
verify(spy, times(1)).meth4("fred");
verify(spy, times(1)).meth4("barf");
verify(spy, never()).meth5();
// substep verifications
verify(spy, times(1)).meth7("something");
verify(spy, times(1)).meth6();
// test the quoted params
verify(spy, times(1)).meth8("something in quotes");
verify(spy, times(1)).meth9();
final List<Map<String, String>> table = new ArrayList<Map<String, String>>();
final Map<String, String> row = new HashMap<String, String>();
row.put("param1", "W");
row.put("param2", "X");
row.put("param3", "Y");
row.put("param4", "Z");
table.add(row);
verify(spy, times(1)).meth10(table);
// test the number of times the notifier was called
int started = 30;
int finished = 24;
int failed = 6;
Assert.assertEquals(started, failed + finished);
verify(notifier, times(started)).fireTestStarted(argThat(any(Description.class)));
// this is now up to 25 as more of a hierarchy with outlines
verify(notifier, times(finished)).fireTestFinished(argThat(any(Description.class)));
verify(notifier, times(failed)).fireTestFailure(argThat(any(Failure.class)));
// test failures now cascade upwards
verify(spy, times(1)).meth4("#quoted parameter");
final IExecutionNode root = runner.getRootExecutionNode();
System.out.println(TreePrinter.asString(root));
}
@Test
public void testNotifications() {
System.out.println("\n\n\n\n\n: testNotifications");
final JunitFeatureRunner runner = new JunitFeatureRunner();
final List<Class<?>> stepImplsList = new ArrayList<Class<?>>();
stepImplsList.add(MockStepImplementations.class);
// pass in the stuff that would normally be placed in the annotation
runner.init(this.getClass(), stepImplsList, SUBSTEPS_RESOURCES_BASE_DIRECTORY
+ "features/notifications.feature", null, SUBSTEPS_RESOURCES_BASE_DIRECTORY
+ "substeps/allFeatures.substeps", null);
final MockStepImplementations stepImpls = new MockStepImplementations();
final MockStepImplementations spy = spy(stepImpls);
// get hold of the step runner
final Map<Class<?>, Object> implsCache = getImplsCache(runner);
implsCache.put(MockStepImplementations.class, spy);
final RunNotifier notifier = mock(RunNotifier.class);
final Description rootDescription = runner.getDescription();
runner.run(notifier);
// test the number of times the notifier was called
verifyNotifications(notifier, rootDescription);
verify(notifier, never()).fireTestFailure(argThat(any(Failure.class)));
}
/**
* @param notifier
* @param rootDescription
*/
private void verifyNotifications(final RunNotifier notifier, final Description rootDescription) {
int idx = 1;
verify(notifier, times(1)).fireTestStarted(rootDescription);
verify(notifier, times(1)).fireTestFinished(rootDescription);
System.out.println("root fire count: " + idx++);
final ArrayList<Description> features = rootDescription.getChildren();
for (final Description fDesc : features) {
System.out.println("feature fire count: " + idx++);
verify(notifier, times(1)).fireTestStarted(fDesc);
verify(notifier, times(1)).fireTestFinished(fDesc);
final ArrayList<Description> scenarios = fDesc.getChildren();
for (final Description scDesc : scenarios) {
System.out.println("scenario fire count: " + idx++);
verify(notifier, times(1)).fireTestStarted(scDesc);
verify(notifier, times(1)).fireTestFinished(scDesc);
final ArrayList<Description> steps = scDesc.getChildren();
for (final Description step : steps) {
System.out.println("step fire count: " + idx++);
verify(notifier, times(1)).fireTestStarted(step);
verify(notifier, times(1)).fireTestFinished(step);
}
}
}
}
@Test
public void testStepWithInlineTable() {
final JunitFeatureRunner runner = new JunitFeatureRunner();
final List<Class<?>> stepImplsList = new ArrayList<Class<?>>();
stepImplsList.add(MockStepImplementations.class);
// pass in the stuff that would normally be placed in the annotation
runner.init(this.getClass(), stepImplsList, SUBSTEPS_RESOURCES_BASE_DIRECTORY
+ "features/stepWithInlineTable.feature", null, SUBSTEPS_RESOURCES_BASE_DIRECTORY
+ "substeps/allFeatures.substeps", null);
final MockStepImplementations stepImpls = new MockStepImplementations();
final MockStepImplementations spy = spy(stepImpls);
final Map<Class<?>, Object> implsCache = getImplsCache(runner);
implsCache.put(MockStepImplementations.class, spy);
final RunNotifier notifier = mock(RunNotifier.class);
runner.run(notifier);
// now verify that what was run was indeed run
final List<Map<String, String>> table = new ArrayList<Map<String, String>>();
final Map<String, String> row = new HashMap<String, String>();
row.put("param1", "W");
row.put("param2", "X");
row.put("param3", "Y");
row.put("param4", "Z");
table.add(row);
verify(spy, times(1)).meth10(table);
}
@Test
public void testSubStepFailureHandledCorrectly() {
final JunitFeatureRunner runner = new JunitFeatureRunner();
final List<Class<?>> stepImplsList = new ArrayList<Class<?>>();
stepImplsList.add(MockStepImplementations.class);
// pass in the stuff that would normally be placed in the annotation
runner.init(this.getClass(), stepImplsList, SUBSTEPS_RESOURCES_BASE_DIRECTORY + "features/bugs.feature",
"@bug1", SUBSTEPS_RESOURCES_BASE_DIRECTORY + "substeps/bugs.substeps", null);
final MockStepImplementations stepImpls = new MockStepImplementations();
final MockStepImplementations spy = spy(stepImpls);
// get hold of the step runner
final Map<Class<?>, Object> implsCache = getImplsCache(runner);
implsCache.put(MockStepImplementations.class, spy);
final RunNotifier notifier = mock(RunNotifier.class);
runner.run(notifier);
// now verify that what was run was indeed run
verify(spy, times(1)).meth12();
verify(spy, times(1)).meth11();
verify(spy, never()).meth9();
verify(spy, never()).meth6();
verify(notifier, times(6)).fireTestStarted(argThat(any(Description.class)));
verify(notifier, times(5)).fireTestFailure(argThat(any(Failure.class)));
}
}