How to create parameterized hooks in junit 5? - kotlin

I am writing a test in using junit 5 where I want to run a same test on different sets of data. To achieve that, I am using parameterization. Now a situation came in where I want to run something before each set. I want to implement that in #BeforeEach hook and after some research, I found that ParameterResolver implementation can be one of the solution for that. But with that, test is getting issues to get parameter in #BeforeEach hook.
Here I am adding my code. These are kotlin classes. One test class StringDataClientTest which extends StringDataTest in which I am adding test parameter and #beforeEach hook. Adding #ExtendWith with this class which is extending StringParameterResolver class.
StringDataClientTest:
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class StringDataClientTest : StringDataTest() {
#DisplayName("First Client Test")
#ParameterizedTest
#MethodSource("getString")
fun firstClientTest(data: String) {
System.out.println("First client test for: $data")
}
}
StringDataTest:
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.params.provider.MethodSource
#ExtendWith(StringParameterResolver::class)
open class StringDataTest {
companion object {
#JvmStatic
fun getString(): List<String> {
return listOf(
"Parameter 1",
"Parameter 2",
"Parameter 3",
"Parameter 4"
)
}
}
#BeforeEach
#MethodSource("getString")
fun beforeEveryTest(value: String) {
System.out.println(" --------- Running before hook for: $value")
}
}
StringParameterResolver:
import org.junit.jupiter.api.extension.ExtensionContext
import org.junit.jupiter.api.extension.ParameterContext
import org.junit.jupiter.api.extension.ParameterResolver
class StringParameterResolver : ParameterResolver {
override fun supportsParameter(parameterContext: ParameterContext?, extensionContext: ExtensionContext?): Boolean {
return parameterContext!!.parameter.type === StringDataTest::class.java
}
override fun resolveParameter(parameterContext: ParameterContext?, extensionContext: ExtensionContext?): Any {
return StringDataTest()
}
}
And I am getting this exception in console output:
org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [java.lang.String arg0] in method [public final void com.thescore.test.cases.team.testCode.StringDataTest.beforeEveryTest(java.lang.String)].
Expecting:
Running before hook for: Parameter 1
First client test for: Parameter 1
Running before hook for: Parameter 2
First client test for: Parameter 2
Running before hook for: Parameter 3
First client test for: Parameter 3
Running before hook for: Parameter 4
First client test for: Parameter 4
Haven't done parameterized hooks before. Please help with this. Thanks!

Related

How can I run Nested Test in kotest?

I'm trying to migrate test framework from JUnit 5 to kotest.
I selected Annotation Spec at first (to check compatibility), but Nested Annotation doesn't work as expected
Sample code is below
class Outside : AnnotationSpec() {
#Test
fun runOutside() { }
#Nested
inner class Inside() {
#Test
fun runInside() { }
}
}
runOutside runs well, but runInside doesn't. The error message is java.lang.IllegalArgumentException: object is not an instance of declaring class
What's wrong ? is this a bug ?

How to apply a Rule to all test cases in a AndroidJUnitRunner?

