package cucumber.runtime; import cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverter; import cucumber.deps.com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter; import cucumber.runtime.xstream.LocalizedXStreams; import gherkin.I18n; import gherkin.formatter.Argument; import gherkin.formatter.model.DataTableRow; import gherkin.formatter.model.DocString; import gherkin.formatter.model.Step; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class StepDefinitionMatchTest { private final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); private static final I18n ENGLISH = new I18n("en"); @Test public void converts_numbers() throws Throwable { StepDefinition stepDefinition = mock(StepDefinition.class); when(stepDefinition.getParameterCount()).thenReturn(1); when(stepDefinition.getParameterType(0, String.class)).thenReturn(new ParameterInfo(Integer.TYPE, null, null, null)); Step stepWithoutDocStringOrTable = mock(Step.class); when(stepWithoutDocStringOrTable.getDocString()).thenReturn(null); when(stepWithoutDocStringOrTable.getRows()).thenReturn(null); StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(Arrays.asList(new Argument(0, "5")), stepDefinition, "some.feature", stepWithoutDocStringOrTable, new LocalizedXStreams(classLoader)); stepDefinitionMatch.runStep(ENGLISH); verify(stepDefinition).execute(ENGLISH, new Object[]{5}); } @Test public void converts_with_explicit_converter() throws Throwable { StepDefinition stepDefinition = mock(StepDefinition.class); when(stepDefinition.getParameterCount()).thenReturn(1); when(stepDefinition.getParameterType(0, String.class)).thenReturn(new ParameterInfo(Thing.class, null, null, null)); Step stepWithoutDocStringOrTable = mock(Step.class); when(stepWithoutDocStringOrTable.getDocString()).thenReturn(null); when(stepWithoutDocStringOrTable.getRows()).thenReturn(null); StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(Arrays.asList(new Argument(0, "the thing")), stepDefinition, "some.feature", stepWithoutDocStringOrTable, new LocalizedXStreams(classLoader)); stepDefinitionMatch.runStep(ENGLISH); verify(stepDefinition).execute(ENGLISH, new Object[]{new Thing("the thing")}); } @Test public void converts_doc_string_with_explicit_converter() throws Throwable { StepDefinition stepDefinition = mock(StepDefinition.class); when(stepDefinition.getParameterCount()).thenReturn(1); when(stepDefinition.getParameterType(0, String.class)).thenReturn(new ParameterInfo(Thing.class, null, null, null)); Step stepWithDocString = mock(Step.class); DocString docString = new DocString("test", "the thing", 999); when(stepWithDocString.getDocString()).thenReturn(docString); when(stepWithDocString.getRows()).thenReturn(null); StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(new ArrayList<Argument>(), stepDefinition, "some.feature", stepWithDocString, new LocalizedXStreams(classLoader)); stepDefinitionMatch.runStep(ENGLISH); verify(stepDefinition).execute(ENGLISH, new Object[]{new Thing("the thing")}); } @XStreamConverter(ThingConverter.class) public static class Thing { public final String name; public Thing(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Thing thing = (Thing) o; return name.equals(thing.name); } @Override public int hashCode() { return name.hashCode(); } } public static class ThingConverter extends AbstractSingleValueConverter { @Override public boolean canConvert(Class type) { return Thing.class.equals(type); } @Override public Object fromString(String str) { return new Thing(str); } } @Test public void gives_nice_error_message_when_conversion_fails() throws Throwable { StepDefinition stepDefinition = mock(StepDefinition.class); when(stepDefinition.getParameterCount()).thenReturn(1); when(stepDefinition.getParameterType(0, String.class)).thenReturn(new ParameterInfo(Thang.class, null, null, null)); Step stepWithoutDocStringOrTable = mock(Step.class); when(stepWithoutDocStringOrTable.getDocString()).thenReturn(null); when(stepWithoutDocStringOrTable.getRows()).thenReturn(null); StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(Arrays.asList(new Argument(0, "blah")), stepDefinition, "some.feature", stepWithoutDocStringOrTable, new LocalizedXStreams(classLoader)); try { stepDefinitionMatch.runStep(ENGLISH); fail(); } catch (CucumberException expected) { assertEquals( "Don't know how to convert \"blah\" into cucumber.runtime.StepDefinitionMatchTest$Thang.\n" + "Try writing your own converter:\n" + "\n" + "@cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverter(ThangConverter.class)\n" + "public class Thang {}\n", expected.getMessage() ); } } public static class Thang { } @Test public void can_have_doc_string_as_only_argument() throws Throwable { StepDefinition stepDefinition = mock(StepDefinition.class); when(stepDefinition.getParameterCount()).thenReturn(1); when(stepDefinition.getParameterType(0, String.class)).thenReturn(new ParameterInfo(String.class, null, null, null)); Step stepWithDocString = mock(Step.class); DocString docString = new DocString("text/plain", "HELLO", 999); when(stepWithDocString.getDocString()).thenReturn(docString); when(stepWithDocString.getRows()).thenReturn(null); StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(new ArrayList<Argument>(), stepDefinition, "some.feature", stepWithDocString, new LocalizedXStreams(classLoader)); stepDefinitionMatch.runStep(ENGLISH); verify(stepDefinition).execute(ENGLISH, new Object[]{"HELLO"}); } @Test public void can_have_doc_string_as_last_argument_among_many() throws Throwable { StepDefinition stepDefinition = mock(StepDefinition.class); when(stepDefinition.getParameterCount()).thenReturn(2); when(stepDefinition.getParameterType(0, String.class)).thenReturn(new ParameterInfo(Integer.TYPE, null, null, null)); when(stepDefinition.getParameterType(1, String.class)).thenReturn(new ParameterInfo(String.class, null, null, null)); Step stepWithDocString = mock(Step.class); DocString docString = new DocString("test", "HELLO", 999); when(stepWithDocString.getDocString()).thenReturn(docString); when(stepWithDocString.getRows()).thenReturn(null); StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(Arrays.asList(new Argument(0, "5")), stepDefinition, "some.feature", stepWithDocString, new LocalizedXStreams(classLoader)); stepDefinitionMatch.runStep(ENGLISH); verify(stepDefinition).execute(ENGLISH, new Object[]{5, "HELLO"}); } @Test public void throws_arity_mismatch_exception_when_there_are_fewer_parameters_than_arguments() throws Throwable { Step step = new Step(null, "Given ", "I have 4 cukes in my belly", 1, null, null); StepDefinition stepDefinition = new StubStepDefinition(new Object(), Object.class.getMethod("toString"), "some pattern"); StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(asList(new Argument(7, "4")), stepDefinition, null, step, new LocalizedXStreams(getClass().getClassLoader())); try { stepDefinitionMatch.runStep(new I18n("en")); fail(); } catch (CucumberException expected) { assertEquals("Arity mismatch: Step Definition 'toString' with pattern [some pattern] is declared with 0 parameters. However, the gherkin step has 1 arguments [4]. \n" + "Step: Given I have 4 cukes in my belly", expected.getMessage()); } } public static class WithTwoParams { public void withTwoParams(int anInt, short aShort, List<String> strings) { } } @Test public void throws_arity_mismatch_exception_when_there_are_more_parameters_than_arguments() throws Throwable { Step step = new Step(null, "Given ", "I have 4 cukes in my belly", 1, new ArrayList<DataTableRow>(), null); StepDefinition stepDefinition = new StubStepDefinition(new Object(), WithTwoParams.class.getMethod("withTwoParams", Integer.TYPE, Short.TYPE, List.class), "some pattern"); StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(asList(new Argument(7, "4")), stepDefinition, null, step, new LocalizedXStreams(getClass().getClassLoader())); try { stepDefinitionMatch.runStep(new I18n("en")); fail(); } catch (CucumberException expected) { assertEquals("Arity mismatch: Step Definition 'withTwoParams' with pattern [some pattern] is declared with 3 parameters. However, the gherkin step has 2 arguments [4, Table:[]]. \n" + "Step: Given I have 4 cukes in my belly", expected.getMessage()); } } }