Search code examples
javaunit-testingmockingmxbean

Java, unit test mocking mxbean


I want to sort all the Java threads on CPU time. I use the ThreadMXBean to get the CPU time of the threads by thread ID. The comparator is used for sortinging the Thread ID's.

public class ThreadStats{
    private static ThreadMXBean mxbean = ManagementFactory.getThreadMXBean();

    class ThreadCPUCompare implements Comparator<Long>{

        @Override
        public int compare(Long threadId1, Long threadId2) {
            return Long.compare(mxbean.getThreadCpuTime(threadId2), mxbean.getThreadCpuTime(threadId1));
        }
    }
}

And I've made the following unit test:

@RunWith(MockitoJUnitRunner.class)
public class ThreadStatsTest {

@InjectMocks ThreadCPUCompare comperator = new ThreadStats().new ThreadCPUCompare();
@Mock ThreadMXBean mxbean;

@Test
public void threadCPUSortTest() {
    Mockito.when(mxbean.getThreadCpuTime(1L)).thenReturn(3L);
    Mockito.when(mxbean.getThreadCpuTime(2L)).thenReturn(2L);
    Mockito.when(mxbean.getThreadCpuTime(3L)).thenReturn(4L);
    Mockito.when(mxbean.getThreadCpuTime(4L)).thenReturn(1L);

    List<Long>expectedList = new ArrayList<Long>();
    expectedList.add(3L);
    expectedList.add(1L);
    expectedList.add(2L);
    expectedList.add(4L);

    List<Long>actualList = new ArrayList<Long>();
    actualList.add(4L);
    actualList.add(2L);
    actualList.add(3L);
    actualList.add(1L);


    //Sorting of the actual list
    Collections.sort(actualList, comperator);

    assertEquals(expectedList, actualList);
    }
}

But I cant get the test to work. I think because the mocking doesn't work. Could someone show me how to fix the unit test please?


Solution

  • Your test is failing because the mock is not being injected. Mockito will not inject into static fields, nor will it inject into an outer class (such as the ThreadStats class in your case).

    You need to write the code as something like this:

    class ThreadCPUCompare implements Comparator<Long>
    {
        private ThreadMXBean mxbean;
    
        @Override
        public int compare(Long threadId1, Long threadId2) {
            return Long.compare(mxbean.getThreadCpuTime(threadId2), mxbean.getThreadCpuTime(threadId1));
        }
    }
    
    @RunWith(MockitoJUnitRunner.class)
    public class ThreadStatsTest
    {
        @Mock ThreadMXBean mxbean;
        @InjectMocks Comparator comperator = new ThreadCPUCompare();
    
        @Test
        public void threadCPUSortTest() {
            // do your tests exactly as before
        }
    }
    

    You will then have the challenge of wiring it into the production code, but that is a different exercise, where I would recommend some sort of dependency injection (guice, spring, manual etc depending on context and preferences).