Why does Mockito throw an "InvalidUseOfMatchersException" at for this code? - kotlin

I am writing a test for a Spring-Boot project written in Kotlin 1.5. This piece of code does fail at test runtime with an InvalidUseOfMatchersException and I struggle to figure out why:
#SpringBootTest
#AutoConfigureMockMvc
internal class ControllerTest {
#Autowired
lateinit var mockMvc: MockMvc
#MockBean
lateinit var mockedAuthFilter: AuthFilter
#BeforeEach
fun setup() {
assertNotNull(mockedAuthFilter)
`when`(
mockedAuthFilter.shouldProceed(
any(HttpServletRequest::class.java),
any(AuthConfig::class.java)
)
).thenReturn(true)
}
#Test
fun `This call should return without an error`() {
mockMvc.perform(get("/api/entities")).andDo(print()).andExpect(status().isOk)
}
}
All I can find in the web for this error is that you tried an argument matcher on a basic type - but that is not the case here. Have you faced this problem and how did you solve it?

From my understanding the failure comes from a masked nullpointer exception caused by the shouldProceed method itself which doesn't allow a call with nulled arguments, which kind of happens internally in Mockito during the setup of the mocked instance after any() returns.
The solution was to not use Mockito with Kotlin, but the MockK test utility instead.

Related

Mocking Gradle javaexec calls with mockK

Currently a beginner with mockK as it relates to developing Gradle plugin code in Kotlin. Suppose I have this class:
abstract class MyDomainObjectClass #Inject constructor(private val execOps: ExecOperations) {
abstract val mainClass: Property<String>
fun run() {
execOps.javaexec {
// ...
}
}
}
What I want to do is to construct a MyDomainObjectClass (normally constructed using something like ObjectFactory.newInstance()) in such a way that I can pass in a mocked ExecOperations, so that I can verify that the javaexec call is called exactly once (with the verification possibly involving mainClass, if I can find a way to involve it).
Is there a way I can satisfy all these requirements, or am I better off with a constructed mock of MyDomainObjectClass (stubbing in mainClass in the process)?

How to avoid NPEs from default parameters in mocked classes?

Here's a simplified version of what I want to test with Mockito:
class UnderTest {
fun doSomething() {
foo.doAnything()
}
}
class Foo {
fun doAnything(bar: Bar = Bar())
}
class TestUnderTest {
#Mock
var underTest: UnderTest
#Test
fun testDoSomething() {
underTest.doSomething() // Causes NPE
}
}
UnderTest is being tested. Its dependencies, like foo, are mocked. However, when my tests call UnderTest.doSomething(), it crashes. doSomething() calls Foo.doAnything(), letting it fill in the null parameter with the default - and the code that runs in that default parameter initialization is outside of the control of my test, as it's inside the static, synthetic method created for the byte code.
Is there a magical Mockito solution to get around this very situation? If so, I would love to hear it. Otherwise, I believe the options I have are:
To use PowerMock or Mockk to be able to mock things Mockito can't
To change Foo to have two doAnything() methods; one would have zero parameters, would call Bar() and pass it to the other.
To change Foo.doAnything() to accept a nullable parameter, then to have the body of the function call Bar() and use it.

Verifying method call with enum in Kotlin

I'm trying to verify that a method is called with a given argument. That argument is a non-nullable enum type. So I get the exception eq(SomeEnum.foo) must not be null. Here is a sample what I'm trying to do:
enum class SomeEnum {
foo, bar
}
open class MyClass {
fun doSomething() {
magic(SomeEnum.foo)
}
internal fun magic(whatever: SomeEnum) {}
}
#Test
fun mockitoBug() {
val sut = spy(MyClass())
sut.doSomething()
verify(sut).magic(eq(SomeEnum.foo))
}
Capturing does not work too. What can I do or is that really a bug as I assume?
Because Mockito was designed for Java, it doesn't play well with Kotlin's null checks. A good solution is to use the mockito-kotlin extensions library: https://github.com/mockito/mockito-kotlin
It includes Kotlin versions of the matchers that won't return null. Add a dependency on mockito-kotlin and just make sure to import the Kotlin versions instead of the Java ones.

How to create a TestContainers base test class in Kotlin with JUnit 5

