forked from mockk/mockk
/
MockKExtension.kt
executable file
·147 lines (123 loc) · 5.17 KB
/
MockKExtension.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package io.mockk.junit5
import io.mockk.*
import io.mockk.impl.annotations.AdditionalInterface
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.RelaxedMockK
import io.mockk.impl.annotations.SpyK
import org.junit.jupiter.api.extension.*
import java.lang.annotation.Inherited
import java.lang.reflect.AnnotatedElement
import java.lang.reflect.Parameter
import java.util.Optional
/**
* JUnit5 extension.
*
* Parameters can use [MockK] and [RelaxedMockK].
* Class properties can use [MockK], [RelaxedMockK] and [SpyK]
* [unmockkAll] will be called after test class execution (*)
*
* Usage: declare @ExtendWith(MockKExtension.class) on a test class
*
* Alternatively –Djunit.extensions.autodetection.enabled=true may be placed on a command line.
*
* (*) [unmockkAll] default behavior can be disabled by adding [KeepMocks] to your test class or method or
* –Dmockk.junit.extension.keepmocks=true on a command line
*/
class MockKExtension : TestInstancePostProcessor, ParameterResolver, AfterAllCallback {
override fun supportsParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Boolean {
return getMockKAnnotation(parameterContext) != null
}
override fun resolveParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Any? {
val parameter = parameterContext.parameter
val type = parameter.type.kotlin
val annotation = getMockKAnnotation(parameterContext) ?: return null
val name = getMockName(parameterContext.parameter, annotation)
val isRelaxed = when (annotation) {
is RelaxedMockK -> true
is MockK -> annotation.relaxed
else -> false
}
val isRelaxedUnitFun = when (annotation) {
is MockK -> annotation.relaxUnitFun
else -> false
}
return mockkClass(
type,
name,
isRelaxed,
*moreInterfaces(parameterContext),
relaxUnitFun = isRelaxedUnitFun
)
}
private fun getMockKAnnotation(parameter: ParameterContext): Any? {
return sequenceOf(MockK::class, RelaxedMockK::class)
.map { parameter.findAnnotation(it.java) }
.firstOrNull { it.isPresent }
?.get()
}
private fun getMockName(parameter: Parameter, annotation: Any): String? {
return when {
annotation is MockK -> annotation.name
annotation is RelaxedMockK -> annotation.name
parameter.isNamePresent -> parameter.name
else -> null
}
}
private fun moreInterfaces(parameter: ParameterContext) =
parameter.findAnnotation(AdditionalInterface::class.java)
.map { it.type }
.map { arrayOf(it) }
.orElseGet { emptyArray() }
override fun postProcessTestInstance(testInstance: Any, context: ExtensionContext) {
MockKAnnotations.init(testInstance)
}
override fun afterAll(context: ExtensionContext) {
if (!context.keepMocks) {
unmockkAll()
}
if (context.confirmVerification) {
confirmVerified()
}
if (context.checkUnnecessaryStub) {
checkUnnecessaryStub()
}
}
private val ExtensionContext.keepMocks: Boolean
get() = testClass.keepMocks || testMethod.keepMocks ||
getConfigurationParameter(KEEP_MOCKS_PROPERTY).map { it.toBoolean() }.orElse(false)
private val Optional<out AnnotatedElement>.keepMocks
get() = map { it.getAnnotation(KeepMocks::class.java) != null }
.orElse(false)
private val ExtensionContext.confirmVerification: Boolean
get() = testClass.confirmVerification ||
getConfigurationParameter(CONFIRM_VERIFICATION_PROPERTY).map { it.toBoolean() }.orElse(false)
private val Optional<out AnnotatedElement>.confirmVerification
get() = map { it.getAnnotation(ConfirmVerification::class.java) != null}
.orElse(false)
private val ExtensionContext.checkUnnecessaryStub: Boolean
get() = testClass.checkUnnecessaryStub ||
getConfigurationParameter(CHECK_UNNECESSARY_STUB_PROPERTY).map { it.toBoolean() }.orElse(false)
private val Optional<out AnnotatedElement>.checkUnnecessaryStub
get() = map { it.getAnnotation(CheckUnnecessaryStub::class.java) != null}
.orElse(false)
/***
* Prevent calling [unmockkAll] after each test execution
*/
@Inherited
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class KeepMocks
@Inherited
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class ConfirmVerification
@Inherited
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class CheckUnnecessaryStub
companion object {
const val KEEP_MOCKS_PROPERTY = "mockk.junit.extension.keepmocks"
const val CONFIRM_VERIFICATION_PROPERTY = "mockk.junit.extension.confirmverification"
const val CHECK_UNNECESSARY_STUB_PROPERTY = "mockk.junit.extension.checkUnnecessaryStub"
}
}