Why can't JUnit Platform find my cucumber tests? - kotlin

I am currently running my cucumber scenarios like this with JUnit 4:
#RunWith(Cucumber::class)
#CucumberOptions(
features = ["src/features"],
tags = "not #ignored"
)
class RunCucumberTest
I am trying to get the same working on JUnit Platform, and currently have:
#Suite
#IncludeEngines("cucumber")
#SelectDirectories("src/features")
#ConfigurationParameter(key = Constants.PLUGIN_PROPERTY_NAME, value = "pretty")
#ConfigurationParameter(key = Constants.GLUE_PROPERTY_NAME, value = "garden.ephemeral.rocket")
class RunCucumberTest
I have updated the stuff on the Gradle side as well:
tasks.withType<Test> {
useJUnitPlatform {
// TODO: Not needed? Doesn't seem to work with or without.
includeEngines("cucumber")
}
jvmArgs("--add-modules=jdk.incubator.vector")
// Workaround. Gradle does not include enough information to disambiguate
// between different examples and scenarios.
// TODO: Move to cucumber.properties?
systemProperty("cucumber.junit-platform.naming-strategy", "long")
// TODO: Move to junit-platform.properties?
systemProperty("junit.jupiter.execution.parallel.enabled", "true")
}
When I run Gradle, the :test task runs, but the test report is empty.
When I try to run the test class from IDEA, I get:
> No tests found for given includes: [garden.ephemeral.rocket.RunCucumberTest](--tests filter)
How do I make this work?
Investigation so far:
I also tried moving the features into the resources and using #SelectClasspathResource("features") instead, but got the same result.
I tried cloning this project and it does run its scenarios, but everything is written in Java instead of Kotlin. Other than that, everything is mostly the same between the two. That project uses #SelectClasspathResource instead of #SelectDirectories, but I already tried that. I also tried using #SelectDirectories over in that project, and that works too.
If I breakpoint inside DiscoverySelectors.selectDirectory, it doesn't seem to stop there. Best guess is, maybe JUnit isn't even finding and running the suite? Or maybe breakpointing inside JUnit just doesn't work.
Best current idea is to convert that skeleton project to Kotlin and see if it still runs.

At a glance this doesn't look right:
useJUnitPlatform {
// TODO: Not needed? Doesn't seem to work with or without.
includeEngines("cucumber")
}
With the #Suite annotation you are using the JUnit Platform Suite engine to kick of the Cucumber engine. By setting the included engines to cucumber you exclude the suite engine from gradles discovery process.
Note that the skeleton project uses:
tasks.withType<Test> {
useJUnitPlatform()
// Work around. Gradle does not include enough information to disambiguate
// between different examples and scenarios.
systemProperty("cucumber.junit-platform.naming-strategy", "long")
}

Related

How do I separate gradle unit tests from integration tests in the same source set?

The problem is that we are an IntelliJ shop and this issue has been a thorn in our sides. It basically means that everything has to be in ./test/ in order to work. ./it/ isn't acceptable because IntelliJ picks it up as the wrong kind of source every time you try to do anything. So.... how do I separate integration tests from unit tests so that they can be run separately in Gradle if they are in the same source set? Anyone have an example?
We use the *Test*.java and *ITCase*.java naming conventions, if that helps. Anothing thing we were thinking of is some kind of use of JUnit's #Category annotation.
P.S. Please vote for this issue. It will be a thorn in the side of any IntelliJ shop considering Gradle that has integration tests in a different directory from unit tests.
I can't speak to IntelliJ configuration, but in build.gradle, if you have:
test {
if (! project.hasProperty("ITCASE")) {
exclude "**/*ITCase*"
}
}
then the following command-line would include the integration tests:
gradle test -PITCASE=true
then and the standard would exclude them:
gradle test

Excluding anonymous classes in tests

