开源软件名称(OpenSource Name):mockk/mockk开源软件地址(OpenSource Url):https://github.com/mockk/mockk开源编程语言(OpenSource Language):Kotlin 84.8%开源软件介绍(OpenSource Introduction):Kotlin Academy articlesCheck the series of articles "Mocking is not rocket science" at Kt. Academy describing MockK from the very basics of mocking up to description of all advanced features. Spring support
Quarkus support
Kotlin version supportFrom version 1.13.0 MockK supports Kotlin 1.4 and higher Known issues
Table of contents:
Examples, guides & articles
Japanese guides and articlesChinese guides and articles
InstallationAll you need to get started is just to add a dependency to Gradle/maven dependency
DSL examplesSimplest example. By default mocks are strict, so you need to provide some behaviour. val car = mockk<Car>()
every { car.drive(Direction.NORTH) } returns Outcome.OK
car.drive(Direction.NORTH) // returns OK
verify { car.drive(Direction.NORTH) }
confirmVerified(car) AnnotationsYou can use annotations to simplify the creation of mock objects: class TrafficSystem {
lateinit var car1: Car
lateinit var car2: Car
lateinit var car3: Car
}
class CarTest {
@MockK
lateinit var car1: Car
@RelaxedMockK
lateinit var car2: Car
@MockK(relaxUnitFun = true)
lateinit var car3: Car
@SpyK
var car4 = Car()
@InjectMockKs
var trafficSystem = TrafficSystem()
@Before
fun setUp() = MockKAnnotations.init(this, relaxUnitFun = true) // turn relaxUnitFun on for all mocks
@Test
fun calculateAddsValues1() {
// ... use car1, car2, car3 and car4
}
} Injection first tries to match properties by name, then by class or superclass.
Check the Properties are injected even if
JUnit4JUnit 4 exposes a rule-based API to allow for some automation following the test lifecycle. MockK includes a rule which uses this to set up and tear down your mocks without needing to manually call class CarTest {
@get:Rule
val mockkRule = MockKRule(this)
@MockK
lateinit var car1: Car
@RelaxedMockK
lateinit var car2: Car
@Test
fun something() {
every { car1.drive() } just runs
every { car2.changeGear(any()) } returns true
// etc
}
} JUnit5In JUnit5 you can use @ExtendWith(MockKExtension::class)
class CarTest {
@MockK
lateinit var car1: Car
@RelaxedMockK
lateinit var car2: Car
@MockK(relaxUnitFun = true)
lateinit var car3: Car
@SpyK
var car4 = Car()
@Test
fun calculateAddsValues1() {
// ... use car1, car2, car3 and car4
}
} Additionally, it adds the possibility to use @Test
fun calculateAddsValues1(@MockK car1: Car, @RelaxedMockK car2: Car) {
// ... use car1 and car2
} Finally, this extension will call Automatic verification confirmationYou can make sure that all stubbed methods are actually verified by also annotating your test class with This will internally call Please note that this behavior may not work as expected when running tests in your IDE, as it is Gradle who takes care of handling the exception being thrown when these Automatic unnecessary stubbing checkYou can make sure that all stubbed methods are useful - used at least once - by also annotating your test class with This will internally call SpySpies allow you to mix mocks and real objects. val car = spyk(Car()) // or spyk<Car>() to call the default constructor
car.drive(Direction.NORTH) // returns whatever the real function of Car returns
verify { car.drive(Direction.NORTH) }
confirmVerified(car) Note: the spy object is a copy of the passed object. Relaxed mockA val car = mockk<Car>(relaxed = true)
car.drive(Direction.NORTH) // returns null
verify { car.drive(Direction.NORTH) }
confirmVerified(car) Note: relaxed mocking is working badly with generic return types. A class cast exception is usually thrown in this case. Opt for stubbing manually in the case of a generic return type. Workaround: val func = mockk<() -> Car>(relaxed = true) // in this case invoke function has generic return type
// this line is workaround, without it the relaxed mock would throw a class cast exception on the next line
every { func() } returns Car() // or you can return mockk() for example
func() Partial mockingSometimes, you need to stub some functions, but still call the real method on others, or on specific arguments.
This is possible by passing class Adder {
fun addOne(num: Int) = num + 1
}
val adder = mockk<Adder>()
every { adder.addOne(any()) } returns -1
every { adder.addOne(3) } answers { callOriginal() }
assertEquals(-1, adder.addOne(2))
assertEquals(4, adder.addOne(3)) // original function is called Mock relaxed for functions returning UnitIf you want Function: mockk<ClassBeingMocked>(relaxUnitFun = true) Annotation: @MockK(relaxUnitFun = true)
lateinit var mock1: ClassBeingMocked
init {
MockKAnnotations.init(this)
} MockKAnnotations.init: @MockK
lateinit var mock2: ClassBeingMocked
init {
MockKAnnotations.init(this, relaxUnitFun = true)
} Object mocksObjects can be turned into mocks in the following way: object ObjBeingMocked {
fun add(a: Int, b: Int) = a + b
}
mockkObject(ObjBeingMocked) // applies mocking to an Object
assertEquals(3, ObjBeingMocked.add(1, 2))
every { ObjBeingMocked.add(1, 2) } returns 55
assertEquals(55, ObjBeingMocked.add(1, 2)) To revert back, use @Before
fun beforeTests() {
mockkObject(ObjBeingMocked)
every { MockObj.add(1,2) } returns 55
}
@Test
fun willUseMockBehaviour() {
assertEquals(55, ObjBeingMocked.add(1,2))
}
@After
fun afterTests() {
unmockkAll()
// or unmockkObject(ObjBeingMocked)
} Despite the Kotlin language restrictions, you can create new instances of objects if required by testing logic: val newObjectMock = mockk<MockObj>() Class mockSometimes you need a mock of an arbitrary class. Use val car = mockkClass(Car::class)
every { car.drive(Direction.NORTH) } returns Outcome.OK
car.drive(Direction.NORTH) // returns OK
verify { car.drive(Direction.NORTH) } Enumeration mocksEnums can be mocked using enum class Enumeration(val goodInt: Int) {
CONSTANT(35),
OTHER_CONSTANT(45);
}
mockkObject(Enumeration.CONSTANT)
every { Enumeration.CONSTANT.goodInt } returns 42
assertEquals(42, Enumeration.CONSTANT.goodInt) Constructor mocksSometimes, especially in code you don't own, you need to mock newly created objects. For this purpose, the following constructs are provided: class MockCls {
fun add(a: Int, b: Int) = a + b
}
mockkConstructor(MockCls::class)
every { anyConstructed<MockCls>().add(1, 2) } returns 4
assertEquals(4, MockCls().add(1, 2)) // note new object is created
verify { anyConstructed<MockCls>().add(1, 2) } The basic idea is that just after the constructor of the mocked class is executed (any of them), objects become a In case a class has more than one constructor, each can be mocked separately: class MockCls(private val a: Int = 0) {
constructor(x: String) : this(x.toInt())
fun add(b: Int) = a + b
}
mockkConstructor(MockCls::class)
every { constructedWith<MockCls>().add(1) } returns 2
every {
constructedWith<MockCls>(OfTypeMatcher<String>(String::class)).add(2) // Mocks the constructor which takes a String
} returns 3
every {
constructedWith<MockCls>(EqMatcher(4)).add(any()) // Mocks the constructor which takes an Int
} returns 4
assertEquals(2, MockCls().add(1))
assertEquals(3, MockCls("2").add(2))
assertEquals(4, MockCls(4).add(7))
verify {
constructedWith<MockCls>().add(1)
constructedWith<MockCls>("2").add(2)
constructedWith<MockCls>(EqMatcher(4)).add(7)
} Note that in this case, a Partial argument matchingYou can mix both regular arguments and matchers: val car = mockk<Car>()
every {
car.recordTelemetry(
speed = more(50),
direction = Direction.NORTH, // here eq() is used
lat = any(),
long = any()
)
} returns Outcome.RECORDED
car.recordTelemetry(60, Direction.NORTH, 51.1377382, 17.0257142)
verify { car.recordTelemetry(60, Direction.NORTH, 51.1377382, 17.0257142) }
confirmVerified(car) Chained callsYou can stub chains of calls: val car = mockk<Car>()
every { car.door(DoorType.FRONT_LEFT).windowState() } returns WindowState.UP
car.door(DoorType.FRONT_LEFT) // returns chained mock for Door
car.door(DoorType.FRONT_LEFT).windowState() // returns WindowState.UP
verify { car.door(DoorType.FRONT_LEFT).windowState() }
confirmVerified(car) Note: if the function's return type is generic then the information about the actual type is gone. every { obj.op2(1, 2).hint(Int::class).op1(3, 4) } returns 5 Hierarchical mockingFrom version 1.9.1 mocks may be chained into hierarchies: interface AddressBook {
val contacts: List<Contact>
}
interface Contact {
val name: String
val telephone: String
val address: Address
}
interface Address {
val city: String
val zip: String
}
val addressBook = mockk<AddressBook> {
every { contacts } returns listOf(
mockk {
every { name } returns "John"
every { telephone } returns "123-456-789"
every { address.city } returns "New-York"
every { address.zip } returns "123-45"
},
mockk {
every { name } returns "Alex"
every { telephone } returns "789-456-123"
every { address } returns mockk {
every { city } returns "Wroclaw"
every { zip } returns "543-21"
}
}
)
} |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论