Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: Fix Cucumber execution on Gradle #1094

Merged
merged 12 commits into from
Feb 18, 2018
Merged

Conversation

gnuechtel
Copy link
Contributor

  • the connected check tasks of the Android/Gradle build system do not like non-unique test names
  • we add a unique index to the test names if they are non-unique (e.g. on scenario outlines with multiple examples)
  • for this bug fix, we provide a unit test

Summary

Fix Android test execution for Gradle builds on scenario outlines with multiple examples

Details

If you would use the current implementation of Cucumber Android with the connected-check Gradle tasks plus scenario outlines and multiple examples, not all tests will be executed. With this fix, it will be executed.

Motivation and Context

This change fixes the great Cucumber software on scenario outlines with examples.
As I know, there is no open issue for this bug fix.

How Has This Been Tested?

Automatic tests have been added.

The changes have been applied to the existing Cukeulator example project on my local machine.

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue).
  • New feature (non-breaking change which adds functionality).
  • Breaking change (fix or feature that would cause existing functionality to not work as expected).

Checklist:

  • I've added tests for my code.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.

* the connected check tasks of the Android/Gradle build system do not like non-unique test names
* we add a unique index to the test names if they are non-unique (e.g. on scenario outlines with multiple examples)
* for this bug fix, we provide a unit test
@lsuski
Copy link
Contributor

lsuski commented Dec 7, 2017

This is very needed beacuse connectedAndroidTest task is currently unusable with Cucumber.
@cgnuechtel could you update this PR to match current version AndroidInstrumentationReporter source code?

# Conflicts:
#	android/src/main/java/cucumber/runtime/android/AndroidInstrumentationReporter.java
#	android/src/test/java/cucumber/runtime/formatter/AndroidInstrumentationReporterTest.java
* the connected check tasks of the Android/Gradle build system do not like non-unique test names
* we add a unique index to the test names if they are non-unique (e.g. on scenario outlines with multiple examples)
* for this bug fix, we provide a unit test

This is a re-integration of PR-1094. The original pull request code was performed on 1.2.6-SNAPSHOT base. This code bases on version 2.3.2-SNAPSHOT.
@coveralls
Copy link

Coverage Status

Coverage increased (+0.06%) to 81.547% when pulling 4e1a79d on cgnuechtel:master into 68c8dbf on cucumber:master.

@gnuechtel
Copy link
Contributor Author

gnuechtel commented Jan 20, 2018

@mlvandijk the code is now up-to-date again

Please note, that this pull request only fixes ambiguous test names in Gradle HTML reports.

For a proper Android project configuration I will provide an updated version of the Cukealator project within this pull request with the next Git push.

…ools

- Update to Android Studio 3.0.1, SDK 26+ and Gradle 4.1
- Replace Instrumentation class by CucumberRunner
- CucumberRunner uses AndroidJUnitRunner (JUnit 4+)
- CalculationSteps class uses ActivityTestRule instead of deprecated ActivityInstrumentationTestCase2
- Fix permissions to write reports on internal storage
@gnuechtel gnuechtel changed the title Fix AndroidInstrumentationReporter for Gradle builds Fix Cucumber execution on Gradle Jan 20, 2018
@coveralls
Copy link

coveralls commented Jan 20, 2018

Coverage Status

Coverage increased (+0.06%) to 81.544% when pulling 9b63692 on cgnuechtel:master into 68c8dbf on cucumber:master.

* Update README.md
* Enable local Maven dependencies for better development experience
* Describe, how to use Cukeulator example project with locally built Cucumber-JVM
@gnuechtel
Copy link
Contributor Author

I created issue 1314 with some extra information how this pull request may be applied.

Please feel free to make suggestions for improvements. Maybe some code (e.g. CucumberRunner) may be transferred from the Cukeulator example project to the Cucumber Android project.

@brasmusson
Copy link
Contributor

As the JUnit formatter already has a similar handling of making test cases names unique, I think we should use consistent behavior here, that is

  • if the test case name does not include a blank (" "), append "_2", "_3" etc.
  • if the test case name include a blank, append " 2", " 3" etc.

