jmockit study notes

Class Initialization mock up

Ex 1

static class ClassWithStaticInitializer1
    {
        static final String CONSTANT = new String("not a compile-time constant");
        static String variable;
        static { variable = doSomething(); }
        static String doSomething() { return "real value"; }
    }

    @Test
    public void mockClassWithStaticInitializerNotStubbedOut(@Mocked/*(stubOutClassInitialization = true)*/
            ClassWithStaticInitializer1 mocked) {
        assertNotNull(ClassWithStaticInitializer1.CONSTANT);
        assertNull(ClassWithStaticInitializer1.doSomething());
//        assertEquals("real value", ClassWithStaticInitializer1.variable);
        assertNull("it is null", ClassWithStaticInitializer1.variable);
    }

notice the code I comment out /*(stubOutClassInitialization = true)*/, if I uncomment it, the test would failed at line assertNotNull(ClassWithStaticInitializer1.CONSTANT);, CONSTANT would become null.

Ex2

another very similar example,

    static class ClassWithStaticInitializer2
    {
        static final String CONSTANT = new String("not a compile-time constant");
        static { doSomething(); }
        static void doSomething() { throw new UnsupportedOperationException("must not execute"); }
    }

    @Test
    public void useClassWithStaticInitializerNeverStubbedOutAndNotMockedNow()
    {
        // Allows the class to be initialized without throwing the exception.
        MockUp<?> mockUp = new MockUp<ClassWithStaticInitializer2>() {
/*          @Mock
            void $clinit() {}*/
            @Mock void doSomething() {} };

        // Initializes the class:
        assertNotNull(ClassWithStaticInitializer2.CONSTANT);

        // Restore the now initialized class:
        mockUp.tearDown();

        try {
            ClassWithStaticInitializer2.doSomething();
            fail();
        }
        catch (UnsupportedOperationException ignore) {}
    }

be aware of this part of code snippet,@Mock void $clinit() {}, without commenting it, this line would also fail : assertNotNull(ClassWithStaticInitializer2.CONSTANT);

Ex3

static class ClassWhichCallsStaticMethodFromInitializer
    {
        static {
            String s = someValue();
            s.length();
        }

        static String someValue() { return "some value"; }
    }

    @Test
    public void mockUninitializedClass(@Mocked ClassWhichCallsStaticMethodFromInitializer unused)
    {
        assertNull(ClassWhichCallsStaticMethodFromInitializer.someValue());
    }

    @Test
    public void mockInitializedClass()
    {
        MockUp<?> mockup = new MockUp<ClassWhichCallsStaticMethodFromInitializer>(){
          @Mock
          void $clinit() {}
          @Mock
          String someValue() {return "hi";}
        };
        assertNotNull(ClassWhichCallsStaticMethodFromInitializer.someValue());
        mockup.tearDown();
        assertEquals("some value", ClassWhichCallsStaticMethodFromInitializer.someValue());
    }

EX4

@Deprecated
    static final class Collaborator
    {
        @Deprecated final boolean b;

        @Deprecated Collaborator() { b = false; }
        Collaborator(boolean b) { this.b = b; }

        @Ignore("test") int doSomething(@Deprecated String s) { return s.length(); }

        <N extends Number> N genericMethod(@SuppressWarnings("unused") N n) { return null; }

        @Deprecated static boolean doSomethingElse() { return false; }
    }

    @Test
    public void attemptToCreateMockUpWithMockMethodLackingCorrespondingRealMethod()
    {
//        thrown.expect(IllegalArgumentException.class);
//        thrown.expectMessage("$init(int i");

        // put aa instantialization won't print any thing
        Collaborator aa = new Collaborator();
        new MockUp<Collaborator>() { @Mock
//                                     void $init(int i) { System.out.println(i); } };
                                       void $init() { System.out.println("i am hero..."); } };
       // ba would print out in console
        Collaborator ba = new Collaborator();
    }

notice , mock init(int i) is wrong, because Collaborator doesn't have a such a constructor

