Do we always need a separate runner class for cucumber tests? - cucumber-jvm

Almost every reference in the web for cucumber project has a separate runner class with nothing in class body. Seems like a waste of class.
My case, the following is the project skeleton
RestTestContext.java
public class RestTestContext {
private Map<String, String> restParams = new HashMap<>();
public void setRestParams(Map<String, String> params) {
this.restParams.putAll(params);
}
public Map<String, String> getRestParams() {
return this.restParams;
}
}
Next I have the StepDefinitions.Java
And a
TestRunner.Java having,
#RunWith(Cucumber.class)
#CucumberOptions(features = {"src/test/resources/features/"},
tags = {"#run"},
dryRun = false,
monochrome = true
)
public class TestRunner{
}
This works totally fine, of course.
Now I could also do the same thing by modifying the RestTestContext.javaas below
#RunWith(Cucumber.class)
#CucumberOptions(features = {"src/test/resources/features/"},
tags = {"#run"},
dryRun = false,
monochrome = true
)
public class RestTestContext {
private Map<String, String> restParams = new HashMap<>();
public void setRestParams(Map<String, String> params) {
this.restParams.putAll(params);
}
public Map<String, String> getRestParams() {
return this.restParams;
}
}
This makes me not to have an empty class just to notify JUnit on how to run. But to have some useful class body as well.
The question: Is it just a best practice to have a separate runner class? Or does it contribute to other factors as well?

Afaik if you want to run your Cucumber tests with JUnit, you'll need a runner class. You can check the documentation for more information.
Note that you are also using this class to specify your #CucumberOptions, telling Cucumber & JUnit how to run your tests.
Note that you can also run Cucumber from the command line. In that case you will not be using JUnit.
Finally, you can also run your tests from your IDE. How to do so, will depend on which IDE you are using. You can find some info on Java IDE's in the documentation.

Related

Kotlin NoArg plugin ignores declaration assignment

This problem is related to Kotlin noarg plugin not initializing default values.
I've got my NoArg plugin set up like this:
plugins {
id "org.jetbrains.kotlin.plugin.noarg" version "1.7.10"
}
noArg {
annotation("tools.AddEmptyConstructor")
}
Now, this kotlin code:
import tools.AddEmptyConstructor
#AddEmptyConstructor
class Test {
private constructor(test: String)
private val declarationAssignment = ArrayList<String>()
}
compiles to this java code:
import tools.AddEmptyConstructor;
public final class Test {
private final ArrayList declarationAssignment;
private Test(String test) {
this.declarationAssignment = new ArrayList();
}
public Test() {
}
}
I get that the empty constructor is supposed to be empty, but why is the declaration assignment, which isn't part of the constructor during compile time, not also being copied into the empty constructor?
How is that intended to be done? I don't have to use declaration assignments and I would be happy with workarounds, but I cannot access the empty constructor and thus not initialize values at all.

Micronaut-Core: How to create dynamic endpoints

Simple question. Is it possible to create endpoints without #Endpoint?
I want to create rather dynamic endpoints by a file and depending on the content to its context.
Thanks!
Update about my idea. I would to create something like a plugin system, to make my application more extensible for maintenance and future features.
It is worth to be mentioned I am using Micronaut with Kotlin. Right now I've got fixed defined Endpoints, which matches my command scripts.
My description files will be under /src/main/resources
I've got following example description file how it might look like.
ENDPOINT: GET /myapi/customendpoint/version
COMMAND: """
#!/usr/bin/env bash
# This will be executed via SSH and streamed to stdout for further handling
echo "1.0.0"
"""
# This is a template JSON which will generate a JSON as production on the endpoint
OUTPUT: """
{
"version": "Server version: $RESULT"
}
"""
How I would like to make it work with the application.
import io.micronaut.docs.context.events.SampleEvent
import io.micronaut.context.event.StartupEvent
import io.micronaut.context.event.ShutdownEvent
import io.micronaut.runtime.event.annotation.EventListener
#Singleton
class SampleEventListener {
/*var invocationCounter = 0
#EventListener
internal fun onSampleEvent(event: SampleEvent) {
invocationCounter++
}*/
#EventListener
internal fun onStartupEvent(event: StartupEvent) {
// 1. I read all my description files
// 2. Parse them (for what I created a parser)
// 3. Now the tricky part, how to add those information to Micronaut Runtime
val do = MyDescription() // After I parsed
// Would be awesome if it is that simple! :)
Micronaut.addEndpoint(
do.getEndpoint(), do.getHttpOption(),
MyCustomRequestHandler(do.getCommand()) // Maybe there is a base class for inheritance?
)
}
#EventListener
internal fun onShutdownEvent(event: ShutdownEvent) {
// shutdown logic here
}
}
You can create a custom RouteBuilder that will register your custom endpoints at runtime:
#Singleton
class CustomRouteBuilder extends DefaultRouteBuilder {
#PostConstruct
fun initRoutes() {
val do = MyDescription();
val method = do.getMethod();
val routeUri = do.getEndpoint();
val routeHandle = MethodExecutionHandle<Object, Object>() {
// implement the 'MethodExecutionHandle' in a suitable manner to invoke the 'do.getCommand()'
};
buildRoute(HttpMethod.parse(method), routeUri, routeHandle);
}
}
Note that while this would still feasible, it would be better to consider another extension path as the solution defeats the whole Micronaut philosophy of being an AOT compilation framework.
It was actually pretty easy. The solution for me was to implement a HttpServerFilter.
#Filter("/api/sws/custom/**")
class SwsRouteFilter(
private val swsService: SwsService
): HttpServerFilter {
override fun doFilter(request: HttpRequest<*>?, chain: ServerFilterChain?): Publisher<MutableHttpResponse<*>> {
return Flux.from(Mono.fromCallable {
runBlocking {
swsService.execute(request)
}
}.subscribeOn(Schedulers.boundedElastic()).flux())
}
}
And the service can process with the HttpRequest object:
suspend fun execute(request: HttpRequest<*>?): MutableHttpResponse<Feedback> {
val path = request!!.path.split("/api/sws/custom")[1]
val httpMethod = request.method
val parameters: Map<String, List<String>> = request.parameters.asMap()
// TODO: Handle request body
// and do your stuff ...
}

