001package com.avaje.ebean; 002 003 004import java.util.concurrent.Callable; 005 006/** 007 * This is a test helper class that can be used to swap out the default EbeanServer in the Ebean 008 * singleton with a mock implementation. 009 * <p> 010 * This enables a developer to write a test using a tool like Mockito to mock the EbeanServer 011 * interface and make this the default server of the Ebean singleton. 012 * <p> 013 * <p> 014 * 015 * </p> 016 * <pre>{@code 017 * 018 * EbeanServer mock = ...; // create a mock or test double etc 019 * 020 * MockiEbean.runWithMock(mock, new Runnable() { 021 * 022 * public void run() { 023 * ... 024 * // test code in here runs with mock EbeanServer 025 * } 026 * }); 027 * 028 * }</pre> 029 * <p> 030 * An example using Mockito to mock the getBeanId() method on EbeanServer. 031 * <p> 032 * <pre>{@code 033 * 034 * @Test 035 * public void testWithMockito() { 036 * 037 * EbeanServer defaultServer = Ebean.getServer(null); 038 * assertTrue("is a real EbeanServer", defaultServer instanceof DefaultServer); 039 * 040 * Long magicBeanId = Long.valueOf(47L); 041 * 042 * EbeanServer mock = Mockito.mock(EbeanServer.class); 043 * when(mock.getBeanId(null)).thenReturn(magicBeanId); 044 * 045 * MockiEbean mockiEbean = MockiEbean.start(mock); 046 * try { 047 * 048 * // So using the Ebean singleton returns the mock instance 049 * EbeanServer server = Ebean.getServer(null); 050 * Object beanId = server.getBeanId(null); 051 * 052 * assertEquals(magicBeanId, beanId); 053 * 054 * } finally { 055 * mockiEbean.restoreOriginal(); 056 * } 057 * 058 * EbeanServer restoredServer = Ebean.getServer(null); 059 * assertTrue("is a real EbeanServer", restoredServer instanceof DefaultServer); 060 * } 061 * 062 * }</pre> 063 */ 064public class MockiEbean { 065 066 /** 067 * Set a mock implementation of EbeanServer as the default server. 068 * <p> 069 * Typically the mock instance passed in is created by Mockito or similar tool. 070 * <p> 071 * The default EbeanSever is the instance returned by {@link Ebean#getServer(String)} when the 072 * server name is null. 073 * 074 * @param mock the mock instance that becomes the default EbeanServer 075 * @return The MockiEbean with a {@link #restoreOriginal()} method that can be used to restore the 076 * original EbeanServer implementation. 077 */ 078 public static MockiEbean start(EbeanServer mock) { 079 080 // using $mock as the server name 081 EbeanServer original = Ebean.mock("$mock", mock, true); 082 083 if (mock instanceof DelegateAwareEbeanServer) { 084 ((DelegateAwareEbeanServer)mock).withDelegateIfRequired(original); 085 } 086 087 return new MockiEbean(mock, original); 088 } 089 090 /** 091 * Run the test runnable using the mock EbeanServer and restoring the original EbeanServer afterward. 092 * 093 * @param mock the mock instance that becomes the default EbeanServer 094 * @param test typically some test code as a runnable 095 */ 096 public static void runWithMock(EbeanServer mock, Runnable test) { 097 098 start(mock).run(test); 099 } 100 101 /** 102 * Run the test runnable using the mock EbeanServer and restoring the original EbeanServer afterward. 103 * 104 * @param mock the mock instance that becomes the default EbeanServer 105 * @param test typically some test code as a callable 106 */ 107 public static <V> V runWithMock(EbeanServer mock, Callable<V> test) throws Exception { 108 109 return start(mock).run(test); 110 } 111 112 /** 113 * The 'original' default EbeanServer that the mock is temporarily replacing. 114 */ 115 protected final EbeanServer original; 116 117 /** 118 * The 'mock' EbeanServer that is temporarily replacing the 'original' during the 'run'. 119 */ 120 protected final EbeanServer mock; 121 122 /** 123 * Construct with the mock and original EbeanServer instances.s 124 */ 125 protected MockiEbean(EbeanServer mock, EbeanServer original) { 126 this.mock = mock; 127 this.original = original; 128 } 129 130 /** 131 * Return the original EbeanServer implementation. 132 * <p> 133 * This is the implementation that is put back as the default EbeanServer when 134 * {@link #restoreOriginal()} is called. 135 */ 136 public EbeanServer getOriginal() { 137 return original; 138 } 139 140 /** 141 * Return the mock EbeanServer instance that was set as the default EbeanServer. 142 */ 143 public EbeanServer getMock() { 144 return mock; 145 } 146 147 /** 148 * Run the test runnable restoring the original EbeanServer afterwards. 149 */ 150 public void run(Runnable testRunnable) { 151 try { 152 beforeRun(); 153 testRunnable.run(); 154 } finally { 155 afterRun(); 156 restoreOriginal(); 157 } 158 } 159 160 161 /** 162 * Run the test callable restoring the original EbeanServer afterwards. 163 */ 164 public <V> V run(Callable<V> testCallable) throws Exception { 165 try { 166 beforeRun(); 167 return testCallable.call(); 168 } finally { 169 afterRun(); 170 restoreOriginal(); 171 } 172 } 173 174 175 /** 176 * Typically only used internally. 177 * 178 * Usually used to set static Finder test doubles. 179 */ 180 public void beforeRun() { 181 if (mock instanceof DelegateAwareEbeanServer) { 182 ((DelegateAwareEbeanServer)mock).beforeRun(); 183 } 184 } 185 186 /** 187 * Typically only used internally. 188 * 189 * Usually used to restore static Finder original implementations. 190 */ 191 public void afterRun() { 192 if (mock instanceof DelegateAwareEbeanServer) { 193 ((DelegateAwareEbeanServer)mock).afterRun(); 194 } 195 } 196 197 /** 198 * Restore the original EbeanServer implementation as the default EbeanServer. 199 */ 200 public void restoreOriginal() { 201 if (original == null) { 202 throw new IllegalStateException("Original EbeanServer instance is null"); 203 } 204 if (original.getName() == null) { 205 throw new IllegalStateException("Original EbeanServer name is null"); 206 } 207 208 // restore the original EbeanServer back 209 Ebean.mock(original.getName(), original, true); 210 } 211 212}