How can I properly mock a JavaPlugin isntance? (Mockito + JUnit)

Discussion in 'Plugin Development' started by HelpMeILostMyAccount, Jul 21, 2019.

Thread Status:
Not open for further replies.
  1. I have been trying to write unit tests for the "onCommand" method of my JavaPlugin instance.
    The first road block I came across was not being able to make a direct instance of the plugin, so I had to mock the instance, and call the onCommand method on the instance.
    The command I'm testing links two IDs in a HashBiMap (Google Guava) with some run-time steps in between that need to be tested.
    Code:
    Command commandMock = mock(Command.class);
    CommandSender commandSenderMock = mock(CommandSender.class);
    
    HashBiMap<Long, OfflinePlayer> instance = HashBiMap.create();
    
    when( commandMock.getName() ).thenReturn( "register-account" );
    when( javaPluginMock.getLinkedAccountsMap() ).thenReturn( instance );
    
    javaPluginMock.onCommand(commandSenderMock, commandMock, "", new String[]{"342002891953012737", "67fe62ad-2efd-4a2a-8ae5-98cc925116fc"});
    
    // Verify that a <Long, OfflinePlayer> link was added
    assertTrue(instance.containsKey(342002891953012737L));
    assertTrue(instance.containsValue(Bukkit.getOfflinePlayer(UUID.fromString("67fe62ad-2efd-4a2a-8ae5-98cc925116fc"))));
    
    I plugged two dummy objects for the command and the sender. The command name to be tested is "register-account", and I made a test instance of the HashBiMap to test with. I passed in two constant, possible argument strings for the method. After a debug, I realized onCommand did absolutely nothing, and I suspect it's because javaPluginMock is a mocked object, and hasn't been told to react to "onCommand" yet. I am stuck. I would greatly appreciate some pointers, as I am very new to unit testing as a whole.
     
  2. Offline

    Kars

    First of all, you need to inject your mocks into your object under test. You can use Mockitos annotations for this and do not need to instantiate the mocks yourself.
    PHP:
    @Mock
    private Command;
    This will mock Command for you in this case.
    You then proceed to inject the mock into your object under test, using the annotation @Inject. No need to instantiate. Note that this only works when your object under test uses dependency injection.

    https://static.javadoc.io/org.mockito/mockito-core/3.0.0/org/mockito/InjectMocks.html

    To get to your particular case. I'm not sure you are applying mocks correctly. You use a mock when you want to test object A, which depends on object B, without using an instance of object B. The goal is to test classes independently.

    Your code, however, is incomplete and i'm going to need a bit more context.
     
  3. I want to link player UUIDs with an ID for a different account of something else I'm trying to do. The command's arguments would be the Long ID and the player's UUID. onCommand turns parameter 1 into a long and the UUID into an OfflinePlayer. It then puts that link in the HashBiMap. That is it. I have read a bit about @InjectMocks, I mocked the fields the class needs to start, and I made a Spy of the HashBiMap.
    Code:
    private HashBiMap<Long, OfflinePlayer> linkedAccountsMapTest = HashBiMap.create();
    
    @Mock private Command commandMock;
    @Mock private CommandSender commandSenderMock;
    @Spy private HashBiMap<Long, OfflinePlayer> linkedAccountsMap = spy(linkedAccountsMapTest);
    @Spy private Connection SQLConnection;
    
    @InjectMocks private TheBestPlugin javaPluginMock;
    
    But I'm getting a MockitoException, stating that the constructor threw an exception.
    Code:
    org.mockito.exceptions.base.MockitoException:
    Cannot instantiate @InjectMocks field named 'javaPluginMock' of type 'class com.alelav.TheBestPlugin'.
    You haven't provided the instance at field declaration so I tried to construct the instance.
    However the constructor or the initialization block threw an exception : JavaPlugin requires org.bukkit.plugin.java.PluginClassLoader
    
    It needs a "PluginClassLoader". Not sure where to go from here
     
  4. Offline

    Kars

  5. Alright, just to tell people who see this thread later. I was testing the instance of the plugin, so mocking the plugin was completely wrong. You must mock everything else that has to do with your instance under test, including injected dependencies. The instance under test must always be real.
     
Thread Status:
Not open for further replies.

Share This Page