Skip to content

Commit

Permalink
Kotlin 1.9.22 && moved documentation to kosi-libs.org
Browse files Browse the repository at this point in the history
  • Loading branch information
SalomonBrys committed Feb 1, 2024
1 parent 50e8df4 commit 71b7595
Show file tree
Hide file tree
Showing 14 changed files with 836 additions and 773 deletions.
777 changes: 7 additions & 770 deletions README.adoc

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ plugins {

allprojects {
group = "org.kodein.mock"
version = "1.16.0"
version = "1.17.0"
}
11 changes: 11 additions & 0 deletions doc/antora.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: mockmp
title: MocKMP
version: '1.17'
display_version: '1.17.0'
nav:
- modules/ROOT/nav.adoc
asciidoc:
attributes:
version: '1.17.0'
kotlin: '1.9.22'
jdk: '11'
2 changes: 2 additions & 0 deletions doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* xref:index.adoc[Introduction]
* xref:getting-started.adoc[Getting Started]
43 changes: 43 additions & 0 deletions doc/modules/ROOT/pages/getting-started.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
= Getting started

1. Apply the Gradle plugin and activate the helper dependency:
+
[source,kotlin,subs="verbatim,attributes"]
----
plugins {
kotlin("multiplatform")
id("org.kodein.mock.mockmp") version "{version}"
}
kotlin {
// Your Koltin/Multiplatform configuration
}

mockmp {
usesHelper = true
installWorkaround()
}
----

2. Create a test class that declares injected mocks and fakes:
+
[source,kotlin]
----
class MyTest : TestsWithMocks() {
override fun setUpMocks() = injectMocks(mocker) //<1>
@Mock lateinit var view: View
@Fake lateinit var model: Model
val controller by withMocks { Controller(view = view, firstModel = model) }
@Test fun controllerTest() {
every { view.render(isAny()) } returns true
controller.start()
verify { view.render(model) }
}
}
----
<1> This is mandatory and cannot be generated. You need to run the KSP generation at least once for your IDE to see the `injectMocks` generated function.
+
NOTE: Every property annotated by `@Mock`, annotated by `@Fake` or delegated to `withMocks` will be reset fresh between each test.
23 changes: 23 additions & 0 deletions doc/modules/ROOT/pages/index.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
= MocKMP

A Kotlin/Multiplatform Kotlin Symbol Processor that generates Mocks & Fakes at compile-time.

[IMPORTANT]
====
- Mocking only applies to *interfaces*
- Faking only applies to *concrete trees*
====

A Mock is an object implementing an interface whose behaviour is configurable at run-time, usually specifically for unit-tests. +
A mocks can be used to validate that a method or property was (or wasn't) accessed a certain number of times.

A fake is a concrete class that contains bogus data: all nullables are null, all strings are empty, all numbers are 0. +
A fake can be used when you need to instanciate a class to test an API but do not care about the data it contains.

[NOTE]
====
MocKMP uses https://github.com/google/ksp[KSP], which has limited support for Kotlin/Multiplatform.
In particular, https://github.com/google/ksp/issues/567[KSP does not support generating code for commonTest].
In order to work on Kotlin/Multiplatform projects, the MocKMP plugin uses a trick that consist of only generating code for the JVM, and then using the generated code for all targets.
====
5 changes: 5 additions & 0 deletions doc/modules/core/nav.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
* xref:setup.adoc[Setup]
* xref:mocking.adoc[]
* xref:facking.adoc[]
* xref:injection.adoc[]
* xref:helper.adoc[]
63 changes: 63 additions & 0 deletions doc/modules/core/pages/facking.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
= Faking concrete classes

CAUTION: Only *concrete trees* (concrete classes containing concrete classes) can be faked!

*Data classes* are ideal candidates for faking.


== Requesting generation

You can declare that a class needs a specific faked data by using the `@UsesFakes` annotation.

[source,kotlin]
----
@UsesFakes(User::class)
class MyTests
----

Once a type appears in `@UsesFakes`, the processor will generate a fake function for it.


== Instantiating

Once a class has been faked, you can get a new instance by calling its `fake*` corresponding function:

[source,kotlin]
----
@UsesFakes(User::class)
class MyTests {
val user = fakeUser()
}
----

Here are the rules the processor uses to generate fakes:

* Nullable values are always `null`.
* `Boolean` values are set to `false`.
* Numeric values are set to `0`.
* `String` values are set to empty `""`.
* Other non-nullable non-primitive values are faked.

[TIP]
====
By using a `data class`, you can easily tweak your fakes according to your needs:
[source,kotlin]
----
val user = fakeUser().copy(id = 42)
----
====


== Providing fake instances

Classes that do not have a public constructor cannot be automatically faked.
For these types, you need to provide your custom fake provider with `@FakeProvider`:

[source,kotlin]
----
@FakeProvider
fun provideFakeInstant() = Instant.fromEpochSeconds(0)
----

CAUTION: There can be only one provider per type, and it needs to be a top-level function.
76 changes: 76 additions & 0 deletions doc/modules/core/pages/helper.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
= The test class helper

MocKMP provides the `TestsWithMocks` helper class that your test classes can inherit from.
It provides the following benefits:

- Provides a `Mocker`.
- Resets the `Mocker` before each tests.
- Provides `withMocks` property delegates to initialize objects with mocks.
- Allows to call `every`, `everySuspending`, `verify`, and `verifyWithSuspend` without `mocker.`.
It does not come with the standard runtime (as it forces the dependency to JUnit on the JVM), so to use it you need to either:

* define `usesHelper = true` in the MocKMP Gradle plulgin configuration block,
* or add the `mockmp-test-helper` implementation dependency.
Here's a test class example with `TestsWithMocks`:

[source,kotlin]
----
@UsesFakes(User::class)
class MyTests : TestsWithMocks() { //<1>
override fun setUpMocks() = injectMocks(mocker) //<2>
@Mock lateinit var db: Database
@Mock lateinit var api: API
@Fake lateinit var user: User
val controller by withMocks { ControllerImpl(db, api) } //<3>
@Test fun controllerTest() {
every { view.render(isAny()) } returns true //<4>
controller.start()
verify { view.render(model) } //<4>
}
}
----
<1> The class inherits `TestsWithMocks`, which provides helpers.
<2> `setUpMocks` must be overriden, and can generally be just a delegation to the `injectMocks` generated function.
<3> Controller will be (re)created before each tests with the new mock dependencies.
<4> Note the absence of `mocker.` as you can use `every` and `verify` directly.

NOTE: Properties delegated to `withMocks` will be (re)initialized *before each tests*, after the mocks have been (re)injected.

[CAUTION]
====
Because of https://youtrack.jetbrains.com/issue/KT-54932[this issue], you cannot consider that the mocks have been initialized in yout `@BeforeTest` methods.
You can override `initMocksBeforeTest` if you need to initialize your mocks before each test:
[source,kotlin]
----
class MyTests : TestsWithMocks() {
override fun initMocksBeforeTest() {
// Access all injected values:
// mocks, fakes & withMocks properties
}
}
----
====

In case your test class already extends another class, you can use the `ITestsWithMocks` interface instead:

[source,kotlin]
----
@UsesFakes(User::class)
class MyTests : MySuperAbstractTests(), ITestsWithMocks { //<1>
override val mocksState = ITestsWithMocks.State() //<2>
override fun setUpMocks() = injectMocks(mocker)
// ...your tests...
}
----
<1> The class implements the `ITestsWithMocks` interface, which provides all helper methods.
<2> The class needs to provide an `ITestsWithMocks.State` (since the interface cannot provide one).
37 changes: 37 additions & 0 deletions doc/modules/core/pages/injection.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
= Injecting your test classes

Instead of manually creating your own mocks & fakes, it can be useful to inject them in your test class, especially if you have multiple tests using them.

[source,kotlin]
----
@UsesFakes(User::class)
class MyTests {
@Mock lateinit var db: Database
@Mock lateinit var api: API
@Fake lateinit var user: User
lateinit var controller: Controller
val mocker = Mocker()
@BeforeTest fun setUp() {
mocker.reset() //<1>
this.injectMocks(mocker) //<2>
controller = ControllerImpl(db, api) //<3>
}
@Test fun controllerTest() {
mocker.every { view.render(isAny()) } returns true
controller.start()
mocker.verify { view.render(model) }
}
}
----
<1> Resets the mocker before any test (which removes all mocked behaviour & logged calls), so that each test gets a "clean" mocker.
<2> Injects mocks and fakes.
<3> Create classes to be tested with injected mocks & fakes.

As soon as a class `T` contains a `@Mock` or `@Fake` annotated property, a `T.injectMocks(Mocker)` function will be created by the processor.

IMPORTANT: Don't forget to `reset` the `Mocker` in a `@BeforeTest` method!
Loading

0 comments on commit 71b7595

Please sign in to comment.