EX5

 static final class Main
    {
        static final AtomicIntegerFieldUpdater<Main> atomicCount =
                AtomicIntegerFieldUpdater.newUpdater(Main.class, "count");

        volatile int count;
        int max = 2;

        boolean increment()
        {
            while (true) {
                int currentCount = count;

                if (currentCount >= max) {
                    return false;
                }

                if (atomicCount.compareAndSet(this, currentCount, currentCount + 1)) {
                    return true;
                }
            }
        }
    }

    @Test
    public void mockUpGivenClass()
    {
        final Main main = new Main();
        AtomicIntegerFieldUpdater<?> atomicCount = Deencapsulation.getField(Main.class, AtomicIntegerFieldUpdater.class);

        new MockUp<AtomicIntegerFieldUpdater<?>>(atomicCount.getClass()) {
            boolean second;

            @Mock(invocations = 2)
            public boolean compareAndSet(Object obj, int expect, int update)
            {
                assertSame(main, obj);
                assertEquals(0, expect);
                assertEquals(1, update);

                if (second) {
                    return true;
                }

                second = true;
                return false;
            }
        };

        assertTrue(main.increment());
    }

protected MockUp(Class<?> classToMock) Applies the mock methods defined in the mock-up subclass to the given class/interface.

given a type, then a particular class, you can mock that given class, this is what new MockUp<AtomicIntegerFieldUpdater<?>>(atomicCount.getClass()) { does

why compareAndSet has a anotation - invocations=2?

  1. call increament. notice the inside the increment there is a while loop.
  2. goes to the if block, this is the first time of compareAndSet got called.
  3. goes to the mocked implementation, by default, second is false, so second = true; return false
  4. goes to the while again. the compareAndSet would be called again, this time, it would return true, and the code returned from while loop finally.

EX6

 @Test
    public void mockUpUsingInvocationParameters()
    {
        new MockUp<Collaborator>() {
            @Mock(invocations = 1)
            void $init(Invocation inv, boolean b)
            {
                Collaborator it = inv.getInvokedInstance();
                assertFalse(it.b);
                assertTrue(b);
            }

            @Mock
            int doSomething(Invocation inv, String s)
            {
                return inv.proceed(s + ": mocked");
            }
        };

        int i = new Collaborator(true).doSomething("test");

        assertEquals(12, i);
    }

notice the usage of invocation parameters, it would call the real implementation. For this code:

    Collaborator it = inv.getInvokedInstance();
    assertFalse(it.b);
    assertTrue(b);

I don't know why it.b is false?

Then I put some diagnostic output there:

@Test
    public void mockUpUsingInvocationParameters()
    {
        new MockUp<Collaborator>() {
            @Mock(invocations = 1)
            void $init(Invocation inv, boolean b)
            {
                Collaborator it = inv.getInvokedInstance();
                System.out.println(it.hashCode());
                System.out.println(it.toString());
                assertFalse(it.b);
                assertTrue(b);
            }

            @Mock
            int doSomething(Invocation inv, String s)
            {
                return inv.proceed(s + ": mocked");
            }
        };

        Collaborator aa = new Collaborator(true);
        System.out.println(aa.hashCode());
        System.out.println(aa.toString());
        if(aa.b)
        {
            System.out.println("it is true..");
        }
        else
        {

            System.out.println("it is false..");
        }
        int i = aa.doSomething("test");

//        int i = new Collaborator(true).doSomething("test");

        assertEquals(12, i);
    }

the output is like this:

315208913
com.weixin.test.MockUpTest$Collaborator@12c9b4d1
315208913
com.weixin.test.MockUpTest$Collaborator@12c9b4d1
it is false..
Tests run: 1,

in fact, it is very simple, because we also mocked the boolean constructor, so the original constructor doesn't get executed. this.b=b doens't run. Also you could see the inv.getInvokedInstace() return the exact instance of aa.

Ex 7

static class Outer
    {
        class Inner
        {
            final int value;
            Inner(int value) { this.value = value; }
        }
    }

    @Test
    public void mockConstructorOfInnerClass()
    {
        final Outer outer = new Outer();

        new MockUp<Outer.Inner>() {
            @Mock void $init(Outer o, int i)
            {
                assertSame(outer, o);
                assertEquals(123, i);
            }
        };

        Outer.Inner inner = outer.new Inner(123);
        assertEquals(0, inner.value);
    }

notice how to mockup a inner class.