Search code examples
javajmockit

JMockit: howto invoke custom method of MockUp


My application use FastScanner (based on BufferedReader) to read input (Look at code section of my question).

If I want to substitute input in my tests I should mock up FastScanner (Look at code section of my question).

Problem: For each input I should make separate mockup. It'll be greate if I can switch input inside the single Mock Up.

Question: How to add custom methods to JMockit MockUps and than call them? (Look at switchInput method of FastScanner mockup)

Code: [This section is optional, only for your better understanding]

FastScanner

protected static class FastScanner {
    BufferedReader br;
    StringTokenizer st;

    FastScanner(InputStream f) {
        br = new BufferedReader(new InputStreamReader(f));
    }

    String next() throws IOException {
        while (st == null || !st.hasMoreTokens()) {
            st = new StringTokenizer(br.readLine());
        }
        return st.nextToken();
    }

    int nextInt() throws IOException {
        return Integer.parseInt(next());
    }
} 

FastScanner MockUp:

new MockUp<FastScanner>() {

        private int[] input1 = new int[] {17, 2, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1};
        private int[] input2 = new int[] {5, 2, 2, 3, 3, 3, 3, 3};
        private int[] input3 = new int[] {8, 2, 5, 1, 3, 1, 1, 1, 1, 3, 1};
        private byte toggler = 1;
        private byte pointer = 0;

        //HERE THE QUESTION: HOW CAN I CALL THIS METHOD
        public void switchInput() { 
            toggler++;
            pointer = 0;
        }

        @SuppressWarnings("unused")
        int nextInt() throws IOException {
            int[] input = null;
            switch (toggler) {
                case 1: input = input1; break;
                case 2: input = input2; break;
                case 3: input = input3; break;
            }
            return input[pointer++];
        }
    };

Solution

  • You can't have multiple instances of the same MockUp subclass at the same time (each such mock-up would simply override the previous one when it got instantiated). Instead, add an Invocation parameter to your @Mock method, and then use the information it provides to distinguish between multiple data sets. For example:

    @Test
    public void testClientWithVariedDataFromFastScanners()
    {
        new MockUp<FastScanner>() {
            // some data structure for test data
    
            @Mock
            int nextInt(Invocation inv) {
                int idx = inv.getInvocationIndex();
                FastScanner fs = inv.getInvokedInstance();
    
                // Find the next value by using idx or fs as a lookup index
                // into the data structures:
                int i = ...
    
                return i;
            }
        };
    
        client.doSomethingUsingFastScanners();
    }
    

    Also, if you want to call whatever methods (including static methods) on a mock-up subclass, just make it a named class rather than an anonymous one.