I'm unable to use Koin 2.0.1 with Kotlin-test 3.4.2. I get an InvocationTargetException like this:
Running koinexample.KoinSampleTests
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.009 sec <<< FAILURE! - in koinexample.KoinSampleTests
koinexample.KoinSampleTests Time elapsed: 0.009 sec <<< ERROR!
java.lang.reflect.InvocationTargetException
at koinexample.KoinSampleTests.getKoin(KoinSampleTests.kt:26)
at koinexample.KoinSampleTests.<init>(KoinSampleTests.kt:61)
I've created a small example on GitHub that reproduces this error:
https://github.com/elifarley/kotlin-tests-with-koin-examples
Just execute these commands to clone the repo and run tests:
git clone https://github.com/elifarley/kotlin-tests-with-koin-examples.git
cd kotlin-tests-with-koin-examples
mvn
Here's the main Kotlin file:
package koinexample
import io.kotlintest.koin.KoinListener
import io.kotlintest.shouldBe
import io.kotlintest.specs.FreeSpec
import org.koin.core.inject
import org.koin.dsl.module
import org.koin.test.KoinTest
data class Stats(var ok: Long = 0, var error: Long = 0)
interface StatsServer {
fun newError(): Long
}
class StatsServerSimple(private val stats: Stats) : StatsServer {
override fun newError() = stats.error++
}
val appModule = module {
single { Stats() }
single { StatsServerSimple(get()) as StatsServer }
}
class KoinSampleTests : FreeSpec(), KoinTest {
private val modules = listOf(
appModule
)
override fun listeners() = listOf(KoinListener(modules))
val statsServer: StatsServer by inject()
init {
"Happy path" {
statsServer.newError() shouldBe 1
}
}
}
Your issue seems to be a simple import confusion.
Note that you're using import org.koin.core.inject, which is this function:
inline fun <reified T> KoinComponent.inject(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null
): Lazy<T> =
getKoin().inject(qualifier, parameters)
It needs getKoin to work, and therefore you cannot initialize your test (The test class is initialized before Koin had a chance to start with the listener).
The correct import is import org.koin.test.inject, which translates to:
inline fun <reified T> KoinTest.inject(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null
): Lazy<T> = lazy { get<T>(qualifier, parameters) }
Take note that this is indeed lazy, so Kotest will have a chance to initialize Koin before your tests start.
Fixing that import should resolve this issue
Seems like you're never starting the Koin app. You need to have
startKoin {
modules(appModule)
}
in your test method or in the beforeSpec/beforeTest function call of the spec.
Or use something like here:
override fun listeners() = listOf(KoinListener(appModule))
which is described in the documentation for kotlintest/kotest: https://github.com/Kotest/Kotest/blob/master/doc/extensions.md#koin
Related
Based on answer in this link, created a test suite with Test classes
#RunWith(Suite::class)
#Suite.SuiteClasses(
DTOtoEntityMapperTest::class,
PostDataSourceCoroutinesTest::class,
PostDataSourceRxJava3Test::class
)
class JUnit5TestSuite
Returns error
org.junit.runners.model.InvalidTestClassError: Invalid test class 'com.x.DTOtoEntityMapperTest':
1. No runnable methods
But every test class added has test methods and runs individually for instance
class DTOtoEntityMapperTest {
private val postDTOList by lazy {
convertFromJsonToObjectList<PostDTO>(getResourceAsText(RESPONSE_JSON_PATH))!!
}
private val postEntityList by lazy {
convertFromJsonToObjectList<PostEntity>(getResourceAsText(RESPONSE_JSON_PATH))!!
}
#Test
fun `given PostDTO is input, should return PostEntity`() {
val mapper = DTOtoEntityMapper()
// GIVEN
val expected = postEntityList
// WHEN
val actual = mapper.map(postDTOList)
// THEN
Truth.assertThat(actual).containsExactlyElementsIn(expected)
}
}
You can now run Suites purely with jUnit 5 engines:
import org.junit.platform.suite.api.SelectClasses
import org.junit.platform.suite.api.Suite
#Suite
#SelectClasses(BasicMockKUnitTest::class, HierarchicalMockKUnitTest::class)
#SelectPackages("exercise")
class TestAllSelectPackage {
}
You'll have to import the junit-platform-suite-engine:
https://search.maven.org/artifact/org.junit.platform/junit-platform-suite-engine
There's some docs here:
https://junit.org/junit5/docs/snapshot/user-guide/#launcher-api-engines-custom
https://junit.org/junit5/docs/snapshot/api/org.junit.platform.suite.engine/org/junit/platform/suite/engine/package-summary.html
Here's an official example:
https://github.com/junit-team/junit5/blob/main/documentation/src/test/java/example/SuiteDemo.java
For JUnit 5 I used JUnitPlatform and SelectClasses (or SelectPackages):
#RunWith(JUnitPlatform::class)
#SelectClasses(Some1Test::class, Some2Test::class)
class SuiteTest
After checking for a few days, I found correctly libs to use Junit 5 test suite with kotlin
In build.gradle, add 2 libs as below:
testImplementation ("org.junit.platform:junit-platform-suite-api:1.7.0")
testImplementation ("org.junit.platform:junit-platform-runner:1.2.0")
Then you can use test suite as below:
import org.junit.platform.runner.JUnitPlatform
import org.junit.platform.suite.api.SelectClasses
import org.junit.platform.suite.api.SelectPackages
import org.junit.runner.RunWith
#RunWith(JUnitPlatform::class)
#SelectClasses(BasicMockKUnitTest::class, HierarchicalMockKUnitTest::class)
#SelectPackages("exercise")
class TestAllSelectPackage {
}
I am writing groovy scripts for jenkins which defines my path names to some extent. I did try to reasearch the problem on stackoverflow and google and I could not figure out a good solution.
Here is a minimal example:
File Structure:
runTests.bat
vars/SemVer.groovy
tests/SemVerTests.groovy
The batch file executes my unit tests:
docker run --rm -v %cd%:/home/groovy/scripts -w /home/groovy/scripts groovy groovy tests/SemVerTests.groovy
SemVer.groovy
#!/usr/bin/env groovy
package vars
class SemVer {
private String _original
SemVer(String original) { this._original = original }
String toString() { return "${this._original}" }
}
SemVerTests.groovy
import groovy.util.GroovyTestSuite
import junit.framework.Test
import junit.textui.TestRunner
import vars.SemVer
class GetLatestSemVerShouldConstruct extends GroovyTestCase {
void testDisplay() {
def actual = new SemVer("Hello World!").toString()
assertToString(actual, "Hello World!")
} }
class AllTests {
static Test suite() {
def allTests = new GroovyTestSuite()
allTests.addTestSuite(GetLatestSemVerShouldConstruct.class)
return allTests
} }
TestRunner.run(AllTests.suite())
The problem is that I need to use SemVer.groovy in my jenkins scripts that live in vars/. To do that I assume I need to remove the line package vars.
When I do that I get:
Compilation incomplete: expected to find the class vars.SemVer in file:/home/groovy/scripts/vars/SemVer.groovy, but the file contains the classes: SemVer
How can I import the class to tests/SemVerTests.groovy without defining the package in SemVer?
Add vars to the classpath (see below), remove the package from SemVers and just import SemVers in your test.
$ find .
.
./tests
./tests/SemVerTests.groovy
./vars
./vars/SemVer.groovy
$ head -n 4 vars/SemVer.groovy
class SemVer {
private String _original
SemVer(String original) { this._original = original }
String toString() { return "${this._original}" }
$ head -n 8 tests/SemVerTests.groovy
import groovy.util.GroovyTestSuite
import junit.framework.Test
import junit.textui.TestRunner
import SemVer
class GetLatestSemVerShouldConstruct extends GroovyTestCase {
void testDisplay() {
def actual = new SemVer("Hello World!").toString()
$ groovy -cp vars tests/SemVerTests.groovy
.
Time: 0.035
OK (1 test)
I am trying to unit test some code that uses the webSocket function from rxjs6. I have tried to spy on the webSocket function by doing the following (as recommended here): -
import * as rxJsWebSocket from 'rxjs/webSocket';
subject = new Subject();
webSocketSpy = spyOn(rxJsWebSocket, 'webSocket').and.returnValue(<any>subject);
but I got the error: -
Error: <spyOn> : webSocket is not declared writable or has no setter
Is there any other way to achieve this or is there a workaround for the error?
I've also tried ts-mock-imports with no success.
It works for me using "rxjs": "^6.6.3". E.g.
index.ts:
import { webSocket } from 'rxjs/webSocket';
export function main() {
return webSocket('ws://localhost:8081');
}
index.test.ts:
import { main } from './';
import * as rxJsWebSocket from 'rxjs/webSocket';
import { Subject } from 'rxjs/internal/Subject';
describe('55415481', () => {
it('should pass', () => {
const subject = new Subject();
const webSocketSpy = spyOn(rxJsWebSocket, 'webSocket').and.returnValue(<any>subject);
const actual = main();
expect(actual).toBe(<any>subject);
expect(webSocketSpy).toHaveBeenCalledWith('ws://localhost:8081');
});
});
unit test result:
Executing 1 defined specs...
Running in random order... (seed: 21376)
Test Suites & Specs:
(node:74151) ExperimentalWarning: The fs.promises API is experimental
1. 55415481
✔ should pass (8ms)
>> Done!
Summary:
👊 Passed
Suites: 1 of 1
Specs: 1 of 1
Expects: 2 (0 failures)
Finished in 0.02 seconds
source code: https://github.com/mrdulin/jasmine-examples/tree/master/src/stackoverflow/55415481
Similar to: Run TestNG tests in random order but different as this question does not include invocations.
TestNG randomizes the tests themselves but the invocations will all run in a row. For Example:
#Test(invocation=2) public void A{}
#Test(invocation=2) public void B{}
will either run AABB or BBAA
but I was hoping to have them run in a truly random manner (ABAB, BABA, ect)
Is there anyway to accomplish this with TestNG, at the moment all I can think of is changing my tests to methods called by one test that controls the order but I hoping for something built in to TestNG that I overlooked.
You can build on the same concept of leveraging a IMethodInterceptor.
Here's how you do it.
First you build a custom annotation that can be used to express the invocation count.
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
#Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
#Target({METHOD, TYPE})
public #interface InvocationCount {
int value() default 1;
}
You now customize your method interceptor such that it looks up the custom annotation and based on it, it adds up more instances and then eventually randomizes the resultant collection.
import org.testng.IMethodInstance;
import org.testng.IMethodInterceptor;
import org.testng.ITestContext;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class LocalInterceptor implements IMethodInterceptor {
#Override
public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
List<IMethodInstance> altered = new ArrayList<>();
for (IMethodInstance method : methods) {
InvocationCount count = method.getMethod().getConstructorOrMethod().getMethod().getAnnotation(InvocationCount.class);
if (count == null) {
altered.add(method);
} else {
if (count.value() > 1) {
for (int i = 0; i < count.value(); i++) {
altered.add(method);
}
}
}
}
long seed = System.nanoTime();
Collections.shuffle(altered, new Random(seed));
return altered;
}
}
Your test would now look like below
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
#Listeners(LocalInterceptor.class)
public class SampleTestClass {
#InvocationCount(2)
#Test
public void A() {
System.err.println("A()");
}
#InvocationCount(3)
#Test
public void B() {
System.err.println("B()");
}
}
Here's a sample output (There were some StringIndexOutOfBoundsException exceptions from the reporters, but I think that's not related to this)
...
... TestNG 6.14.2 by Cédric Beust (cedric#beust.com)
...
B()
A()
B()
B()
A()
PASSED: B
PASSED: A
PASSED: B
PASSED: B
PASSED: A
===============================================
48715503_test
Tests run: 5, Failures: 0, Skips: 0
===============================================
===============================================
48715503_Suite
Total tests run: 5, Failures: 0, Skips: 0
===============================================
In a grails 2 project I'm using groovy's metaclass programming to add some methods to my domain classes.
Everything is working fine at runtime and I can run my integration tests fine.
But for unit tests I have some issues.
I have created a test mixin that is in charge of initializing the metaclass programming part.
This mixin is not running reliably:
the methods added to the metaclass are not available, or they are available after a first call, or they are available only after a previous grails test-app unit: command has been called.
This is quite a problem for continuous build.
You should be able to reproduce this issue (at least with grails 2.0.4) by
0) create a new grails projects
1) add a domain object
create-domain-class playground.Data
2) add this class to your src/groovy/playground dir
package playground
import grails.test.mixin.domain.DomainClassUnitTestMixin
import grails.test.mixin.support.GrailsUnitTestMixin
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.codehaus.groovy.grails.commons.GrailsDomainClass
import org.junit.Before
class EnhanceDomainTestMixin {
boolean enhancerMethodCalled = false;
GrailsApplication application
MetaMethod mockDomainMethod
//replace the mockDomain Method from DomainClassUnitTestMixin with this closure
def enhancedMockDomain = { Class cl, List list ->
def enhanced =cl.metaClass.getMetaMethod("isEnhanced")
try {
//run the mockDomain method to have the mocked domain class registered in the grails application
mockDomainMethod.invoke(delegate, cl, list)
}
finally {
//enhance the grails domain with a new method
def domain = application.getDomainClass(cl.name) as GrailsDomainClass
domain.metaClass.isEnhanced = { return true; }
assert domain.newInstance().isEnhanced();
}
}
#Before void runDomainEnhancer() {
enhancerMethodCalled = true;
//GrailsUnitTestMixin.initGrailsApplication() should have already been called. (at least this was not an issue here)
application = GrailsUnitTestMixin.grailsApplication
//pick the mockDomain method
mockDomainMethod = DomainClassUnitTestMixin.metaClass.pickMethod("mockDomain", Class, List)
//if the picked mockDomain has never been enhanced, wrap it.
if(mockDomainMethod != enhancedMockDomain) {
DomainClassUnitTestMixin.metaClass.mockDomain = enhancedMockDomain
}
}
}
3) Add this small utils class (in test/unit/playground)
package playground
class TestSetup {
static Data d1
static void setup() {
d1 = new Data()
assert d1.isEnhanced()
}
}
4) Add these tests into the unit test already created by grails DataTests
package playground
import grails.test.mixin.*
#TestFor(Data)
#TestMixin(EnhanceDomainTestMixin)
class DataTests {
void testIsEnhancedLocal() {
assert enhancerMethodCalled
Data d = new Data()
assert d.isEnhanced()
}
void testIsEnhancedLocalSecondTime() {
assert enhancerMethodCalled
Data d = new Data()
assert d.isEnhanced()
}
void testIsEnhancedGlobalFirstTime() {
assert enhancerMethodCalled
TestSetup.setup()
assert TestSetup.d1 != null
}
void testIsEnhancedGlobalSecondTime() {
assert enhancerMethodCalled
TestSetup.setup()
assert TestSetup.d1 != null
}
}
Now run this command:
grails test-app unit:
you should have something like this output:
| Completed 4 unit tests, 4 failed in 1651ms
| Tests FAILED - view reports in target\test-reports
Now run the this command again (sometime one more is needed):
grails test-app unit: playground.DataTests
testMixin> grails test-app unit: playground.DataTests
| Completed 4 unit tests, 0 failed in 1384ms
| Tests PASSED - view reports in target\test-reports
So does anyone has a clue of why the metaClass modification is not reliable while running unit tests ? And how to workaround this issue ?
I had to use grailsApplication config in my domain class method. I ran into the same problem. Try using Holders.config instead of grailsApplication.config. It worked for me.