Enforcing the Bukkit PluginClassLoader in the JavaPlugin constructor makes unit testing infeasible

Discussion in 'Plugin Development' started by jmatt, Oct 17, 2021.

  1. Offline

    jmatt

    In the constructor(s) for org.bukkit.plugin.java.JavaPlugin, the class loader is enforced and required to be an instance of org.bukkit.plugin.java.PluginClassLoader. If this condition is not met, an IllegalStateException will be thrown with the following stack trace:

    JavaPlugin requires org.bukkit.plugin.java.PluginClassLoader
    java.lang.IllegalStateException: JavaPlugin requires org.bukkit.plugin.java.PluginClassLoader
    at org.bukkit.plugin.java.JavaPlugin.<init>(JavaPlugin.java:51)
    at com.testplugin.TestPlugin.<init>(TestPlugin.java:8)
    ...


    I understand that this is intended design to ensure that the plugin has been loaded from the .jar, but a side effect of doing this in the constructor is that it makes it far more difficult to fully unit test one's plugin using JUnit + Mockito.

    - You can't test the onEnable() or onDisable() methods because you can't create an instance of your plugin to call them on due to the classloader issue
    - You can't mock the instance of your plugin because that is the thing you are trying to test
    - You can't mock the classLoader to be an instance of PluginClassLoader as that class is not accessible
    - You can't mock the constructor in the JavaPlugin class as constructor mocking is not well supported by Mockito & the JavaPlugin class is abstract

    The only way I have been able to get this working is to incorporate PowerMock to directly create an instance of either the JavaPlugin class or my own plugin class in a way that bypasses the constructors entirely, which I don't consider a great solution since generally I try to not rely on PowerMock's bytecode manipulation voodoo.

    Is there a recommended way to test the main plugin class that I'm perhaps missing?
     
    Last edited: Oct 17, 2021
  2. Offline

    timtower Administrator Administrator Moderator

    @jmatt You start a server?
     
  3. Offline

    Kars

    Why not? What is wrong with PowerMockito?
    There is no "recommended" way. If you must do this, use the main JavaPlugin extending class only to initialize another class, in which you do the logic that the main class would otherwise do. Call onEnable and onDisable in your new class and test that. Simple.
     
  4. Offline

    jmatt

    I don't think I understand, sorry. My plugin works fine on my server, no issues there. I'm just talking about unit testing the plugin code independently, in an environment outside of the normal minecraft server .jar.

    Nothing, its a fine tool. I just try to use the ease of writing tests for my code as a guideline for its overall quality. In my own projects if I have a bit of code that is structured in such a way that it's untestable by normal means I usually see this as a sign that something needs refactoring to make it more loosely coupled & cohesive - writing testable code and all that. I guess this makes me (perhaps unjustifiably) perceive PowerMock as some sort of hack solution that I have to bring out a last resort when all else fails. That's just my perspective though, I'm willing to admit I'm just being stubborn and that this is probably an acceptable edge case.

    Mostly I just wanted to see if there was a simple solution that I was just missing that others have already agreed on. I figured Bukkit has been around for a while so I might have been wasting my time on a problem that was already solved.

    I like this idea, thanks for the suggestion!

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Oct 17, 2021

Share This Page