I am trying to use Neo4j TestContainers with Kotlin, Spring Data Neo4j, Spring Boot and JUnit 5. I have a lot of tests that require to use the test container. Ideally, I would like to avoid copying the container definition and configuration in each test class.
Currently I have something like:
#Testcontainers
#DataNeo4jTest
#Import(Neo4jConfiguration::class, Neo4jTestConfiguration::class)
class ContainerTest(#Autowired private val repository: XYZRepository) {
companion object {
const val IMAGE_NAME = "neo4j"
const val TAG_NAME = "3.5.5"
#Container
#JvmStatic
val databaseServer: KtNeo4jContainer = KtNeo4jContainer("$IMAGE_NAME:$TAG_NAME")
.withoutAuthentication()
}
#TestConfiguration
internal class Config {
#Bean
fun configuration(): Configuration = Configuration.Builder()
.uri(databaseServer.getBoltUrl())
.build()
}
#Test
#DisplayName("Create xyz")
fun testCreateXYZ() {
// ...
}
}
class KtNeo4jContainer(val imageName: String) : Neo4jContainer<KtNeo4jContainer>(imageName)
How can I extract the databaseServer definition and the #TestConfiguration? I tried different ways of creating a base class and having the ContainerTest extend it, but it is not working. From what I understand, static attriubutes are not inherited in Kotlin.
Below my solution for sharing same container between tests.
#Testcontainers
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
abstract class IntegrationTest {
companion object {
#JvmStatic
private val mongoDBContainer = MongoDBContainer(DockerImageName.parse("mongo:4.0.10"))
.waitingFor(HostPortWaitStrategy())
#BeforeAll
#JvmStatic
fun beforeAll() {
mongoDBContainer.start()
}
#JvmStatic
#DynamicPropertySource
fun registerDynamicProperties(registry: DynamicPropertyRegistry) {
registry.add("spring.data.mongodb.host", mongoDBContainer::getHost)
registry.add("spring.data.mongodb.port", mongoDBContainer::getFirstMappedPort)
}
}
}
The key here is to not use #Container annotation as it will close just created container after your first test subclass executes all tests.
Method start() in beforeAll() initialize container only once (upon first subclass test execution), then does nothing while container is running.
By theory we shouldn't have to do this hack, based on:
https://www.testcontainers.org/test_framework_integration/junit_5/
...container that is static should not be closed until all of tests of all subclasses are finished, but it's not working that way and I don't know why. Would be nice to have some answer on that :).
I've had the same issue (making Spring Boot + Kotlin + Testcontainers work together) and after searching the web for (quite) a while I found this nice solution: https://github.com/larmic/testcontainers-junit5. You'll just have to adopt it to your database.
I faced very similar issue in Kotlin and spring boot 2.4.0.
The way you can reuse one testcontainer configuration can be achieved through initializers, e.g.:
https://dev.to/silaev/the-testcontainers-mongodb-module-and-spring-data-mongodb-in-action-53ng or https://nirajsonawane.github.io/2019/12/25/Testcontainers-With-Spring-Boot-For-Integration-Testing/ (java versions)
I wanted to use also new approach of having dynamicProperties and it worked out of a boxed in java. In Kotlin I made sth like this (I wasn't able to make #Testcontainer annotations working for some reason). It's not very elegant but pretty simple solution that worked for me:
MongoContainerConfig class:
import org.testcontainers.containers.MongoDBContainer
class MongoContainerConfig {
companion object {
#JvmStatic
val mongoDBContainer = MongoDBContainer("mongo:4.4.2")
}
init {
mongoDBContainer.start()
}
}
Test class:
#SpringBootTest(
classes = [MongoContainerConfig::class]
)
internal class SomeTest {
companion object {
#JvmStatic
#DynamicPropertySource
fun setProperties(registry: DynamicPropertyRegistry) {
registry.add("mongodb.uri") {
MongoContainerConfig.mongoDBContainer.replicaSetUrl
}
}
}
Disadvantage is this block with properties in every test class what suggests that maybe approach with initializers is desired here.

Mocking a private method in an Expectations block causes "IllegalStateException: Missing invocation to mocked type at this point"

I have a JUnit test that uses a JMockit Expectations block:
#Tested
private MyTestedClass myTestedClass;
#Injectable
private MyOtherClass myOtherClass;
#Test
public void publicBooleanMethodTest() {
new Expectations() {{
invoke(myOtherClass, "privateMethod");
result = true;
}};
myTestedClass.run(); // Calls myOtherClass.privateMethod();
}
However, this results in the following error:
java.lang.IllegalStateException: Missing invocation to mocked type at this point; please make sure such invocations appear only after the declaration of a suitable mock field or parameter
As you can see, myClass is mocked via #Injectable, so I'm not sure why the error is happening. I have also found that if I change the scope of privateMethod() to public, everything works.
What is going on and how can I fix this? It worked fine in JMockit 1.22, but now fails in JMockit 1.23 and greater.
JMockit 1.23 removed support for mocking private methods in Expectations blocks. From the release notes:
Version 1.23 (Apr 24, 2016):
Dropped support for the mocking of private methods/constructors when using the Expectations API, to prevent misuse. If still needed, they can be mocked or stubbed out with the application of a MockUp<T>.
Unfortunately, the erroneous error message is currently seen as "too costly too fix" by the development team. A better error message was added in JMockit 1.28 as a result of this discussion.
The error log says to use MockUp<T> as an alternative. In this case, the code would be as follows:
#Tested
private MyTestedClass myTestedClass;
#Injectable
private MyOtherClass myOtherClass;
#Test
public void publicBooleanMethodTest() {
// Partially mock myOtherClass
new MockUp<MyOtherClass>(myOtherClass) {
#Mock boolean privateBooleanMethod() {
return true;
}
};
myTestedClass.run(); // Calls myOtherClass.privateMethod();
}