Spock unit testing a Kotlin private static method

I'm looking for help to debug/resolve the obscure error:
java.lang.IllegalArgumentException: wrong number of arguments
I don't want to get into an argument of whether or not I should be testing a private method. But, I'm open to learning how to restructure this into something more testable that isn't exposed to the plugin user. I just can't seem to make it work. Below is the signature of the method and the relevant code.
Output from println in test setup
public static final org.gradle.api.file.DirectoryProperty my.project.MyPlugin.access$getResolvedDir(my.project.MyPlugin,java.lang.Object,org.gradle.api.Project)
Class under test
class MyPlugin : Plugin<Project> {
override fun apply(project: Project): Unit = project.run {
// do some gradle plugin stuff
myTask.configure { dir = getResolvedDir(pluginExtension.dir, project) }
}
private fun getResolvedDir(dir: Any?, project: Project): DirectoryProperty {
// do some stuff to transform the input to a DirectoryProperty
return resolvedDir
}
}
Spock test case
class MyPluginTest extends Specification {
#Rule
public final TemporaryFolder testProjectDir = new TemporaryFolder()
private Project p
private MyPlugin plugin
private CachedMethod getResolvedDirMethod
def setup() {
p = ProjectBuilder.builder().withName("install-plugin-test").build()
plugin = new InstallPlugin()
plugin.metaClass.methods.each {
if (it.name.contains("getResolvedDir"))
getResolvedDirMethod = it
}
getResolvedDirMethod.setAccessible()
println getResolvedDirMethod
}
def "String resolution for dir"() {
when:
def x = "xyz"
then:
DirectoryProperty dir = getResolvedDirMethod.invoke(plugin, x, p)
}
}

Accessing a Kotlin extension function from Java

Is it possible to access extension functions from Java code?
I defined the extension function in a Kotlin file.
package com.test.extensions
import com.test.model.MyModel
/**
*
*/
public fun MyModel.bar(): Int {
return this.name.length()
}
Where MyModel is a (generated) java class. Now, I wanted to access it in my normal java code:
MyModel model = new MyModel();
model.bar();
However, that doesn't work. The IDE won't recognize the bar() method and compilation fails.
What does work is using with a static function from kotlin:
public fun bar(): Int {
return 2*2
}
by using import com.test.extensions.ExtensionsPackage so my IDE seems to be configured correctly.
I searched through the whole Java-interop file from the kotlin docs and also googled a lot, but I couldn't find it.
What am I doing wrong? Is this even possible?
Perhaps like this:
// CallExtensionFunction.java
package com.example.groundup;
public class CallExtensionFunction {
public static void main(String[] args) {
MyModel myModel = new MyModel();
int bar = MyModelKt.bar(myModel);
System.out.println(bar);
}
}
// MyModell.kt
package com.example.groundup
fun MyModel.bar(): Int {
return this.name.length
}
class MyModel() {
val name = "Hugo"
}
The extension function is provided in the corresponding singleton with the suffix "Kt" as a static method.

How to Implement ITestListener in Cucumber+Selenium+Java

I've created a project where am trying to run Cucumber+Selenium+Java tests using TestNG plugin.
My Runner class is :
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
#CucumberOptions(features = "src/test/resources", monochrome = true, glue = "com.ipkgs.stepdefinitions", plugin = {
"html:target/cucumber-html-report", "json:target/cucumber-json-report.json" }, dryRun = false)
public class CucumberTests extends AbstractTestNGCucumberTests {
#DataProvider(parallel = true)
public Object[][] scenarios() {
return super.scenarios();
}
}
I am creating a listener class where I want to get the result details as well as the scenario details like at which scenario failed at which step.
I am extending ItestListener but it is only giving me the pass/fail result without scenario details.
Is there any way to achieve this?
You shouldn't use TestNG listeners for that. While Cucumber "knows" about TestNG, TestNG in turn does not know anything about Cucumber. You need to use hooks mechanism for watching your scenario execution status.
Example from documentation:
#After
public void doSomethingAfter(Scenario scenario){
// Do something after after scenario
}
#AfterStep
public void doSomethingAfterStep(Scenario scenario){
// Do something after after step
}