/* * Copyright (c) 2006 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit; import java.io.*; import java.lang.reflect.*; import java.util.*; import java.util.concurrent.*; import javax.accessibility.*; import javax.faces.application.*; import javax.security.auth.callback.*; import static mockit.Deencapsulation.*; import static mockit.internal.util.Utilities.*; import static org.junit.Assert.*; import org.junit.*; import org.junit.rules.*; @SuppressWarnings("JUnitTestMethodWithNoAssertions") public final class MockAnnotationsTest { @Rule public final ExpectedException thrown = ExpectedException.none(); final CodeUnderTest codeUnderTest = new CodeUnderTest(); boolean mockExecuted; static class CodeUnderTest { private final Collaborator dependency = new Collaborator(); void doSomething() { dependency.provideSomeService(); } long doSomethingElse(int i) { return dependency.getThreadSpecificValue(i); } } public static class Collaborator { static Object xyz; protected int value; public Collaborator() {} Collaborator(int value) { this.value = value; } @Deprecated protected static String doInternal() { return "123"; } public void provideSomeService() { throw new RuntimeException("Real provideSomeService() called"); } public int getValue() { return value; } void setValue(int value) { this.value = value; } @SuppressWarnings("unused") List<?> complexOperation(Object input1, Object... otherInputs) { return input1 == null ? Collections.emptyList() : Arrays.asList(otherInputs); } @SuppressWarnings("unused") final void simpleOperation(int a, String b, Date c) {} protected long getThreadSpecificValue(int i) { return Thread.currentThread().getId() + i; } } // Mocks without expectations ////////////////////////////////////////////////////////////////////////////////////// static class MockCollaborator1 extends MockUp<Collaborator> { @Mock void provideSomeService() {} } @Test public void mockWithNoExpectationsPassingMockInstance() { new MockCollaborator1(); codeUnderTest.doSomething(); } public interface GenericInterface<T> { void method(T t); String method(int[] ii, T l, String[][] ss, T[] ll); } public interface NonGenericSubInterface extends GenericInterface<Long> {} public static final class MockForNonGenericSubInterface extends MockUp<NonGenericSubInterface> { @Mock public void method(Long l) { assertTrue(l > 0); } @Mock public String method(int[] ii, Long l, String[][] ss, Long[] ll) { assertTrue(ii.length > 0 && l > 0); return "mocked"; } } @Test public void mockMethodOfSubInterfaceWithGenericTypeArgument() { NonGenericSubInterface mock = new MockForNonGenericSubInterface().getMockInstance(); mock.method(123L); assertEquals("mocked", mock.method(new int[] {1}, 45L, null, null)); } @Test public void mockMethodOfGenericInterfaceWithArrayAndGenericTypeArgument() { GenericInterface<Long> mock = new MockUp<GenericInterface<Long>>() { @Mock String method(int[] ii, Long l, String[][] ss, Long[] tt) { assertTrue(ii.length > 0 && l > 0); return "mocked"; } }.getMockInstance(); assertEquals("mocked", mock.method(new int[] {1}, 45L, null, null)); } @Test public void applyMockupsFromInnerMockClassWithMockConstructor() { new MockCollaborator4(); assertFalse(mockExecuted); new CodeUnderTest().doSomething(); assertTrue(mockExecuted); } class MockCollaborator4 extends MockUp<Collaborator> { @Mock void $init() { mockExecuted = true; } @Mock void provideSomeService() {} } // Reentrant mocks ///////////////////////////////////////////////////////////////////////////////////////////////// @Test public void applyReentrantMockup() { thrown.expect(RuntimeException.class); new MockCollaboratorWithReentrantMock(); codeUnderTest.doSomething(); } static class MockCollaboratorWithReentrantMock extends MockUp<Collaborator> { @Mock int getValue() { return 123; } @Mock void provideSomeService(Invocation inv) { inv.proceed(); } } // Mocks for constructors and static methods /////////////////////////////////////////////////////////////////////// @Test public void applyMockupForConstructor() { new MockCollaboratorWithConstructorMock(); new FacesMessage("test"); } static class MockCollaboratorWithConstructorMock extends MockUp<FacesMessage> { @Mock void $init(String value) { assertEquals("test", value); } } public static class SubCollaborator extends Collaborator { public SubCollaborator(int i) { throw new RuntimeException(String.valueOf(i)); } @Override public void provideSomeService() { value = 123; } } @Test public void applyMockupForClassHierarchy() { new MockUp<SubCollaborator>() { @Mock void $init(Invocation inv, int i) { assertNotNull(inv.getInvokedInstance()); assertTrue(i > 0); } @Mock void provideSomeService(Invocation inv) { SubCollaborator it = inv.getInvokedInstance(); it.value = 45; } @Mock int getValue(Invocation inv) { assertNotNull(inv.getInvokedInstance()); // The value of "it" is undefined here; it will be null if this is the first mock invocation reaching this // mock class instance, or the last instance of the mocked subclass if a previous invocation of a mock // method whose mocked method is defined in the subclass occurred on this mock class instance. return 123; } }; SubCollaborator collaborator = new SubCollaborator(123); collaborator.provideSomeService(); assertEquals(45, collaborator.value); assertEquals(123, collaborator.getValue()); } @Test public void mockNativeMethodInClassWithRegisterNatives() { new MockSystem(); assertEquals(0, System.nanoTime()); } static class MockSystem extends MockUp<System> { @Mock public static long nanoTime() { return 0; } } @Test public void mockNativeMethodInClassWithoutRegisterNatives() throws Exception { // For some reason, the native method doesn't get mocked when running on Java 9. if (!JAVA9) { new MockFloat(); assertEquals(0.0, Float.intBitsToFloat(2243019), 0.0); } } static class MockFloat extends MockUp<Float> { @SuppressWarnings("UnusedDeclaration") @Mock public static float intBitsToFloat(int bits) { return 0; } } @After public void checkThatLocalMockUpsHaveBeenTornDown() { assertTrue(System.nanoTime() > 0); assertTrue(Float.intBitsToFloat(2243019) > 0); } @Test public void applyMockupForJREClass() { MockThread mockThread = new MockThread(); Thread.currentThread().interrupt(); assertTrue(mockThread.interrupted); } public static class MockThread extends MockUp<Thread> { boolean interrupted; @Mock public void interrupt() { interrupted = true; } } // Stubbing of static class initializers /////////////////////////////////////////////////////////////////////////// @Test public void fakeStaticInitializer() { new MockUp<AccessibleState>() { @Mock void $clinit() {} }; assertNull(AccessibleState.ACTIVE); } // Other tests ///////////////////////////////////////////////////////////////////////////////////////////////////// @Test public void mockJREInterface() throws Exception { CallbackHandler callbackHandler = new MockCallbackHandler().getMockInstance(); callbackHandler.handle(new Callback[] {new NameCallback("Enter name:")}); } public static class MockCallbackHandler extends MockUp<CallbackHandler> { @Mock public void handle(Callback[] callbacks) { assertEquals(1, callbacks.length); assertTrue(callbacks[0] instanceof NameCallback); } } @Test public void mockJREInterfaceWithMockUp() throws Exception { CallbackHandler callbackHandler = new MockUp<CallbackHandler>() { @Mock void handle(Callback[] callbacks) { assertEquals(1, callbacks.length); assertTrue(callbacks[0] instanceof NameCallback); } }.getMockInstance(); callbackHandler.handle(new Callback[] {new NameCallback("Enter name:")}); } public interface AnInterface { int doSomething(); } @Test public void mockPublicInterfaceWithMockUpHavingInvocationParameter() { AnInterface obj = new MockUp<AnInterface>() { @Mock int doSomething(Invocation inv) { assertNotNull(inv.getInvokedInstance()); return 122 + inv.getInvocationCount(); } }.getMockInstance(); assertEquals(123, obj.doSomething()); } abstract static class AnAbstractClass { protected abstract int doSomething(); } @Test public <A extends AnAbstractClass> void mockAbstractClassWithMockForAbstractMethodHavingInvocationParameter() { final AnAbstractClass obj = new AnAbstractClass() { @Override protected int doSomething() { return 0; } }; new MockUp<A>() { @Mock int doSomething(Invocation inv) { assertSame(obj, inv.getInvokedInstance()); Method invokedMethod = inv.getInvokedMember(); assertTrue(AnAbstractClass.class.isAssignableFrom(invokedMethod.getDeclaringClass())); return 123; } }; assertEquals(123, obj.doSomething()); } interface AnotherInterface { int doSomething(); } AnotherInterface interfaceInstance; @Test public void attemptToProceedIntoInterfaceImplementation() { thrown.expect(UnsupportedOperationException.class); thrown.expectMessage("abstract/interface method"); interfaceInstance = new MockUp<AnotherInterface>() { @Mock int doSomething(Invocation inv) { return inv.proceed(); } }.getMockInstance(); interfaceInstance.doSomething(); } @Test public void mockNonPublicInterfaceWithMockUpHavingInvocationParameter() { interfaceInstance = new MockUp<AnotherInterface>() { @Mock int doSomething(Invocation inv) { AnotherInterface instanceThatWasInvoked = inv.getInvokedInstance(); assertSame(interfaceInstance, instanceThatWasInvoked); int invocationCount = inv.getInvocationCount(); assertTrue(invocationCount > 0); return invocationCount == 1 ? instanceThatWasInvoked.doSomething() : 123; } }.getMockInstance(); assertEquals(123, interfaceInstance.doSomething()); } @Test public void mockGenericInterfaceWithMockUpHavingInvocationParameter() throws Exception { Callable<String> mock = new MockUp<Callable<String>>() { @Mock String call(Invocation inv) { return "mocked"; } }.getMockInstance(); assertEquals("mocked", mock.call()); } static class GenericClass<T> { protected T doSomething() { return null; } } @Test public void mockGenericClassWithMockUpHavingInvocationParameter() { new MockUp<GenericClass<String>>() { @Mock String doSomething(Invocation inv) { return "mocked"; } }; GenericClass<String> mock = new GenericClass<String>(); assertEquals("mocked", mock.doSomething()); } @Test public void mockFileConstructor() { new MockUp<File>() { @Mock void $init(Invocation inv, String pathName) { File it = inv.getInvokedInstance(); setField(it, "path", "fixedPrefix/" + pathName); } }; File f = new File("test"); assertEquals("fixedPrefix/test", f.getPath()); } @Test @SuppressWarnings("MethodWithMultipleLoops") public void concurrentMock() throws Exception { new MockUp<Collaborator>() { @Mock long getThreadSpecificValue(int i) { return Thread.currentThread().getId() + 123; } }; Thread[] threads = new Thread[5]; for (int i = 0; i < threads.length; i++) { threads[i] = new Thread() { @Override public void run() { long threadSpecificValue = Thread.currentThread().getId() + 123; long actualValue = new CodeUnderTest().doSomethingElse(0); assertEquals(threadSpecificValue, actualValue); } }; } for (Thread thread : threads) { thread.start(); } for (Thread thread : threads) { thread.join(); } } @Test public void mockUpAffectsInstancesOfSpecifiedSubclassAndNotOfBaseClass() { new MockUpForSubclass(); // Mocking applies to instance methods executed on instances of the subclass: assertEquals(123, new SubCollaborator(5).getValue()); // And to static methods from any class in the hierarchy: //noinspection deprecation assertEquals("mocked", Collaborator.doInternal()); // But not to instance methods executed on instances of the base class: assertEquals(62, new Collaborator(62).getValue()); } static class MockUpForSubclass extends MockUp<SubCollaborator> { @Mock void $init(int i) {} @Mock String doInternal() { return "mocked"; } @Mock int getValue() { return 123; } } }