I am trying to combine the concept of a Parameterized runner from JUnit4 with the JUnit5 Parameterized Test. Essentially I want to test two separate functions on the same set of data.
I know I could just add the function as another argument to the parameterized test itself but I am trying to make changing or adding new functions to test easy.
Would I be able to leverage nested test classes to achieve this? I am not sure the best way to approach.
#RunWith(Parameterized::class)
class RomanNumeralTest(val func: (Int) -> String) {
#ParameterizedTest(name = "{index} - Expect [{0}] should return [{1}]")
#MethodSource("testData")
fun `Test roman numeral from integer values`(num: Int, expected: String) =
assertEquals(expected, func(num))
companion object {
#JvmStatic
#Parameterized.Parameters
fun data(): Collection<Array<(Int) -> String>> {
return listOf(
arrayOf({num -> roman(num)}),
arrayOf({num -> num.toRomanNumeral()})
)
}
#JvmStatic
private fun testData() = sequenceOf(
arrayOf(1, "I"),
arrayOf(2, "II"),
arrayOf(3, "III"),
arrayOf(4, "IV"),
arrayOf(5, "V")
).asStream()
}
}
I tried the same but in the end I came to the conclusion that: you can't.
There is no way you can use the JUnit 4 Parameterized test runner using the annotations which came with JUnit 5.
You need to move to JUnit 5 to use the latest features like TestFactory or all the powerful annotations of parameterized tests described here
Related
Suppose I have an instance of the following class:
data class User(val name: String, val startedOn: LocalDate, val score: BigDecimal)
Its toString() method is provided automatically and it outputs this:
val user = User("Mike", LocalDate.of(2021, 1, 2), BigDecimal.TEN)
println(user)
User(name=Mike, startedOn=2021-01-02, score=10)
Is there another standard function to provide a compilable String for an instance of any data class:
User("Mike", LocalDate.of(2021, 1, 2), BigDecimal("10"))
Of course, I can write something myself, using reflection, but I don't want to reinvent the wheel. This will allow me to write unit tests faster.
Edit: I'm trying to quickly replace real API calls with mocks. So Id like to add something my code calling the API:
val request = getRequest(...)
println(toCompilableString(request))
val response = myApi.call(request)
println(toCompilableString(response))
and use that output in my tests like this:
val request = <output from the first println>
val response = <output from the second println>
every { myApi.call(request) } returns response
TIA!
There is no built-in way to my knowledge to do this, but there are libraries that can help, such as Kotlin Poet. However, you won't get the automatic behaviour you're looking for.
If you really want to do this with pure Kotlin, the only "built-in" way I can think of right now is to override toString(). You don't have to use reflection, but you'll have to construct the String you want by hand. For instance, something like:
data class User(val name: String, val startedOn: LocalDate, val score: BigDecimal) {
override fun toString(): String {
return """User("$name", ${startedOn.toCompilableString()}, BigDecimal("$score"))"""
}
private fun LocalDate.toCompilableString() =
"LocalDate.of($dayOfMonth, $monthValue, $year)"
}
Let say I have this class A:
class A(a: Int, b: String, c: Long, d: Double, ...)
Now if I want to test a method that returns a list of objects A:
fun test_getListOfObjectsA(){
val expected = listOf(A(), A(), A())
whenever(someClass.getListOfObjectsA()).thenReturn(listOf(A(), A(), A()))
val actual = someClass.getListOfObjectsA()
assertEquals(expected, actual)
}
Here is the problem, this code won't compile because each instance of A() requires a list of argument to be passed in. How can I create a List of A without having to pass all the constructor's arguments every time?
To mock the class A, you can use mock<A>().
As said in comments, your test isn't testing getListOfObjectsA(), it's testing Mockito's mocking behavior.
By the way, the following test passes:
fun test_getListOfObjectsA(){
val expected = listOf(mock<A>(), mock<A>(), mock<A>())
whenever(someClass.getListOfObjectsA()).thenReturn(expected)
val actual = someClass.getListOfObjectsA()
assertEquals(expected, actual)
}
Important note
Remember that, since your class is final, to successfully mock it, you have 3 choices:
make it open
use the dependency mockito-inline to allow the mocking on final classes
use the Kotlin all-open compiler plugin to make the class and its fields open in tests (https://kotlinlang.org/docs/reference/compiler-plugins.html)
JUnit 5 has a neat extensions functionality which is not compatible with kotlintest even if it runs on JUnit framework. While the simple use cases in which we just need to log something can be handled by the TestListener, we cannot handle more advanced cases. In particular, how to interact with the extension? Ideally, I would like to get a hold of the extension so I could query it.
In JUnit5 it would be (one of the options anyway)
#ExtendWith(MyExtension.class)
class Something() {
#MyAnnotation
MyType myType;
#Test
void doSomething() {
myType.doSomething();
}
}
In JUnit4 it would be even simpler
#Rule
MyRule myRule;
#Test
void fun() {
myRule.something();
}
Of course, there is a SpringExtension but it does the reflective instantiation of the class.
Is there any way to do it easier?
You can keep a reference to an extension/listener before passing it to the overriden function.
For example:
val myListener = MyKotlinTestListener()
val myOtherListener = MyOtherKotlinTestListener()
override fun listeners() = listOf(myListener, myOtherListener)
This way you can do what you want in your tests with that reference available
test("MyTest") {
myListener.executeX()
}
The same goes if you're using an extension of any sort.
They'll still be executed as part of KotlinTest's lifecycle!
I'd want to use doReturn(sth).when(underTest).someFunc() instead of when(underTest.someFunc()).thenReturn(sth).
(don't want to actually execute anything inside someFunc() - https://stackoverflow.com/a/29394497/541624)
In Java, I could do underTest = Mockito.spy(new SomeClass(someParam));
I'm getting:
Mockito cannot mock/spy because :
- final class
The reason you cannot mock/spy Kotlin classes is they are final (by default). So Mockito cannot mock such classes unless you put the keyword open.
The Mockito version 2 introduced a feature which allows you to mock/spy final classes.
How to do it?
Add mockito-inline dependency with other mockito v2 dependencies. For ex: testImplementation 'org.mockito:mockito-inline:2.8.9'
Then use mockito methods as normal.
Here's a dummy test which demonstrates how to mock a method and do nothing.
class Foo {
var xval = 0
fun foo(x: Int, y: Int): Int = x / y
fun bar(x: Int) {
xval = x
}
}
class FooTest {
#Test
fun fooTest() {
val foo = Mockito.mock(Foo::class.java)
Mockito.doAnswer(Answers.RETURNS_DEFAULTS).`when`(foo).foo(10, 2)
assertEquals(0, foo.foo(10, 2))
Mockito.doNothing().`when`(foo).bar(100)
assertEquals(0, foo.xval)
}
}
As you can see, you could return defaults for methods which return something or do nothing for void methods.
Otherwise, you could use mockk https://mockk.io/ library which doesn't have this issue.
Having said all the above, I suggest that think if you could use an interface/abstract class rather than a concrete class here. That’s the best way to abstract away your dependency using mocking.
I can't seem to mock private functions in android tests. I'm also using the all-open plugin for pre-P testing. On non-android tests it runs with no problems. I figured it should work on android too, because it's marked on MockK-android. Is this not implemented or am I missing something obvious?
androidTestImplementation "io.mockk:mockk-android:1.8.7"
#OpenForTesting
class A {
fun publicFun() = privateFun()
private fun privateFun() {}
protected fun protectedFun() {}
}
#Test
fun privateFunctionMock() {
val spy = spyk<A>()
val mock = mockk<A>()
val a = A()
val functions = a::class.functions // size -> 6
val spyFunctions = spy::class.functions // size -> 5
val mockFunctions = mock::class.functions // size -> 5
every { spy["privateFun"]() } returns Unit
a.publicFun()
}
Fails with Exception, because the private function is missing.
io.mockk.MockKException: can't find function privateFun() for dynamic call
Subclassing is employed to create mocks and spies for pre-P android instrumented tests. That means basically private methods are skipped because it is not possible to inherit them. That way counters are not counting private methods.
InternalPlatformDsl.dynamicSet(autoBannerViewPagerMock, "mBannerList", list)
every { autoBannerViewPagerMock.invoke("loadCoverImage") withArguments listOf(any<Int>(), any<Int>(), any<ImageView>(), any<stMetaBanner>()) } returns Unit