Kotest and kotlinx-coroutines-test Integration - kotlin-coroutines

I use the Funspec testing style in kotest and I get a coroutineScope injected automatically by the framework as shown below.
class MyTestSpec: FunSpec() {
init {
test("test event loop") {
mySuspendedFunction() // a coroutineScope is already injected by the test framework here
}
}
}
How can I configure the Kotest framework to use an instance of kotlinx.coroutines.test.TestCoroutineScope instead of a kotlinx.coroutines.CoroutineScope in my tests? or is there a reason why this wouldn't make sense?

Since Kotest 5.0, there is built-in support for TestCoroutineDispatcher. See here
Simply:
class MyTest : FunSpec(
{
test("do your thing").config(testCoroutineDispatcher = true) {
}
}
)

Create a test listener like this:
class MainCoroutineListener(
val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
) : TestListener {
override suspend fun beforeSpec(spec: Spec) {
Dispatchers.setMain(testDispatcher)
}
override suspend fun afterSpec(spec: Spec) {
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
}
Then use it in your test like this
class MyTest : FunSpec({
listeners(MainCoroutineListener())
tests...
})

Related

Configure default Kotlin coroutine context in Spring MVC

I need to configure default coroutine context for all requests in Spring MVC. For example MDCContext (similar question as this but for MVC not WebFlux).
What I have tried
Hook into Spring - the coroutine code is here but there is no way to change the default behavior (need to change InvocableHandlerMethod.doInvoke implementation)
Use AOP - AOP and coroutines do not play well together
Any ideas?
This seems to work:
#Configuration
class ContextConfig: WebMvcRegistrations {
override fun getRequestMappingHandlerAdapter(): RequestMappingHandlerAdapter {
return object: RequestMappingHandlerAdapter() {
override fun createInvocableHandlerMethod(handlerMethod: HandlerMethod): ServletInvocableHandlerMethod {
return object : ServletInvocableHandlerMethod(handlerMethod) {
override fun doInvoke(vararg args: Any?): Any? {
val method = bridgedMethod
ReflectionUtils.makeAccessible(method)
if (KotlinDetector.isSuspendingFunction(method)) {
// Exception handling skipped for brevity, copy it from super.doInvoke()
return invokeSuspendingFunctionX(method, bean, *args)
}
return super.doInvoke(*args)
}
/**
* Copied from CoroutinesUtils in order to be able to set CoroutineContext
*/
#Suppress("UNCHECKED_CAST")
private fun invokeSuspendingFunctionX(method: Method, target: Any, vararg args: Any?): Publisher<*> {
val function = method.kotlinFunction!!
val mono = mono(YOUR_CONTEXT_HERE) {
function.callSuspend(target, *args.sliceArray(0..(args.size-2))).let { if (it == Unit) null else it }
}.onErrorMap(InvocationTargetException::class.java) { it.targetException }
return if (function.returnType.classifier == Flow::class) {
mono.flatMapMany { (it as Flow<Any>).asFlux() }
}
else {
mono
}
}
}
}
}
}
}

Spring Boot Cucumber Java 8 Testing ApplicationOnReady Event

I have a reporting application that generates a report on ApplicationReadyEvent. I am trying to write cucumber tests for it but as the application event is fired even before my feature is executed , i am not sure what is the right way to test it. Can i control the event during testing ?
#EventListener(ApplicationReadyEvent.class)
private void generateAccuracyAnalysisReport() throws IOException
{
//some Logic
}
Cucumber Classes :
#SpringBootTest
#CucumberContextConfiguration
#ActiveProfiles("junit")
public class CucumberConfiguration
{
}
#RunWith(Cucumber.class)
#CucumberOptions(plugin = "pretty", features = "src/test/resources/cucumber/features")
public class CucumberFullIntegrationTest
{
}
Step Definition:
public class ReportStepDefs implements En {
public ReportStepDefs() {
When("^System sends an application event to generate report$", () -> {
});
Then("^Report should be generated successfully\\.$", () -> {
});
}
}
If your Cucumber tests involve Spring life-cycle you can not use cucumber-spring. Rather you have to use something like Springs ApplicationContextRunner to, configure, run and verify something about your application as part of each scenario.
// Given
ApplicationContextRunner contextRunner = new ApplicationContextRunner();
// When
contextRunner.withConfiguration(AutoConfigurations.of(...);
// Then
contextRunner.run(context -> assertThat(context).... /* something */ );
// Or assert something external to the application context.
Though it sounds like your application is doing something once and then exits. If so you should be using the CommandLineRunner instead of ApplicationReadyEvent in a web application. This is testable with cucumber-spring.
#RequiredArsConstructor
public class StepDefinitions {
final MyCommandLineRunner commandLineRunner;
#When(....)
public void something() {
commandLineRunner.run("input.txt", "input2.txt");
}
#Then(....)
public void assertSomething() {
// check if report was generated
}
}

How to configure Selenide remote grid url in Selenium-Jupiter test framework?

I am trying to configure a Selenide driver within the Selenium-Jupiter framework, to use my remote grid url but it keeps ignoring the configuration, and just runs the local installed browser. Here is how I am trying to configure it. Any idea what might be wrong here?
import com.codeborne.selenide.Configuration;
import com.codeborne.selenide.SelenideConfig;
import io.github.bonigarcia.seljup.SelenideConfiguration;
import static com.codeborne.selenide.Browsers.CHROME;
public abstract class ChromeTest extends BaseTest {
#SelenideConfiguration
SelenideConfig selenideConfig = new SelenideConfig();
private String getSeleniumRemote() {
System.getProperty("selenide.remote", "");
}
public ChromeTest() {
if (getSelenideRemote().isEmpty()) {
selenideConfig.proxyEnabled(false)
.browser(CHROME).startMaximized(false)
.browserSize("800x1200").browserPosition("50x60");
} else {
Configuration.timeout = 6000;
Configuration.remote = getSelenideRemote();
selenideConfig.proxyEnabled(false)
.startMaximized(true).browser(CHROME);
}
}
}
I know the regular RemoteWebDriver works and I can get that working but I am hoping to use Selenide in the above example:
Example:
#Test
void testWithRemoteSelenide(#DriverUrl("http://127.1:4444/wd/hub")
#DriverCapabilities("browserName=" + CHROME) SelenideDriver driver)
I can get it to work with the annotation, but the problem is that I need that annotation to be conditional on passing a param to the tests. I want to be able to easily switch using grid or local. Thanks for your help anyone.
Ok, after almost 48 hours an no reponse, I finally figured out the solution. Here it is:
//build.gradle
test {
useJUnitPlatform()
ignoreFailures = false
beforeTest { descriptor ->
logger.lifecycle("Running test: $descriptor.className")
}
systemProperty "env", System.getProperty("env")
def remote = System.getProperty("selenide.remote", "")
if (!remote.isEmpty()) {
systemProperty("selenide.remote", remote)
}
}
Then, in my test base class:
public abstract class ChromeTest extends BaseTest {
#SelenideConfiguration
SelenideConfig selenideConfig = new SelenideConfig();
/**
* This config is equivilant to the documented method:
* Example: test(#DriverUrl("http://127.1:4444/wd/hub")
* #DriverCapabilities("browserName=chrome") SelenideDriver sd)
*/
public ChromeFormTest() {
if (getSelenideRemote().isEmpty()) {
selenideConfig.proxyEnabled(false).proxyHost("http://proxy.domain.com")
.proxyPort(8080)
.browser(CHROME).startMaximized(false)
.browserSize("800x1200").browserPosition("50x60");
} else {
Configuration.timeout = 6000;
Configuration.remote = getSelenideRemote();
selenideConfig.proxyEnabled(false).proxyHost("http://proxy.domain.com")
.proxyPort(8080)
.startMaximized(false).browser(CHROME);
}
}
}
Then, when I execute, it looks like this:
gradle clean test -Denv=sys -Dselenide.remote=http://127.1:4444/wd/hub
--info --tests com.qa.suite.*
And the constructor of each test looks like:
#Test
public void testWhatever(SelenideDriver sd) {

Is there a concept of test suites in Gradle/Spock land?

Groovy/Gradle project here that uses Spock for unit testing.
Does Spock and/or Gradle support test suites or named sets of tests? For reasons outside the scope of this question, there are certain Spock tests (Specifications) that the CI server just can't run.
So it would be great to divide all my app's Spock tests into two groups:
"ci-tests"; and
"local-only-tests"
And then perhaps we could invoke them via:
./gradlew test --suite ci-tests
etc. Is this possible? If so, what does the setup/config look like?
You can annotate the tests that should not run in your CI server with the Spock annotation #IgnoreIf( ).
See the documentation here: https://spockframework.github.io/spock/docs/1.0/extensions.html#_ignoreif
All you need to do is let the CI server set an environment variable, and exclude the test class if that variable is set.
Spock even have properties inside the closure to make it easy:
#IgnoreIf({ sys.isCiServer })
I would set up a submodule my-app-ci-test, with the following in build.gradle:
test {
enabled = false
}
task functionalTest(type: Test) {
}
Then you place your tests in src/test/groovy and run ./gradlew functionalTest.
Alternatively, you could include them in the same module and configure the test and functionalTest tasks with includes / excludes
test {
exclude '**/*FunctionalTest.groovy'
}
task functionalTest(type: Test) {
include '**/*FunctionalTest.groovy'
}
If you use Junit test-runner for Spock tests, you may use #Category annotation. Example by article and official documentation:
public interface FastTests {
}
public interface SlowTests {
}
public interface SmokeTests
}
public static class A {
#Test
public void a() {
fail();
}
#Category(SlowTests.class)
#Test
public void b() {
}
#Category({FastTests.class, SmokeTests.class})
#Test
public void c() {
}
}
#Category({SlowTests.class, FastTests.class})
public static class B {
#Test
public void d() {
}
}
test {
useJUnit {
includeCategories 'package.FastTests'
}
testLogging {
showStandardStreams = true
}
}
You can use the following SpockConfiguration.groovy to allow passing include/exclude via system properties
runner {
exclude {
System.properties['spock.exclude.annotations']
?.split(',')
*.trim()
?.each {
try {
annotation Class.forName(it)
println "Excluding ${it}"
} catch (ClassNotFoundException e) {
println "Can't load ${it}: ${e.message}"
}
}
}
include {
System.properties['spock.include.annotations']
?.split(',')
*.trim()
?.each {
try {
annotation Class.forName(it)
println "Including ${it}"
} catch (ClassNotFoundException e) {
println "Can't load ${it}: ${e.message}"
}
}
}
}

how to check if a service implements an interface in grails

I have an interface "TestInterface" and grails service "TestService" that implements the "TestInterface". But when I test if I have a service that implements the interface like this:
application.serviceClasses?.each { serviceClazz ->
if(serviceClazz instanceof TestInterface) {
println "service name => "+ serviceClazz.name;
}
}
The result is I am not getting anything neither error nor my expectation ( service name => TestService )
I have also tried changing the serviceClazz to serviceClazz.class,serviceClazz.metaClass in the if condition but still not working.
Thank you,
What about:
if (TestInterface.class.isAssignableFrom(serviceClazz)) {
...
}
UPDATE
So I managed to run the actual example using Grails 2.3.11.
class BootStrap {
def grailsApplication
def init = { servletContext ->
grailsApplication.serviceClasses.each { serviceClazz ->
if (TestInterface.isAssignableFrom(serviceClazz.clazz)) {
println serviceClazz
}
}
}
def destroy = {
}
}
As you can see, the important part is clazz in serviceClazz.clazz.
Hope this helps!

Resources