Unit-Tests with MCCoroutine
Unit-Test support is only available for mccoroutine-bukkit
and mccoroutine-folia
. Please create a GitHub ticket, if you want support for other libraries.
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.
dependencies {
testImplementation("com.github.shynixn.mccoroutine:mccoroutine-bukkit-test:2.21.0")
}
dependencies {
testImplementation("com.github.shynixn.mccoroutine:mccoroutine-folia-test:2.21.0")
}
2. Create a test method
import org.junit.jupiter.api.Test
class MyExampleTest {
@Test
fun testCase01(){
}
}
3. Change the MCCoroutine Production-Driver to the Test-Driver
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
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.
}
}
}