A rational for not using a blank in the first case, and to stay away from ( and ) in the second case is to reduce the possibility for file name issues like in #972.

@gnuechtel gnuechtel changed the title Fix Cucumber execution on Gradle Android: Fix Cucumber execution on Gradle Jan 21, 2018
@gnuechtel
Copy link
Contributor Author

@brasmusson Thanks for your feedback! You are right! Changed the code right now.

@gnuechtel
Copy link
Contributor Author

Is anyone available to review this pull request?

@mlvandijk mlvandijk added the Bug label Jan 30, 2018
Copy link
Member

@mlvandijk mlvandijk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I don't use Android so cannot comment on that. Some other feedback provided below.

androidTestCompile 'io.cucumber:cucumber-android:2.3.0'
//
// The following dependency works, if you build Cucumber-JVM on your local machine:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be added to the android README instead of in comments here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mlvandijk Thanks for your feedback

Please look at the end of README.md

There is a reference to build.gradle. I think it is better to leave the technical details in build.gradle since it does not affect most users.

.invokeGetUniqueTestNameOnDifferentStatesInTestCaseLifecycle()
);

// TODO Consider to split this test method into multiple ones, to fulfil the one-assert-per-test-method-paradigm
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be done? (or remove "TODO"). In general, PR contains a several comments that should possible either be javadoc or be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mlvandijk Thanks again for your feedback

I removed the TODO comment, added/improved comments and hopefully made the test method more readable

@lsuski
Copy link
Contributor

lsuski commented Jan 30, 2018

I've made some refactoring to avoid duplicating code

@brasmusson
Copy link
Contributor

Is anyone available to review this pull request?

I will definitely not have time to look at it before the weekend.

@lsuski
Copy link
Contributor

lsuski commented Jan 30, 2018

I've run all my Android tests with this PR but for 653 test cases got 648 reports. I need to investigate it further. Without this PR I have about 350 reports.

@lsuski
Copy link
Contributor

lsuski commented Jan 31, 2018

I had 2 features with the same name and first of them was overwritten in gradle report. I'm not sure if feature name conflict should be somehow resolved by AndroidInstrumentationReporter or it is just bad practice and should be avoided. For me this PR can be merged as is.

Copy link
Contributor

@brasmusson brasmusson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I apologize for delayed review.

The behavior works, but I think that getUniqueTestName and ensureUniqueTestName should be made into one method, and that the test should be divided so that the test names provides specification/documentation of the behavior under test.

