package cucumber.runtime.android;
import android.app.Instrumentation;
import android.os.Bundle;
import cucumber.runtime.Runtime;
import edu.emory.mathcs.backport.java.util.Collections;
import gherkin.formatter.model.Feature;
import gherkin.formatter.model.Match;
import gherkin.formatter.model.Result;
import gherkin.formatter.model.Scenario;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static cucumber.runtime.android.AndroidInstrumentationReporter.StatusCodes;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
public class AndroidInstrumentationReporterTest {
@Rule
public final ExpectedException expectedException = ExpectedException.none();
private final Runtime runtime = mock(Runtime.class);
private final Instrumentation instrumentation = mock(Instrumentation.class);
private final Feature feature = mock(Feature.class);
private final Scenario scenario = mock(Scenario.class);
private final Match match = mock(Match.class);
private final Result firstResult = mock(Result.class);
private final Result secondResult = mock(Result.class);
@Before
public void beforeEachTest() {
when(feature.getKeyword()).thenReturn("Feature");
when(feature.getName()).thenReturn("Some important feature");
when(scenario.getKeyword()).thenReturn("Scenario");
when(scenario.getName()).thenReturn("Some important scenario");
}
@Test
public void feature_name_and_keyword_is_contained_in_start_signal() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
// when
formatter.feature(feature);
formatter.startOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.START), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.CLASS), containsString(feature.getKeyword()));
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.CLASS), containsString(feature.getName()));
}
@Test
public void feature_name_and_keyword_is_contained_in_end_signal() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
when(firstResult.getStatus()).thenReturn(Result.PASSED);
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.OK), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.CLASS), containsString(feature.getKeyword()));
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.CLASS), containsString(feature.getName()));
}
@Test
public void scenario_name_and_keyword_is_contained_in_start_signal() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
// when
formatter.feature(feature);
formatter.startOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.START), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.TEST), containsString(scenario.getKeyword()));
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.TEST), containsString(scenario.getName()));
}
@Test
public void scenario_name_and_keyword_is_contained_in_end_signal() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
when(firstResult.getStatus()).thenReturn(Result.PASSED);
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.OK), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.TEST), containsString(scenario.getKeyword()));
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.TEST), containsString(scenario.getName()));
}
@Test
public void any_before_hook_exception_causes_test_error() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
when(firstResult.getStatus()).thenReturn(Result.FAILED);
when(firstResult.getError()).thenReturn(new RuntimeException("some random runtime exception"));
// when
formatter.feature(feature);
formatter.before(match, firstResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("some random runtime exception"));
}
@Test
public void any_step_exception_causes_test_error() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
when(firstResult.getStatus()).thenReturn(Result.FAILED);
when(firstResult.getError()).thenReturn(new RuntimeException("some random runtime exception"));
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("some random runtime exception"));
}
@Test
public void any_after_hook_exception_causes_test_error() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
when(firstResult.getStatus()).thenReturn(Result.FAILED);
when(firstResult.getError()).thenReturn(new RuntimeException("some random runtime exception"));
// when
formatter.feature(feature);
formatter.after(match, firstResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("some random runtime exception"));
}
@Test
public void any_failing_step_causes_test_failure() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
when(firstResult.getStatus()).thenReturn(Result.FAILED);
when(firstResult.getError()).thenReturn(new AssertionError("some test assertion went wrong"));
when(firstResult.getErrorMessage()).thenReturn("some test assertion went wrong");
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.FAILURE), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("some test assertion went wrong"));
}
@Test
public void any_undefined_step_causes_test_error() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
when(firstResult.getStatus()).thenReturn(Result.UNDEFINED.getStatus());
when(runtime.getSnippets()).thenReturn(Collections.singletonList("some snippet"));
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("some snippet"));
}
@Test
public void passing_step_causes_test_success() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 1);
when(firstResult.getStatus()).thenReturn(Result.PASSED);
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
verify(instrumentation).sendStatus(eq(StatusCodes.OK), any(Bundle.class));
}
@Test
public void skipped_step_causes_test_success() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
when(firstResult.getStatus()).thenReturn(Result.PASSED);
when(secondResult.getStatus()).thenReturn(Result.SKIPPED.getStatus());
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.result(secondResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
verify(instrumentation).sendStatus(eq(StatusCodes.OK), any(Bundle.class));
}
@Test
public void first_before_exception_is_reported() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
when(firstResult.getStatus()).thenReturn(Result.FAILED);
when(firstResult.getError()).thenReturn(new RuntimeException("first exception"));
when(secondResult.getStatus()).thenReturn(Result.FAILED);
when(secondResult.getError()).thenReturn(new RuntimeException("second exception"));
// when
formatter.feature(feature);
formatter.before(match, firstResult);
formatter.before(match, secondResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("first exception"));
}
@Test
public void first_step_result_exception_is_reported() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
when(firstResult.getStatus()).thenReturn(Result.FAILED);
when(firstResult.getError()).thenReturn(new RuntimeException("first exception"));
when(secondResult.getStatus()).thenReturn(Result.FAILED);
when(secondResult.getError()).thenReturn(new RuntimeException("second exception"));
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.result(secondResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("first exception"));
}
@Test
public void first_after_exception_is_reported() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
when(firstResult.getStatus()).thenReturn(Result.FAILED);
when(firstResult.getError()).thenReturn(new RuntimeException("first exception"));
when(secondResult.getStatus()).thenReturn(Result.FAILED);
when(secondResult.getError()).thenReturn(new RuntimeException("second exception"));
// when
formatter.feature(feature);
formatter.after(match, firstResult);
formatter.after(match, secondResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("first exception"));
}
@Test
public void undefined_step_overrides_preceding_passed_step() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
when(firstResult.getStatus()).thenReturn(Result.PASSED);
when(secondResult.getStatus()).thenReturn(Result.UNDEFINED.getStatus());
when(runtime.getSnippets()).thenReturn(Collections.singletonList("some snippet"));
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.result(secondResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("some snippet"));
}
@Test
public void failed_step_overrides_preceding_passed_step() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
when(firstResult.getStatus()).thenReturn(Result.PASSED);
when(secondResult.getStatus()).thenReturn(Result.FAILED);
when(secondResult.getError()).thenReturn(new AssertionError("some assertion went wrong"));
when(secondResult.getErrorMessage()).thenReturn("some assertion went wrong");
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.result(secondResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.FAILURE), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("some assertion went wrong"));
}
@Test
public void error_step_overrides_preceding_passed_step() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
when(firstResult.getStatus()).thenReturn(Result.PASSED);
when(secondResult.getStatus()).thenReturn(Result.FAILED);
when(secondResult.getError()).thenReturn(new RuntimeException("some exception"));
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.result(secondResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("some exception"));
}
@Test
public void failed_step_does_not_overrides_preceding_undefined_step() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
when(firstResult.getStatus()).thenReturn(Result.UNDEFINED.getStatus());
when(runtime.getSnippets()).thenReturn(Collections.singletonList("some snippet"));
when(secondResult.getStatus()).thenReturn(Result.FAILED);
when(secondResult.getError()).thenReturn(new AssertionError("some assertion went wrong"));
when(secondResult.getErrorMessage()).thenReturn("some assertion went wrong");
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.result(secondResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.ERROR), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("some snippet"));
}
@Test
public void error_step_does_not_override_preceding_failed_step() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
when(firstResult.getStatus()).thenReturn(Result.FAILED);
when(firstResult.getError()).thenReturn(new AssertionError("some assertion went wrong"));
when(firstResult.getErrorMessage()).thenReturn("some assertion went wrong");
when(secondResult.getStatus()).thenReturn(Result.FAILED);
when(secondResult.getError()).thenReturn(new RuntimeException("some exception"));
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.result(secondResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(instrumentation).sendStatus(eq(StatusCodes.FAILURE), captor.capture());
final Bundle actualBundle = captor.getValue();
assertThat(actualBundle.getString(AndroidInstrumentationReporter.StatusKeys.STACK), containsString("some assertion went wrong"));
}
@Test
public void unexpected_status_code_causes_IllegalStateException() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
when(firstResult.getStatus()).thenReturn("foobar");
// then
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(containsString("foobar"));
// when
formatter.feature(feature);
formatter.result(firstResult);
formatter.endOfScenarioLifeCycle(scenario);
}
@Test
public void step_result_contains_only_the_current_scenarios_severest_result() {
// given
final AndroidInstrumentationReporter formatter = new AndroidInstrumentationReporter(runtime, instrumentation, 2);
when(firstResult.getStatus()).thenReturn(Result.FAILED);
when(firstResult.getError()).thenReturn(new AssertionError("some assertion went wrong"));
when(firstResult.getErrorMessage()).thenReturn("some assertion went wrong");
when(secondResult.getStatus()).thenReturn(Result.PASSED);
// when
formatter.feature(feature);
formatter.startOfScenarioLifeCycle(scenario);
formatter.result(firstResult);
formatter.endOfScenarioLifeCycle(scenario);
formatter.startOfScenarioLifeCycle(scenario);
formatter.result(secondResult);
formatter.endOfScenarioLifeCycle(scenario);
// then
final InOrder inOrder = inOrder(instrumentation);
final ArgumentCaptor<Bundle> firstCaptor = ArgumentCaptor.forClass(Bundle.class);
final ArgumentCaptor<Bundle> secondCaptor = ArgumentCaptor.forClass(Bundle.class);
inOrder.verify(instrumentation).sendStatus(eq(StatusCodes.FAILURE), firstCaptor.capture());
inOrder.verify(instrumentation).sendStatus(eq(StatusCodes.OK), secondCaptor.capture());
}
}