-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #90 from Shynixn/development
Merge changes to Master --release
- Loading branch information
Showing
19 changed files
with
432 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
# Unit-Tests with MCCoroutine | ||
|
||
(This site is only relevant for Spigot, Paper and CraftBukkit. If you need Unit-Tests support for BungeeCord, Sponge or | ||
Velocity, please submit an issue on GitHub) | ||
|
||
If you try to write Unit- or IntegrationTests for your Minecraft plugin, you may need to test suspend functions. These | ||
functions | ||
may use ``plugin.launch{...}`` or other extension methods from MCCoroutine. | ||
|
||
However, extensive mocking is required to get MCCoroutine to work without a running server. As a solution to this | ||
problem, a new test dependency is available, which | ||
closely simulates MCCoroutine under real conditions. This means you can focus on writing your tests and get a very close | ||
feedback to the real environment. | ||
|
||
### 1. Add the dependency | ||
|
||
**Do not** shade this library into your final plugin.jar file. This should only be available during UnitTests. | ||
|
||
```kotlin | ||
dependencies { | ||
testImplementation("com.github.shynixn.mccoroutine:mccoroutine-bukkit-test:2.9.0") | ||
} | ||
``` | ||
|
||
### 2. Create a test method | ||
|
||
```kotlin | ||
import org.junit.jupiter.api.Test | ||
|
||
class MyExampleTest { | ||
@Test | ||
fun testCase01(){ | ||
} | ||
} | ||
``` | ||
|
||
### 3. Change the MCCoroutine Production-Driver to the Test-Driver | ||
|
||
|
||
```kotlin | ||
import org.junit.jupiter.api.Test | ||
|
||
class MyExampleTest { | ||
|
||
init { | ||
/** | ||
* This switches MCCoroutine to the test implementation of MCCoroutine. | ||
* It affects all the tests in the current session. | ||
*/ | ||
MCCoroutine.Driver = TestMCCoroutine.Driver | ||
} | ||
|
||
@Test | ||
fun testCase01(){ | ||
} | ||
} | ||
``` | ||
|
||
#### 4. Use MCCoroutine in the same way as on your server | ||
|
||
```kotlin | ||
import org.junit.jupiter.api.Test | ||
|
||
class MyExampleTest { | ||
|
||
init { | ||
/** | ||
* This switches MCCoroutine to the test implementation of MCCoroutine. | ||
* It affects all the tests in the current session. | ||
*/ | ||
MCCoroutine.Driver = TestMCCoroutine.Driver | ||
} | ||
|
||
@Test | ||
fun testCase01(){ | ||
// Uses the mocking library called Mockito to mock a plugin instance. | ||
// It does not matter how you create a plugin instance. Other mocking libraries work as well. | ||
val plugin = Mockito.mock(Plugin::class.java) | ||
|
||
// We need to use runBlocking here, otherwise the test exits early | ||
runBlocking(plugin.minecraftDispatcher) { | ||
println("Step 1: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
|
||
withContext(Dispatchers.IO) { | ||
println("Step 2: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
delay(1000) | ||
} | ||
|
||
println("Step 3: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
|
||
// As always, prefer using Dispatchers.IO instead of plugin.asyncDispatcher. | ||
withContext(plugin.asyncDispatcher) { | ||
println("Step 4: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
delay(1000) | ||
} | ||
|
||
println("Step 5: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
|
||
// Just as an example, we can also use plugin.launch | ||
plugin.launch { | ||
println("Step 6: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
delay(1000) | ||
println("Step 7: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
}.join() // Wait until finished. | ||
} | ||
} | ||
} | ||
``` | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
82 changes: 82 additions & 0 deletions
82
mccoroutine-bukkit-sample/src/test/java/ExampleUnitTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import com.github.shynixn.mccoroutine.bukkit.MCCoroutine | ||
import com.github.shynixn.mccoroutine.bukkit.asyncDispatcher | ||
import com.github.shynixn.mccoroutine.bukkit.launch | ||
import com.github.shynixn.mccoroutine.bukkit.minecraftDispatcher | ||
import com.github.shynixn.mccoroutine.bukkit.sample.impl.FakeDatabase | ||
import com.github.shynixn.mccoroutine.bukkit.sample.impl.UserDataCache | ||
import com.github.shynixn.mccoroutine.bukkit.test.TestMCCoroutine | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.delay | ||
import kotlinx.coroutines.runBlocking | ||
import kotlinx.coroutines.withContext | ||
import org.bukkit.entity.Player | ||
import org.bukkit.plugin.Plugin | ||
import org.junit.jupiter.api.Test | ||
import org.mockito.Mockito | ||
import kotlin.test.assertEquals | ||
|
||
class ExampleUnitTest { | ||
|
||
init { | ||
/** | ||
* This switches MCCoroutine to the test implementation of MCCoroutine. | ||
* It affects all the tests in the current session. | ||
*/ | ||
MCCoroutine.Driver = TestMCCoroutine.Driver | ||
} | ||
|
||
@Test | ||
fun test1() { | ||
// Uses the mocking library called Mockito to mock a plugin instance. | ||
// It does not matter how you create a plugin instance. Other mocking libraries work as well. | ||
val plugin = Mockito.mock(Plugin::class.java) | ||
|
||
// We need to use runBlocking here, otherwise the test exits early | ||
runBlocking(plugin.minecraftDispatcher) { | ||
println("Step 1: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
|
||
withContext(Dispatchers.IO) { | ||
println("Step 2: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
delay(1000) | ||
} | ||
|
||
println("Step 3: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
|
||
// As always, prefer using Dispatchers.IO instead of plugin.asyncDispatcher. | ||
withContext(plugin.asyncDispatcher) { | ||
println("Step 4: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
delay(1000) | ||
} | ||
|
||
println("Step 5: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
|
||
// Just as an example, we can also use plugin.launch | ||
plugin.launch { | ||
println("Step 6: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
delay(1000) | ||
println("Step 7: " + Thread.currentThread().name + "/" + Thread.currentThread().id) | ||
}.join() // Wait until finished. | ||
} | ||
} | ||
|
||
@Test | ||
fun test2() { | ||
// Uses the mocking library called Mockito to mock a plugin and a player instance. | ||
// It does not matter how you create a plugin instance. Other mocking libraries work as well. | ||
val plugin = Mockito.mock(Plugin::class.java) | ||
val player = Mockito.mock(Player::class.java) | ||
|
||
// The 'Unit' we want to test. | ||
val fakeDatabase = FakeDatabase() | ||
val classUnderTest = UserDataCache(plugin, fakeDatabase) | ||
|
||
// Act and Assert. | ||
runBlocking(plugin.minecraftDispatcher) { | ||
val data1 = classUnderTest.getUserDataFromPlayerAsync(player) | ||
val data2 = classUnderTest.getUserDataFromPlayerAsync(player) | ||
|
||
// Should be the same instance because of cache hit. Hashcode should be equal. | ||
assertEquals(data1, data2) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
repositories { | ||
maven { | ||
url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") | ||
} | ||
} | ||
|
||
dependencies { | ||
implementation(project(":mccoroutine-bukkit-api")) | ||
|
||
compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9") | ||
compileOnly("org.spigotmc:spigot-api:1.16.3-R0.1-SNAPSHOT") | ||
|
||
testCompile("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9") | ||
testCompile("org.spigotmc:spigot-api:1.16.3-R0.1-SNAPSHOT") | ||
} |
13 changes: 13 additions & 0 deletions
13
...e-bukkit-test/src/main/java/com/github/shynixn/mccoroutine/bukkit/test/TestMCCoroutine.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.github.shynixn.mccoroutine.bukkit.test | ||
|
||
import com.github.shynixn.mccoroutine.bukkit.test.impl.TestMCCoroutineImpl | ||
|
||
interface TestMCCoroutine { | ||
companion object { | ||
/** | ||
* The driver to load the test implementation of MCCoroutine. | ||
* Useful for UnitTests. | ||
*/ | ||
val Driver = TestMCCoroutineImpl::class.java.name | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
...ava/com/github/shynixn/mccoroutine/bukkit/test/dispatcher/TestAsyncCoroutineDispatcher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.github.shynixn.mccoroutine.bukkit.test.dispatcher | ||
|
||
import kotlinx.coroutines.CoroutineDispatcher | ||
import java.util.concurrent.ExecutorService | ||
import java.util.concurrent.Executors | ||
import kotlin.coroutines.CoroutineContext | ||
|
||
internal class TestAsyncCoroutineDispatcher(private val minecraftDispatcher: TestMinecraftCoroutineDispatcher) : | ||
CoroutineDispatcher() { | ||
private val threadPool: ExecutorService = Executors.newFixedThreadPool(4) | ||
|
||
override fun isDispatchNeeded(context: CoroutineContext): Boolean { | ||
return Thread.currentThread().id == minecraftDispatcher.threadId | ||
} | ||
|
||
override fun dispatch(context: CoroutineContext, block: Runnable) { | ||
threadPool.submit { | ||
Thread.currentThread().name = "[TestAsyncThread]" | ||
block.run() | ||
} | ||
} | ||
|
||
fun dispose() { | ||
threadPool.shutdown() | ||
} | ||
} |
Oops, something went wrong.