@weixin
        
        2014-11-23T16:05:11.000000Z
        字数 6415
        阅读 1560
    mock
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"; }}@Testpublic 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.
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"); }}@Testpublic void useClassWithStaticInitializerNeverStubbedOutAndNotMockedNow(){// Allows the class to be initialized without throwing the exception.MockUp<?> mockUp = new MockUp<ClassWithStaticInitializer2>() {/* @Mockvoid $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);
static class ClassWhichCallsStaticMethodFromInitializer{static {String s = someValue();s.length();}static String someValue() { return "some value"; }}@Testpublic void mockUninitializedClass(@Mocked ClassWhichCallsStaticMethodFromInitializer unused){assertNull(ClassWhichCallsStaticMethodFromInitializer.someValue());}@Testpublic void mockInitializedClass(){MockUp<?> mockup = new MockUp<ClassWhichCallsStaticMethodFromInitializer>(){@Mockvoid $clinit() {}@MockString someValue() {return "hi";}};assertNotNull(ClassWhichCallsStaticMethodFromInitializer.someValue());mockup.tearDown();assertEquals("some value", ClassWhichCallsStaticMethodFromInitializer.someValue());}
@Deprecatedstatic 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; }}@Testpublic void attemptToCreateMockUpWithMockMethodLackingCorrespondingRealMethod(){// thrown.expect(IllegalArgumentException.class);// thrown.expectMessage("$init(int i");// put aa instantialization won't print any thingCollaborator 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 consoleCollaborator ba = new Collaborator();}
notice , mock init(int i) is wrong, because Collaborator doesn't have a such a constructor
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;}}}}@Testpublic 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?
increment there is a while loop.compareAndSet got called.second = true; return falsewhile again. the compareAndSet would be called again, this time, it would return true, and the code returned from while loop finally.
@Testpublic void mockUpUsingInvocationParameters(){new MockUp<Collaborator>() {@Mock(invocations = 1)void $init(Invocation inv, boolean b){Collaborator it = inv.getInvokedInstance();assertFalse(it.b);assertTrue(b);}@Mockint 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:
@Testpublic 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);}@Mockint 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:
315208913com.weixin.test.MockUpTest$Collaborator@12c9b4d1315208913com.weixin.test.MockUpTest$Collaborator@12c9b4d1it 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.
static class Outer{class Inner{final int value;Inner(int value) { this.value = value; }}}@Testpublic 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.