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
.
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);
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());
}
@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
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
?
increment
there is a while loop.compareAndSet
got called.second = true; return false
while
again. the compareAndSet
would be called again, this time, it would return true, and the code returned from while loop finally. @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.
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.