I'm trying to apply a TestWatcher as a rule across all my test cases run by a particular runner.
MetadataCollector:
class MetadataCollector : TestWatcher() { ... }
TestRunner:
class TestRunner : AndroidJUnitRunner() {
override fun onCreate(arguments: Bundle?) {
super.onCreate(arguments)
}
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
return super.newApplication(cl, TestApplication::class.java.name, context)
}
}
All of my test classes currently require MetadataCollector() to be initialized as a Rule:
Test Class:
#JvmField #Rule val collector = MetadataCollector()
Is there a way I can create an instance of this rule automatically to each test case from the runner? Ideally, this is to avoid duplicating this #Rule creation in every single Test Class.
I am unfortunately stuck with JUnit 4 at the moment. :(
There is a better way to do this, by injecting an instance of RunListener into your test runner. In your gradle config, you need to:
defaultConfig.testInstrumentationRunner = "com.mypackage.MyTestRunnerClassName"
defaultConfig.testInstrumentationRunnerArgument("listener", "com.mypackage.MyRunListenerClassName")
And in code, create a RunListenerClassName to implement the corresponding hooks
class MyRunListener : RunListener() {
// impl
}

How to test add method in Kotlin object Singleton class

I'm trying to do some testing in my object kotlin class, but I can getting an error in my thenReturn method when I try to pass the object. I get a Require: Unit! Found checkout. Someone can point me how is possible to test it??
If I remove thenReturn method I get this error:
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, which is not supported
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
I refer this link but I can't get it.
object CheckoutRepository: CheckoutContract.Model {
var checkout: MutableList<Checkout> = mutableListOf<Checkout>()
override fun addProductToShoppingCart(checkoutProduct: Checkout){
checkout.add(checkoutProduct)
}
override fun getProductsInShoppinCart() : List<Checkout>?{
return checkout
}
override fun cleanCheckout(){
checkout.clear()
}
}
#Test
fun test_with_mock() {
val mock = mock<CheckoutContract.Model>()
var checkout = Checkout("VOUCHER", "voucher", 35.0, 5)
mock.addProductToShoppingCart(checkout)
val answer = mock.getProductsInShoppinCart()
`when`(mock.addProductToShoppingCart(checkout)).thenReturn(checkout)
assertNotNull(checkout)
assertEquals(checkout, answer)
}
In your function addProductToShoppingCart don't have any return type.
if you want check Checkout class make the change in the functions then test case will pass
override fun addProductToShoppingCart(checkoutProduct: Checkout):Checkout{
checkout.add(checkoutProduct)
return checkout
}

unit testing extension function and mocking the other methods of the class

I am writing an extension function adding some retry capabilities to AmazonKinesis.putRecords. In my extension method i do some logic and some calls to the original putRecords method:
fun AmazonKinesis.putRecordsWithRetry(records: List<PutRecordsRequestEntry>, streamName: String) {
//...
val putRecordResult = this.putRecords(PutRecordsRequest().withStreamName(streamName).withRecords(records))
//...
}
From a unit test point of view I am finding it hard to see how I should mock the call to this.putRecords
I am using com.nhaarman.mockitokotlin2.*
val successfulRequest = PutRecordsResultEntry().withErrorCode(null);
class KinesisExtensionTest : StringSpec({
val testRecords = ArrayList<PutRecordsRequestEntry>()
testRecords.add(PutRecordsRequestEntry().withPartitionKey("iAmABunny").withData(ByteBuffer.wrap("aaa".toByteArray()))
)
val kinesis = mock<AmazonKinesis>{
on { putRecordsWithRetry(testRecords, "/dev/null") }.thenCallRealMethod()
on { putRecords(any()) }.thenReturn(PutRecordsResult().withRecords(listOf(successfulRequest, successfulRequest)))
}
"can write a record" {
kinesis.putRecordsWithRetry(testRecords, "/dev/null")
verify(kinesis).putRecord(any())
}
})
The putRecordResult is always null
The extension function AmazonKinesis.putRecordsWithRetry will be compiled to a static function under the hood, and Mockito doesn't support static method mocking yet.
Therefore Mockito may not know the stubbing information at the verification step and thus the default null value is produced.

How to correct "verify should appear after all code under test has been exercised" when verify is last?

I get the error "verify should appear after all code under test has been exercised" with the following:
class CowTest extends MockFactory {
Cow.init(testCowProcesses)
#Test
def noProcessesTest: Unit = {
val cow: Cow = Cow(testCowProcesses)
cow.simulateOneDay(0 nanoseconds)
}
#Test
def processSimulationTest: Unit = {
val NUMBER_OF_TRIES: Int = 10
val cow: Cow = Cow(testCowProcesses)
for (ii <- 0 until NUMBER_OF_TRIES) {
cow.simulateOneDay(0 nanoseconds)
}
(cow.metabolicProcess.simulateOneDay _).verify(0 nanoseconds).repeated(NUMBER_OF_TRIES)
}
}
testCowProcesses is defined in another file, like this (abbreviated):
object CowTesters extends MockFactory {
val metProc = stub[MetabolicProcess]
(metProc.replicate _).when().returns(metProc)
val testCowProcesses = CowProcesses(metProc)
}
I don't quite understand the error message. If I comment out the verify line, the test runs. Alternatively, if I comment out the first test, the second test can run. There are no other tests in the test class. This seems to indicate that the stub objects cannot be reused, as they were in mockito (I'm adapting code from mockito).
Is the best solution to reinstantiate the mock objects, perhaps by converting CowTesters into a class?
Edit:
I confirmed the above suggestion works (not sure if it is the best), but in the mean time I did something a bit more convoluted to get me through compiles:
//TODO: once all tests are converted to ScalaMock,
//TODO: just make this a class with a companion object
trait CowTesters extends MockFactory {
val metProc = stub[MetabolicProcess]
(metProc.replicate _).when().returns(metProc)
val testCowProcesses = CowProcesses(metProc)
}
object CowTesters extends CowTesters {
def apply(): CowTesters = new CowTesters {}
}
From your code above, it seems you are either trying to use JUnit or TestNG. ScalaMock doesn't support either of those frameworks directly, which is why you are struggling with the verification of mocks.
You need to implement your tests using either ScalaTest, or Specs2. See http://scalamock.org/user-guide/integration/
The conversion from JUnit to ScalaTest should be pretty straightforward if you switch to e.g. a FunSuite: http://www.scalatest.org/user_guide/selecting_a_style