After some modifications for test configuration
test {
scanForTestClasses = false
include '**/*Test.class'
include 'lt/inventi/apollo/scenarios/**'
}
My tests started to fail with error java.lang.Exception: Test class should have exactly one public constructor
so my workaround is like so
test {
scanForTestClasses = false
include '**/*Test.class'
include 'lt/inventi/apollo/scenarios/**'
exclude '**/*$*' //fixed
}
Is this correct way?
Once you turn off test class scanning, it's your responsibility to filter out all non-test classes. If you can't be more specific than scenarios/**, you'll have to compensate with an exclude (like you already do).
Note that it's perfectly fine to use test class scanning together with include/exclude filters.
I had this issue exclusively on a mac, and found 2 issues:
There was a bug #4544 on Gradle versions prior to 4.7, causing inner classes to be picked up
The mac JVM I happen to use (Zulu 1.8, installed via SDKMan) had issues when namespaces were capitalised (and the associated folders were too). This was fine with Zulu 1.8 in ubuntu, but lower-casing the folders seemed to do the trick

gradle test phases?

Is there a way to build custom test phases in gradle?
I have a series of tests, namely
/unit
/local
/remote
which is running JUnit managed tests for a couple different purposes. I'd love to be able to run these as test phases, such as gradle test:unit, or something like that. Ideally, I could also specify a description for each test phase, so gradle tasks describes the role of each of the tests.
Is this possible? If so, how?
I tried this:
sourceSets {
unit {
java.srcDir file('src/test/groovy/local')
resources.srcDir file('src/local/resources')
}
}
dependencies {...}
task unitTest(type: Test){
testClassesDir = sourceSets.local.output.classesDir
classpath = sourceSets.local.runtimeClasspath
}
...as copied from withIntegrationTests in the provided samples, but when I run gradle unitTest, no tests run.
You just need to add three Test tasks (testUnit, testLocal, testRemote) and configure them accordingly. (Instead of introducing testUnit, you could also reuse the test task that comes with the Java plugin.) If the sources are in different source folders, it also makes sense to add additional source sets. For the details, have a look at samples/java/withIntegrationTests in the full Gradle distribution.

Running Scala tests automatically either after test change or tested class change

I'm wondering if there is any solution to let Scala tests run automatically upon change of test class itself or class under the test (just to test automatically pairs Class <---> ClassTest) would be a good start.
sbt can help you with this. After you setup project, just run
~test
~ means continuous execution. So that sbt will watch file system changes and when changes are detected it recompiles changed classes and tests your code. ~testQuick can be even more suitable for you, because it runs only tests, that were changed (including test class and all it's transitive dependencies). You can read more about this here:
http://code.google.com/p/simple-build-tool/wiki/TriggeredExecution
http://php.jglobal.com/blog/?p=363
By the way, ~ also works with other tasks like ~run.

Can I run JUnit 4 to test Scala code from the command line?

If so, how? I haven't come across the proper incantation yet.
If not, what's the best approach to unit-testing Scala code from the command line? (I'm a troglodyte; I use IDEs when I have to, but I prefer to play around using Emacs and command-line tools.)
Since compiled Scala is just Java bytecode (OK, with a lot more $ characters in class names), it would be exactly as for running JUnit 4 tests against Java code, i.e. from the command line by passing the test classes as arguments to org.junit.runner.JUnitCore. As JUnit 4 out of the box only has command line support, you don't even have to worry about suppressing GUI based test runners.
That said, the specific Scala test frameworks (ScalaTest, ScalaCheck) do provide a more idiomatic set approach to testing code written in this more functional language.
You may be interested in ScalaTest.
ScalaTest is a free, open-source
testing tool for Scala and Java
programmers. It is written in Scala,
and enables you to write tests in
Scala to test either Scala or Java
code. It is released under the Apache
2.0 open source license.
Because different developers take
different approaches to creating
software, no single approach to
testing is a good fit for everyone. In
light of this reality, ScalaTest is
designed to facilitate different
styles of testing.
See the Runner documentation for how to run tests from the command line.
The suggestion of ScalaTest -- or any of the other Scala-specific frameworks, for that matter, is very good. I'd like to point to something else, though.
SBT.
SBT is a build tool, like Ant, Maven or Make. One interesting aspect of it, which will matter to us, is that it is Scala-based. I don't mean it has special capabilities to handle Scala code or that it is written in Scala, though both these things are true. I mean it uses Scala code, instead of XML like Maven and Ant, as the configuration source.
That in itself is interesting. Just today I saw a wonderful example of separating test sources from program sources, which I post here just because its so cool.
// on this project we keep all sources, whether they be Scala or Java, and whether they be
// regular classes or test classes, in a single src tree.
override def mainScalaSourcePath = "src"
override def mainJavaSourcePath = "src"
override def testScalaSourcePath = "src"
override def testJavaSourcePath = "src"
override def mainResourcesPath = "resources"
// distinguish main sources from test sources
def testSourceFilter =
"Test*.scala" | "Test*.java" |
"AbstractTest*.scala" | "AbstractTest*.java" |
"ScalaTestRunner.scala"
def mainSourceFilter = ("*.scala" | "*.java") - testSourceFilter
override def mainSources = descendents(mainSourceRoots, mainSourceFilter)
override def testSources = descendents(testSourceRoots, testSourceFilter)
But what makes it even more interesting is that SBT works like a console. You run "sbt", and you get dropped into a console-like interface, from which you can type commands like, for instance, "test", and have your tests run.