Support for Spring Boot integration tests written in Kotlin using MockK instead of Mockito.
Spring provides @MockitoBean and @MockitoSpyBean annotations for integration tests, which create mock/spy beans using Mockito.
This project provides equivalent annotations MockkBean and MockkSpyBean to do the exact same thing with MockK.
All the Mockito-specific classes of Spring, including the automated tests, have been cloned, translated to Kotlin, and adapted to MockK.
This library thus provides the same functionality as the standard Mockito-based Spring mock beans.
For example:
@ExtendWith(SpringExtension::class)
@WebMvcTest
class GreetingControllerTest {
@MockkBean
private lateinit var greetingService: GreetingService
@Autowired
private lateinit var controller: GreetingController
@Test
fun `should greet by delegating to the greeting service`() {
every { greetingService.greet("John") } returns "Hi John"
assertThat(controller.greet("John")).isEqualTo("Hi John")
verify { greetingService.greet("John") }
}
}Add this to your dependencies:
testImplementation("com.ninja-squad:springmockk:5.0.1")Add this to your dependencies:
<dependency>
<groupId>com.ninja-squad</groupId>
<artifactId>springmockk</artifactId>
<version>5.0.1</version>
<scope>test</scope>
</dependency>- the MockK defaults are used, which means that mocks created by the annotations are strict (i.e. not relaxed) by default. But you can configure MockK to use different defaults globally, or you can use
@MockkBean(relaxed = true)or@MockkBean(relaxUnitFun = true). - the created mocks can't be serializable as they can be with Mockito (AFAIK, MockK doesn't support that feature)
- When spying a bean that is then wrapped in a Spring AOP proxy (for example when using the
@Cacheableannotation), you must stub and verify using the ultimate target of the bean, rather than the bean itself. UseAopTestUtils.getUltimateTargetObject()to get the ultimate target.
Qualifier annotations are looked up on fields, and not on properties.
This doesn't matter much until you use a custom qualifier annotation.
In that case, make sure that it targets fields and not properties, or use @field:YourQualifier to apply it on your beans.
In some situations, the beans that need to be spied are JDK proxies. In recent versions of Java (Java 16+ AFAIK),
MockK can't spy JDK proxies unless you pass the argument --add-opens java.base/java.lang.reflect=ALL-UNNAMED
to the JVM running the tests.
Not doing that and trying to spy on a JDK proxy will lead to an error such as
java.lang.IllegalAccessException: class io.mockk.impl.InternalPlatform cannot access a member of class java.lang.reflect.Proxy (in module java.base) with modifiers "protected"
To pass that option to the test JVM with Gradle, configure the test task with
tasks.test {
// ...
jvmArgs(
"--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED"
)
}For Maven users:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
</argLine>
</configuration>
</plugin>- this is not an official Spring project, so it might not work out of the box for newest versions if backwards incompatible changes are introduced in Spring.
SpringMockK originally cloned the Mockito annotations provided by Spring Boot (MockBean and SpyBean).
Spring Boot now doesn't provide these annotations anymore. Similar annotations are now provided
by the Spring Framework itself.
Since version 5.x of SpringMockK, it clones the Mockito annotations from Spring Framework.
- Version 5.x of SpringMockK: compatible with Spring Framework 7, Java 17+
- Version 4.x of SpringMockK: compatible with Spring Boot 3.x, Java 17+
- Version 3.x of SpringMockK: compatible with Spring Boot 2.4.x, 2.5.x and 2.6.x, Java 8+
- Version 2.x of SpringMockK: compatible with Spring Boot 2.2.x and 2.3.x, Java 8+
- Version 1.x of SpringMockK: compatible with Spring Boot 2.1.x, Java 8+
Most of the changes have been done to align SpringMockK with the native Mockito support in Spring framework.
- Replace
@SpykBeanby@MockkSpyBean: the annotation has been renamed to be consistent with the naming used by Spring Framework for@MockitoBeanand@MockitoSpyBean @MockkBeanand@MockkSpyBeanare now written in Kotlin, and are repeatable using Kotlin's native mechanism. If you were using@MockkBeansand@SpykBeansexplicitly, don't do it anymore and repeat the@MockkBeanand@MockkSpyBeanannotations.- The
classesproperty has been renamed totypes. it was previously an alias forclasses. So if you had@MockkBean(classes = [SomeService::class]), it should be rewrteen as@MockkBean(types = [SomeService::class]) - The
valueproperty is now an alias forname. it was previously an alias forclasses. So if you had@MockkBean([SomeService::class]), it should be rewrteen as@MockkBean(types = [SomeService::class]) - The extension property
com.ninjasquad.springmockk.MockkFunctionsKt.isMockhas been renamed toisMockOrSpy.
./gradlew build