/* * 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.management.*; import java.net.*; import java.nio.channels.*; import java.util.*; import java.util.concurrent.*; import org.junit.*; import org.junit.runners.*; import static org.junit.Assert.*; import mockit.internal.*; @SuppressWarnings("ConstantConditions") @FixMethodOrder(MethodSorters.NAME_ASCENDING) public final class CascadingParametersTest { static class Foo { Bar getBar() { return null; } static Bar globalBar() { return null; } void doSomething(String s) { throw new RuntimeException(s); } int getIntValue() { return 1; } Boolean getBooleanValue() { return true; } final List<Integer> getList() { return null; } HashMap<?, ?> getMap() { return null; } } static class Bar { Bar() { throw new RuntimeException(); } int doSomething() { return 1; } Baz getBaz() { return null; } Baz getBaz(@SuppressWarnings("unused") int i) { return null; } AnEnum getEnum() { return null; } static String staticMethod() { return "notMocked"; } } static final class SubBar extends Bar {} public interface Baz { void runIt(); Date getDate(); } enum AnEnum { First, Second, Third } static Bar cascadedBar1; static Bar cascadedBar2; @Test public void cascadeOneLevelDuringReplay(@Mocked Foo foo) { cascadedBar1 = foo.getBar(); assertEquals(0, cascadedBar1.doSomething()); cascadedBar2 = Foo.globalBar(); assertEquals(0, cascadedBar2.doSomething()); Bar bar = foo.getBar(); assertSame(cascadedBar1, bar); Bar globalBar = Foo.globalBar(); assertSame(cascadedBar2, globalBar); assertNotSame(bar, globalBar); foo.doSomething("test"); assertEquals(0, foo.getIntValue()); assertFalse(foo.getBooleanValue()); assertTrue(foo.getList().isEmpty()); Map<?, ?> map = foo.getMap(); assertNull(map); } @Test public void verifyThatPreviousCascadedInstancesHaveBeenDiscarded(@Mocked Foo foo) { Bar bar = foo.getBar(); assertNotSame(cascadedBar1, bar); Bar globalBar = Foo.globalBar(); assertNotSame(cascadedBar2, globalBar); } @Test public void verifyThatStaticMethodsAndConstructorsAreNotMockedWhenCascading(@Mocked Foo foo) { foo.getBar(); assertEquals("notMocked", Bar.staticMethod()); try { new Bar(); fail(); } catch (RuntimeException ignored) {} } @Test public void verifyThatStaticMethodsAndConstructorsAreMockedWhenCascadedMockIsMockedNormally( @Mocked Foo mockFoo, @Mocked Bar mockBar) { assertSame(mockBar, mockFoo.getBar()); assertEquals(0, mockBar.doSomething()); assertNull(Bar.staticMethod()); new Bar(); } @Test public void useAvailableMockedInstanceOfSubclassAsCascadedInstance(@Mocked Foo foo, @Mocked SubBar bar) { Bar cascadedBar = foo.getBar(); assertSame(bar, cascadedBar); } @Test public void replaceCascadedInstanceWithFirstOneOfTwoInjectableInstances( @Mocked final Foo foo, @Injectable final Bar bar1, @Injectable Bar bar2) { new Expectations() {{ foo.getBar(); result = bar1; }}; Bar cascadedBar = foo.getBar(); assertSame(bar1, cascadedBar); assertEquals(0, bar1.doSomething()); assertEquals(0, bar2.doSomething()); } @Test public void cascadeOneLevelDuringRecord(@Mocked final Foo mockFoo) { final List<Integer> list = Arrays.asList(1, 2, 3); new Expectations() {{ mockFoo.doSomething(anyString); minTimes = 2; mockFoo.getBar().doSomething(); result = 2; Foo.globalBar().doSomething(); result = 3; mockFoo.getBooleanValue(); result = true; mockFoo.getIntValue(); result = -1; mockFoo.getList(); result = list; }}; Foo foo = new Foo(); foo.doSomething("1"); assertEquals(2, foo.getBar().doSomething()); foo.doSomething("2"); assertEquals(3, Foo.globalBar().doSomething()); assertTrue(foo.getBooleanValue()); assertEquals(-1, foo.getIntValue()); assertSame(list, foo.getList()); } @Test public void cascadeOneLevelDuringVerify(@Mocked final Foo foo) { Bar bar = foo.getBar(); bar.doSomething(); bar.doSomething(); Foo.globalBar().doSomething(); assertEquals(0, foo.getIntValue()); assertFalse(foo.getBooleanValue()); assertTrue(foo.getList().isEmpty()); new Verifications() {{ foo.getBar().doSomething(); minTimes = 2; Foo.globalBar().doSomething(); times = 1; }}; new VerificationsInOrder() {{ foo.getIntValue(); foo.getBooleanValue(); }}; } @Test public void cascadeTwoLevelsDuringReplay(@Mocked Foo foo) { foo.getBar().getBaz().runIt(); } @Test public void cascadeTwoLevelsDuringRecord(@Mocked final Foo mockFoo) { new Expectations() {{ mockFoo.getBar().doSomething(); result = 1; Foo.globalBar().doSomething(); result = 2; mockFoo.getBar().getBaz().runIt(); times = 2; }}; Foo foo = new Foo(); assertEquals(1, foo.getBar().doSomething()); assertEquals(2, Foo.globalBar().doSomething()); Baz baz = foo.getBar().getBaz(); baz.runIt(); baz.runIt(); } @Test public void cascadeOneLevelAndVerifyInvocationOnLastMockOnly(@Mocked Foo foo, @Injectable final Bar bar) { Bar fooBar = foo.getBar(); assertSame(bar, fooBar); fooBar.doSomething(); new Verifications() {{ bar.doSomething(); }}; } @Test public void cascadeTwoLevelsWithInvocationRecordedOnLastMockOnly(@Mocked Foo foo, @Mocked final Baz baz) { new Expectations() {{ baz.runIt(); times = 1; }}; Baz cascadedBaz = foo.getBar().getBaz(); cascadedBaz.runIt(); } @Test public void cascadeTwoLevelsAndVerifyInvocationOnLastMockOnly(@Mocked Foo foo, @Mocked final Baz baz) { Baz cascadedBaz = foo.getBar().getBaz(); assertSame(baz, cascadedBaz); cascadedBaz.runIt(); new Verifications() {{ baz.runIt(); }}; } // Tests using the java.lang.Process and java.lang.ProcessBuilder classes ////////////////////////////////////////// @Test public void cascadeOnJREClasses(@Mocked final ProcessBuilder pb) throws Exception { new Expectations() {{ ProcessBuilder sameBuilder = pb.directory((File) any); assertSame(sameBuilder, pb); Process process = sameBuilder.start(); process.getOutputStream().write(5); process.exitValue(); result = 1; }}; Process process = new ProcessBuilder("test").directory(new File("myDir")).start(); process.getOutputStream().write(5); process.getOutputStream().flush(); assertEquals(1, process.exitValue()); } @Test public void returnSameMockedInstanceThroughCascadingEvenWithMultipleCandidatesAvailable( @Injectable ProcessBuilder pb1, @Injectable ProcessBuilder pb2) { assertSame(pb1, pb1.command("a")); assertSame(pb2, pb2.command("b")); } @Test public void createOSProcessToCopyTempFiles(@Mocked final ProcessBuilder pb) throws Exception { // Code under test creates a new process to execute an OS-specific command. String cmdLine = "copy /Y *.txt D:\\TEMP"; File wrkDir = new File("C:\\TEMP"); Process copy = new ProcessBuilder().command(cmdLine).directory(wrkDir).start(); int exit = copy.waitFor(); if (exit != 0) { throw new RuntimeException("Process execution failed"); } // Verify the desired process was created with the correct command. new Verifications() {{ pb.command(withSubstring("copy")).start(); }}; } // Tests using java.net classes //////////////////////////////////////////////////////////////////////////////////// @Test public void recordAndVerifyExpectationsOnCascadedMocks( @Mocked Socket anySocket, @Mocked final SocketChannel cascadedChannel, @Mocked InetSocketAddress inetAddr) throws Exception { Socket sk = new Socket(); SocketChannel ch = sk.getChannel(); if (!ch.isConnected()) { SocketAddress sa = new InetSocketAddress("remoteHost", 123); ch.connect(sa); } InetAddress adr1 = sk.getInetAddress(); InetAddress adr2 = sk.getLocalAddress(); assertNotSame(adr1, adr2); new Verifications() {{ cascadedChannel.connect((SocketAddress) withNotNull()); }}; } static final class SocketFactory { public Socket createSocket() { return new Socket(); } public Socket createSocket(String host, int port) throws IOException { return new Socket(host, port); } } @Test public void mockDynamicallyAClassToBeLaterMockedThroughCascading() { new Expectations(Socket.class) {}; } @Test public void cascadeOneLevelWithArgumentMatchers(@Mocked final SocketFactory sf) throws Exception { new Expectations() {{ sf.createSocket(anyString, 80); result = null; }}; assertNull(sf.createSocket("expected", 80)); assertNotNull(sf.createSocket("unexpected", 8080)); } @Test public void recordAndVerifyOneLevelDeep(@Mocked final SocketFactory sf) throws Exception { final OutputStream out = new ByteArrayOutputStream(); new Expectations() {{ sf.createSocket().getOutputStream(); result = out; }}; assertSame(out, sf.createSocket().getOutputStream()); } @Test public void recordAndVerifyOnTwoCascadingMocksOfTheSameType( @Mocked final SocketFactory sf1, @Mocked final SocketFactory sf2) throws Exception { final OutputStream out1 = new ByteArrayOutputStream(); final OutputStream out2 = new ByteArrayOutputStream(); new Expectations() {{ sf1.createSocket().getOutputStream(); result = out1; sf2.createSocket().getOutputStream(); result = out2; }}; assertSame(out1, sf1.createSocket().getOutputStream()); assertSame(out2, sf2.createSocket().getOutputStream()); new FullVerificationsInOrder() {{ sf1.createSocket().getOutputStream(); sf2.createSocket().getOutputStream(); }}; } @Test public void recordAndVerifySameInvocationOnMocksReturnedFromInvocationsWithDifferentArguments( @Mocked final SocketFactory sf) throws Exception { new Expectations() {{ sf.createSocket().getPort(); result = 1; sf.createSocket("first", 80).getPort(); result = 2; sf.createSocket("second", 80).getPort(); result = 3; sf.createSocket(anyString, 81).getPort(); result = 4; }}; assertEquals(1, sf.createSocket().getPort()); assertEquals(2, sf.createSocket("first", 80).getPort()); assertEquals(3, sf.createSocket("second", 80).getPort()); assertEquals(4, sf.createSocket("third", 81).getPort()); new VerificationsInOrder() {{ sf.createSocket().getPort(); times = 1; sf.createSocket("first", 80).getPort(); sf.createSocket("second", 80).getPort(); sf.createSocket(anyString, 81).getPort(); maxTimes = 1; sf.createSocket("fourth", -1); times = 0; }}; } @Test public void cascadeOnInheritedMethod(@Mocked SocketChannel sc) { assertNotNull(sc.provider()); } @Test public void recordAndVerifyWithMixedCascadeLevels(@Mocked final SocketFactory sf) throws Exception { new Expectations() {{ sf.createSocket("first", 80).getKeepAlive(); result = true; sf.createSocket("second", anyInt).getChannel().close(); times = 1; }}; sf.createSocket("second", 80).getChannel().close(); assertTrue(sf.createSocket("first", 80).getKeepAlive()); sf.createSocket("first", 8080).getChannel().provider().openPipe(); new Verifications() {{ sf.createSocket("first", 8080).getChannel().provider().openPipe(); }}; } // Cascading other Java SE types /////////////////////////////////////////////////////////////////////////////////// static class SomeClass { Future<Foo> doSomething() { return null; } } @Test public void cascadeAFuture(@Mocked SomeClass mock) throws Exception { Future<Foo> f = mock.doSomething(); Foo foo = f.get(); assertNotNull(foo); } // Other tests ///////////////////////////////////////////////////////////////////////////////////////////////////// @Test public void recordStrictExpectationOnCascadedMock(@Mocked Foo foo, @Mocked final Bar mockBar) { new StrictExpectations() {{ mockBar.doSomething(); }}; Bar bar = foo.getBar(); bar.doSomething(); } @Test public void recordExpectationOnCascadedMock(@Mocked Foo foo, @Mocked final Bar mockBar) { new Expectations() {{ mockBar.doSomething(); times = 1; result = 123; }}; Bar bar = foo.getBar(); assertEquals(123, bar.doSomething()); } @Test public void overrideTwoCascadedMocksOfTheSameType( @Mocked final Foo foo1, @Mocked final Foo foo2, @Mocked final Bar mockBar1, @Mocked final Bar mockBar2) { new Expectations() {{ foo1.getBar(); result = mockBar1; foo2.getBar(); result = mockBar2; mockBar1.doSomething(); mockBar2.doSomething(); }}; Bar bar1 = foo1.getBar(); Bar bar2 = foo2.getBar(); bar1.doSomething(); bar2.doSomething(); } @Test(expected = UnexpectedInvocation.class) public void overrideTwoCascadedMocksOfTheSameTypeButReplayInDifferentOrder( @Mocked final Foo foo1, @Mocked final Foo foo2, @Injectable final Bar mockBar1, @Mocked final Bar mockBar2) { new StrictExpectations() {{ foo1.getBar(); result = mockBar1; foo2.getBar(); result = mockBar2; mockBar1.doSomething(); mockBar2.doSomething(); }}; Bar bar1 = foo1.getBar(); Bar bar2 = foo2.getBar(); bar2.doSomething(); bar1.doSomething(); } @Test public void cascadedEnum(@Mocked final Foo mock) { new Expectations() {{ mock.getBar().getEnum(); result = AnEnum.Second; }}; assertEquals(AnEnum.Second, mock.getBar().getEnum()); } @Test public void cascadedEnumReturningConsecutiveValuesThroughResultField(@Mocked final Foo mock) { new Expectations() {{ mock.getBar().getEnum(); result = AnEnum.First; result = AnEnum.Second; result = AnEnum.Third; }}; assertSame(AnEnum.First, mock.getBar().getEnum()); assertSame(AnEnum.Second, mock.getBar().getEnum()); assertSame(AnEnum.Third, mock.getBar().getEnum()); } @Test public void cascadedEnumReturningConsecutiveValuesThroughReturnsMethod(@Mocked final Foo mock) { new Expectations() {{ mock.getBar().getEnum(); returns(AnEnum.First, AnEnum.Second, AnEnum.Third); }}; assertSame(AnEnum.First, mock.getBar().getEnum()); assertSame(AnEnum.Second, mock.getBar().getEnum()); assertSame(AnEnum.Third, mock.getBar().getEnum()); } @Test public void cascadedStrictEnumReturningConsecutiveValuesThroughResultField(@Mocked final Foo mock) { new StrictExpectations() {{ mock.getBar().getEnum(); result = AnEnum.Third; result = AnEnum.Second; result = AnEnum.First; }}; Bar bar = mock.getBar(); assertSame(AnEnum.Third, bar.getEnum()); assertSame(AnEnum.Second, bar.getEnum()); assertSame(AnEnum.First, bar.getEnum()); } @Test public void cascadedStrictEnumReturningConsecutiveValuesThroughReturnsMethod(@Mocked final Foo mock) { new StrictExpectations() {{ mock.getBar().getEnum(); returns(AnEnum.First, AnEnum.Second, AnEnum.Third); }}; Bar bar = mock.getBar(); assertSame(AnEnum.First, bar.getEnum()); assertSame(AnEnum.Second, bar.getEnum()); assertSame(AnEnum.Third, bar.getEnum()); } @Test public void overrideLastCascadedObjectWithNonMockedInstance(@Mocked final Foo foo) { final Date newDate = new Date(123); assertEquals(123, newDate.getTime()); new Expectations() {{ foo.getBar().getBaz().getDate(); result = newDate; }}; assertSame(newDate, new Foo().getBar().getBaz().getDate()); assertEquals(123, newDate.getTime()); } @Test public void returnDeclaredMockedInstanceFromMultiLevelCascading(@Mocked Date mockedDate, @Mocked Foo foo) { Date newDate = new Date(123); assertEquals(0, newDate.getTime()); Date cascadedDate = new Foo().getBar().getBaz().getDate(); assertSame(mockedDate, cascadedDate); assertEquals(0, newDate.getTime()); assertEquals(0, mockedDate.getTime()); } @Test public void returnInjectableMockInstanceFromMultiLevelCascading(@Injectable Date mockDate, @Mocked Foo foo) { Date newDate = new Date(123); assertEquals(123, newDate.getTime()); Date cascadedDate = new Foo().getBar().getBaz().getDate(); assertSame(mockDate, cascadedDate); assertEquals(123, newDate.getTime()); assertEquals(0, mockDate.getTime()); } static class Factory { static Factory create() { return null; } } static class Client { OtherClient getOtherClient() { return null; } } static class OtherClient { static final Factory F = Factory.create(); } @Test public void cascadeDuringStaticInitializationOfCascadingClass(@Mocked Factory mock1, @Mocked Client mock2) { assertNotNull(mock2.getOtherClient()); assertNotNull(OtherClient.F); } public interface LevelZero { Runnable getFoo(); } public interface LevelOne extends LevelZero {} public interface LevelTwo extends LevelOne {} @Test public void createCascadedMockFromMethodDefinedTwoLevelsUpAnInterfaceHierarchy(@Mocked LevelTwo mock) { assertNotNull(mock.getFoo()); } public abstract class AbstractClass implements LevelZero {} @Test public void cascadeTypeReturnedFromInterfaceImplementedByAbstractClass(@Mocked AbstractClass mock) { Runnable foo = mock.getFoo(); assertNotNull(foo); } @Test public void produceDifferentCascadedInstancesOfSameInterfaceFromDifferentInvocations(@Mocked Bar bar) { Baz cascaded1 = bar.getBaz(1); Baz cascaded2 = bar.getBaz(2); Baz cascaded3 = bar.getBaz(1); assertSame(cascaded1, cascaded3); assertNotSame(cascaded1, cascaded2); } @Test public void cascadeFromJavaManagementAPI(@Mocked ManagementFactory mngmntFactory) { CompilationMXBean compilation = ManagementFactory.getCompilationMXBean(); assertNotNull(compilation); assertNull(compilation.getName()); } }