* More technical: Checks, if {{@link AndroidInstrumentationReporter#getUniqueTestName(TestCase)}} is working.
*
* Note about the test code: We are using multiple assertions and for-loops in this method, which is a code smell.
* Here it is okay, since we are just answering the question if the getUniqueTestName-method is working or not.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a big fan of "Test method names should be sentences (that specify/document the item under test)" (see https://dannorth.net/introducing-bdd/), so I do not think this test is okay. The interesting behavior that should be specified/documented in test method names includes:

  • test case names are made unique with numbered suffices
  • the numbered suffices do not include blanks when test case names do not include blanks
  • out of order identical test case names are also made unique
  • running a test case several times will use the same name each time
  • identical test case names in different feature will not be changed

currentTestCaseName = testCase.getName();
// Since the names of test cases are not guaranteed to be unique, we must check for unique names
ensureUniqueTestName(testCase);
currentTestCaseName = getUniqueTestName(testCase);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What we want to do here is to calculate the unique name for the current test case, to divide this into two methods which are dependent and need to be called in order i s IMHO not the right solution (if this is driven from "command query separation", I do not think it is the right place for it). I think getUniqueTestName (maybe renamed calculateUniqueTestName to indicate that it is not just a getter) should both do the work and return the result.

* @param testCase the testCase
* @return the unique test name or {@code null}
*/
String getUniqueTestName(TestCase testCase) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this method did all the work I could be okay with it being package private and all details of its behavior tested by direct calls from the test class. Currently when its only use in test is to fetch data after startTestCase has been executed from the tests, I think it should be made private and the tests get the test names from the instrumentation (like in this test)

@@ -458,6 +464,128 @@ public void step_result_contains_only_the_current_scenarios_severest_result() {
inOrder.verify(instrumentation).sendStatus(eq(StatusCodes.OK), secondCaptor.capture());
}

/**
* Verifies, that different test names are used when scenario names are duplicated.
* More technical: Checks, if {{@link AndroidInstrumentationReporter#getUniqueTestName(TestCase)}} is working.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, the behavior tested is performed by ensureUniqueTestName (but I'm suggesting to change that).

}

// when
private void invokeGetUniqueTestNameOnDifferentTestCaseStates() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just confusing. The unique name is calculated once (in startTestCase), so calling getUniqueTestName several times during the test life cycle cannot be relevant. This is a good reason why getUniqueTestName should be private.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brasmusson Thanks for your review. It was very helpful

I agree with your point, that my tests are too low-level, will correct it later

/**
* Creates a unique test name for the given test case by filling the internal maps
* {@link #uniqueTestNameForTestCase} and {@link #uniqueTestNamesForFeature}.<br/>
* If the test case name is unique, it will be used, otherwise, a index will be added "(2)", "(3)", "(4)", ...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"(2)" etc, is not correct anymore.


/**
* Gets the unique test name for the given test case.<br/>
* If, {@link #ensureUniqueTestName(TestCase)} was not yet invoked for the test case, {@code null} will be returned.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This indicate the need to make a check for null when calling getUniqueTestName but it should not be used in such a way that will happen. For me it is another reason why getUniqueTestName and ensureUniqueTestName should be made into one method.

- Create merged method calculateUniqueTestName from getUniqueTestName and ensureUniqueTestName and make it private
- Use better test method name: test_case_names_are_unique_on_equal_scenario_names (instead of scenario_outline_all_test_names_unique)
- Refactor test code: now it should be readable
@gnuechtel
Copy link
Contributor Author

@brasmusson Your suggestions should be implemented now. Thanks again!

The test method is still a little bit long with some for-loops, but I think that should be okay here.
Before it was unreadable. Hopefully it is usable now.

@gnuechtel
Copy link
Contributor Author

@brasmusson Can you look again at this pull request, please?

To provide better documentation of the functionality, that is:
* test names within feature are made unique by appending blank and
  number
* test names within are made unique by appending underscore and number
  when no blank in name
* test names in different features can be the same
* test names are made unique also when not consecutive
@brasmusson
Copy link
Contributor

@cgnuechtel I was not clear enough that I wanted the test case split up. I fixed that in 9b63692, now the test case names provide the documentation:

  • test names within feature are made unique by appending blank and
    number
  • test names within are made unique by appending underscore and number
    when no blank in name
  • test names in different features can be the same
  • test names are made unique also when not consecutive

@brasmusson brasmusson merged commit d8406dd into cucumber:master Feb 18, 2018
@aslakhellesoy
Copy link
Contributor

Hi @cgnuechtel,

Thanks for your making your first contribution to Cucumber, and welcome to the Cucumber committers team! You can now push directly to this repo and all other repos under the cucumber organization! 🍾

In return for this generous offer we hope you will:

  • ✅ Continue to use branches and pull requests. When someone on the core team approves a pull request (yours or someone else's), you're welcome to merge it yourself.
  • 💚 Commit to setting a good example by following and upholding our code of conduct in your interactions with other collaborators and users.
  • 💬 Join the community Slack channel to meet the rest of the team and make yourself at home.
  • ℹ️ Don't feel obliged to help, just do what you can if you have the time and the energy.
  • 🙋 Ask if you need anything. We're looking for feedback about how to make the project more welcoming, so please tell us!

On behalf of the Cucumber core team,
Aslak Hellesøy
Creator of Cucumber

brasmusson added a commit that referenced this pull request Feb 18, 2018
[SKIP CI]
@gnuechtel
Copy link
Contributor Author

@brasmusson and @aslakhellesoy

Thanks for the good news 😊

@lock
Copy link

lock bot commented Feb 19, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Feb 19, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants