Java, unit test mocking mxbean

290 Views Asked by At

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?

2

There are 2 best solutions below

3
On BEST ANSWER

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).

0
On

One simple way to write the test is as follows, no mocking involved:

public class ThreadStatsTest {
    Comparator<Long> comparator = new ThreadStats().new ThreadCPUCompare();

    @Test
    public void orderThreadIdsFromLongestToShortestCPUTime() {
        long longLivedThread = Thread.currentThread().getId(); // > 0 cpu time
        long shortLivedThread = new Thread().getId(); // 0 cpu time

        int longTimeFirst = comparator.compare(longLivedThread, shortLivedThread);
        int sameTimes = comparator.compare(longLivedThread, longLivedThread);
        int longTimeSecond = comparator.compare(shortLivedThread, longLivedThread);

        assertEquals(-1, longTimeFirst);
        assertEquals( 0, sameTimes);
        assertEquals( 1, longTimeSecond);
    }
}