View Javadoc
1   package mockit;
2   
3   import static org.junit.jupiter.api.Assertions.assertEquals;
4   import static org.junit.jupiter.api.Assertions.assertFalse;
5   import static org.junit.jupiter.api.Assertions.assertTrue;
6   import static org.junit.jupiter.api.Assertions.fail;
7   
8   import jakarta.faces.event.ActionEvent;
9   import jakarta.faces.event.ActionListener;
10  import jakarta.servlet.ServletContextListener;
11  
12  import java.lang.management.ManagementFactory;
13  import java.lang.management.ThreadMXBean;
14  import java.lang.reflect.Constructor;
15  import java.lang.reflect.InvocationHandler;
16  import java.lang.reflect.Proxy;
17  import java.util.concurrent.Callable;
18  import java.util.concurrent.ExecutorService;
19  import java.util.concurrent.Executors;
20  
21  import javax.xml.parsers.SAXParser;
22  import javax.xml.parsers.SAXParserFactory;
23  
24  import mockit.internal.ClassFile;
25  
26  import org.junit.jupiter.api.BeforeAll;
27  import org.junit.jupiter.api.BeforeEach;
28  import org.junit.jupiter.api.Test;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  /**
33   * The Class CapturingImplementationsTest.
34   */
35  final class CapturingImplementationsTest {
36  
37      /** The logger. */
38      private static final Logger logger = LoggerFactory.getLogger(CapturingImplementationsTest.class);
39  
40      /**
41       * The Interface ServiceToBeStubbedOut.
42       */
43      interface ServiceToBeStubbedOut {
44          /**
45           * Do something.
46           *
47           * @return the int
48           */
49          int doSomething();
50      }
51  
52      /** The unused. */
53      // Just to cause any implementing classes to be stubbed out.
54      @Capturing
55      ServiceToBeStubbedOut unused;
56  
57      /**
58       * The Class ServiceLocator.
59       */
60      static final class ServiceLocator {
61  
62          /**
63           * Gets the single instance of ServiceLocator.
64           *
65           * @param <S>
66           *            the generic type
67           * @param serviceInterface
68           *            the service interface
69           *
70           * @return single instance of ServiceLocator
71           */
72          @SuppressWarnings("unused")
73          static <S> S getInstance(Class<S> serviceInterface) {
74              ServiceToBeStubbedOut service = new ServiceToBeStubbedOut() {
75                  @Override
76                  public int doSomething() {
77                      return 10;
78                  }
79              };
80              // noinspection unchecked
81              return (S) service;
82          }
83      }
84  
85      /**
86       * Capture implementation loaded by service locator.
87       */
88      @Test
89      void captureImplementationLoadedByServiceLocator() {
90          ServiceToBeStubbedOut service = ServiceLocator.getInstance(ServiceToBeStubbedOut.class);
91          assertEquals(0, service.doSomething());
92      }
93  
94      /**
95       * The Interface Service1.
96       */
97      public interface Service1 {
98          /**
99           * Do something.
100          *
101          * @return the int
102          */
103         int doSomething();
104     }
105 
106     /**
107      * The Class Service1Impl.
108      */
109     static final class Service1Impl implements Service1 {
110         @Override
111         public int doSomething() {
112             return 1;
113         }
114     }
115 
116     /** The mock service 1. */
117     @Capturing
118     Service1 mockService1;
119 
120     /**
121      * Capture implementation using mock field.
122      */
123     @Test
124     void captureImplementationUsingMockField() {
125         Service1 service = new Service1Impl();
126 
127         new Expectations() {
128             {
129                 mockService1.doSomething();
130                 returns(2, 3);
131             }
132         };
133 
134         assertEquals(2, service.doSomething());
135         assertEquals(3, new Service1Impl().doSomething());
136     }
137 
138     /**
139      * The Interface Service2.
140      */
141     public interface Service2 {
142         /**
143          * Do something.
144          *
145          * @return the int
146          */
147         int doSomething();
148     }
149 
150     /**
151      * The Class Service2Impl.
152      */
153     static final class Service2Impl implements Service2 {
154         @Override
155         public int doSomething() {
156             return 1;
157         }
158     }
159 
160     /**
161      * Capture implementation using mock parameter.
162      *
163      * @param mock
164      *            the mock
165      */
166     @Test
167     void captureImplementationUsingMockParameter(@Capturing final Service2 mock) {
168         Service2Impl service = new Service2Impl();
169 
170         new Expectations() {
171             {
172                 mock.doSomething();
173                 returns(3, 2);
174             }
175         };
176 
177         assertEquals(3, service.doSomething());
178         assertEquals(2, new Service2Impl().doSomething());
179     }
180 
181     /**
182      * The Class AbstractService.
183      */
184     public abstract static class AbstractService {
185         /**
186          * Do something.
187          *
188          * @return true, if successful
189          */
190         protected abstract boolean doSomething();
191     }
192 
193     /**
194      * The Class DefaultServiceImpl.
195      */
196     static final class DefaultServiceImpl extends AbstractService {
197         @Override
198         protected boolean doSomething() {
199             return true;
200         }
201     }
202 
203     /**
204      * Capture implementation of abstract class.
205      *
206      * @param mock
207      *            the mock
208      */
209     @Test
210     void captureImplementationOfAbstractClass(@Capturing AbstractService mock) {
211         assertFalse(new DefaultServiceImpl().doSomething());
212 
213         assertFalse(new AbstractService() {
214             @Override
215             protected boolean doSomething() {
216                 throw new RuntimeException();
217             }
218         }.doSomething());
219     }
220 
221     /** The Constant customLoadedClass. */
222     static final Class<? extends Service2> customLoadedClass = new ClassLoader() {
223         @Override
224         protected Class<? extends Service2> findClass(String name) {
225             byte[] bytecode = ClassFile.readBytesFromClassFile(name.replace('.', '/'));
226             // noinspection unchecked
227             return (Class<? extends Service2>) defineClass(name, bytecode, 0, bytecode.length);
228         }
229     }.findClass(Service2Impl.class.getName());
230 
231     /** The service 2. */
232     Service2 service2;
233 
234     /**
235      * Instantiate custom loaded class.
236      *
237      * @throws Exception
238      *             the exception
239      */
240     @BeforeEach
241     void instantiateCustomLoadedClass() throws Exception {
242         Constructor<?> defaultConstructor = customLoadedClass.getDeclaredConstructors()[0];
243         defaultConstructor.setAccessible(true);
244         service2 = (Service2) defaultConstructor.newInstance();
245     }
246 
247     /**
248      * Capture class previously loaded by class loader other than context.
249      *
250      * @param mock
251      *            the mock
252      */
253     @Test
254     void captureClassPreviouslyLoadedByClassLoaderOtherThanContext(@Capturing final Service2 mock) {
255         new Expectations() {
256             {
257                 mock.doSomething();
258                 result = 15;
259             }
260         };
261 
262         assertEquals(15, service2.doSomething());
263     }
264 
265     /**
266      * The Interface Service3.
267      */
268     public interface Service3 {
269         /**
270          * Do something.
271          *
272          * @return the int
273          */
274         int doSomething();
275     }
276 
277     /** The proxy instance. */
278     static Service3 proxyInstance;
279 
280     /**
281      * Generate dynamic proxy class.
282      */
283     @BeforeAll
284     static void generateDynamicProxyClass() {
285         ClassLoader loader = Service3.class.getClassLoader();
286         Class<?>[] interfaces = { Service3.class };
287         InvocationHandler invocationHandler = (proxy, method, args) -> {
288             fail("Should be mocked out");
289             return null;
290         };
291 
292         proxyInstance = (Service3) Proxy.newProxyInstance(loader, interfaces, invocationHandler);
293     }
294 
295     /**
296      * Capture dynamically generated proxy class.
297      *
298      * @param mock
299      *            the mock
300      */
301     @Test
302     void captureDynamicallyGeneratedProxyClass(@Capturing final Service3 mock) {
303         new Expectations() {
304             {
305                 mock.doSomething();
306                 result = 123;
307             }
308         };
309 
310         assertEquals(123, proxyInstance.doSomething());
311     }
312 
313     /**
314      * The Interface Interface.
315      */
316     interface Interface {
317         /**
318          * Op.
319          */
320         void op();
321     }
322 
323     /**
324      * The Interface SubInterface.
325      */
326     interface SubInterface extends Interface {
327     }
328 
329     /**
330      * The Class Implementation.
331      */
332     static class Implementation implements SubInterface {
333         @Override
334         public void op() {
335             throw new RuntimeException();
336         }
337     }
338 
339     /**
340      * Capture class implementing sub interface of captured interface.
341      *
342      * @param base
343      *            the base
344      */
345     @Test
346     void captureClassImplementingSubInterfaceOfCapturedInterface(@Capturing Interface base) {
347         Interface impl = new Implementation();
348         impl.op();
349     }
350 
351     /**
352      * Capture classes from the java management API.
353      *
354      * @param anyThreadMXBean
355      *            the any thread MX bean
356      */
357     @Test
358     void captureClassesFromTheJavaManagementAPI(@Capturing ThreadMXBean anyThreadMXBean) {
359         ThreadMXBean threadingBean = ManagementFactory.getThreadMXBean();
360         int threadCount = threadingBean.getThreadCount();
361 
362         assertEquals(0, threadCount);
363     }
364 
365     /**
366      * Capture classes from the SAX parser API.
367      *
368      * @param anyParser
369      *            the any parser
370      *
371      * @throws Exception
372      *             the exception
373      */
374     @Test
375     void captureClassesFromTheSAXParserAPI(@Capturing final SAXParser anyParser) throws Exception {
376         new Expectations() {
377             {
378                 anyParser.isNamespaceAware();
379                 result = true;
380             }
381         };
382 
383         SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
384         boolean b = parser.isNamespaceAware();
385 
386         assertTrue(b);
387     }
388 
389     /**
390      * Capture classes from the java concurrency API.
391      *
392      * @param anyExecutorService
393      *            the any executor service
394      */
395     @Test
396     void captureClassesFromTheJavaConcurrencyAPI(@Capturing ExecutorService anyExecutorService) {
397         ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
398         ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(2);
399         ExecutorService cachedThreadPoolExecutor = Executors.newCachedThreadPool();
400         ExecutorService scheduledThreadPoolExecutor = Executors.newScheduledThreadPool(3);
401 
402         // These calls would throw a NPE unless mocked.
403         singleThreadExecutor.submit((Runnable) null);
404         threadPoolExecutor.submit((Runnable) null);
405         cachedThreadPoolExecutor.submit((Runnable) null);
406         scheduledThreadPoolExecutor.submit((Callable<Object>) null);
407     }
408 
409     /**
410      * The Interface Interface2.
411      */
412     interface Interface2 {
413         /**
414          * Do something.
415          *
416          * @return the int
417          */
418         int doSomething();
419     }
420 
421     /**
422      * The Interface SubInterface2.
423      */
424     interface SubInterface2 extends Interface2 {
425     }
426 
427     /**
428      * The Class ClassImplementingSubInterfaceAndExtendingUnrelatedBase.
429      */
430     static class ClassImplementingSubInterfaceAndExtendingUnrelatedBase extends Implementation
431             implements SubInterface2 {
432         @Override
433         public int doSomething() {
434             return 123;
435         }
436     }
437 
438     /**
439      * Capture class which implements captured base interface and extends unrelated base.
440      *
441      * @param captured
442      *            the captured
443      */
444     @Test
445     void captureClassWhichImplementsCapturedBaseInterfaceAndExtendsUnrelatedBase(@Capturing Interface2 captured) {
446         int i = new ClassImplementingSubInterfaceAndExtendingUnrelatedBase().doSomething();
447 
448         assertEquals(0, i);
449     }
450 
451     /**
452      * The Class Base.
453      *
454      * @param <T>
455      *            the generic type
456      */
457     static class Base<T> {
458 
459         /**
460          * Do something.
461          *
462          * @return the t
463          */
464         T doSomething() {
465             return null;
466         }
467 
468         /**
469          * Do something.
470          *
471          * @param t
472          *            the t
473          */
474         void doSomething(T t) {
475             logger.info("test");
476         }
477 
478         /**
479          * Do something return.
480          *
481          * @param t
482          *            the t
483          *
484          * @return the t
485          */
486         T doSomethingReturn(T t) {
487             return t;
488         }
489     }
490 
491     /**
492      * The Class Impl.
493      */
494     static final class Impl extends Base<Integer> {
495         @Override
496         Integer doSomething() {
497             return 1;
498         }
499 
500         @Override
501         void doSomething(Integer i) {
502         }
503 
504         @Override
505         Integer doSomethingReturn(Integer t) {
506             return null;
507         }
508     }
509 
510     /**
511      * Capture implementations of generic type.
512      *
513      * @param anyInstance
514      *            the any instance
515      */
516     @Test
517     void captureImplementationsOfGenericType(@Capturing final Base<Integer> anyInstance) {
518         new Expectations() {
519             {
520                 anyInstance.doSomething();
521                 result = 2;
522                 anyInstance.doSomethingReturn(0);
523                 anyInstance.doSomething(0);
524             }
525         };
526 
527         Base<Integer> impl = new Impl();
528         int i = impl.doSomething();
529         impl.doSomethingReturn(0);
530         impl.doSomething(0);
531 
532         assertEquals(2, i);
533     }
534 
535     /**
536      * The Class Base2.
537      */
538     static class Base2 {
539         /**
540          * Base.
541          */
542         void base() {
543         }
544     }
545 
546     /**
547      * The Class Sub.
548      */
549     static class Sub extends Base2 {
550     }
551 
552     /**
553      * The Class Sub2.
554      */
555     static class Sub2 extends Sub {
556         @Override
557         void base() {
558             throw new RuntimeException();
559         }
560     }
561 
562     /**
563      * Verify invocation to method from base class on captured subclass of intermediate subclass.
564      *
565      * @param sub
566      *            the sub
567      */
568     @Test
569     void verifyInvocationToMethodFromBaseClassOnCapturedSubclassOfIntermediateSubclass(@Capturing final Sub sub) {
570         Sub impl = new Sub2();
571         impl.base();
572 
573         new Verifications() {
574             {
575                 sub.base();
576             }
577         };
578     }
579 
580     /**
581      * The Interface BaseItf.
582      */
583     public interface BaseItf {
584         /**
585          * Base.
586          */
587         void base();
588     }
589 
590     /**
591      * The Interface SubItf.
592      */
593     public interface SubItf extends BaseItf {
594     }
595 
596     /**
597      * Verify invocation to base interface method on captured implementation of sub interface.
598      *
599      * @param sub
600      *            the sub
601      */
602     @Test
603     void verifyInvocationToBaseInterfaceMethodOnCapturedImplementationOfSubInterface(@Capturing final SubItf sub) {
604         SubItf impl = new SubItf() {
605             @Override
606             public void base() {
607             }
608         };
609         impl.base();
610 
611         new Verifications() {
612             {
613                 sub.base();
614             }
615         };
616     }
617 
618     /**
619      * The listener interface for receiving myAction events. The class that is interested in processing a myAction event
620      * implements this interface, and the object created with that class is registered with a component using the
621      * component's <code>addMyActionListener<code> method. When the myAction event occurs, that object's appropriate
622      * method is invoked.
623      */
624     static final class MyActionListener implements ActionListener {
625         @Override
626         public void processAction(ActionEvent event) {
627         }
628 
629         /**
630          * Do something.
631          *
632          * @return true, if successful
633          */
634         boolean doSomething() {
635             return true;
636         }
637     }
638 
639     /**
640      * Capture user defined class implementing external API.
641      *
642      * @param actionListener
643      *            the action listener
644      */
645     @Test
646     void captureUserDefinedClassImplementingExternalAPI(@Capturing ActionListener actionListener) {
647         boolean notCaptured = new MyActionListener().doSomething();
648         assertFalse(notCaptured);
649     }
650 
651     /**
652      * Capture library class implementing interface from another library.
653      *
654      * @param mock
655      *            the mock
656      */
657     @Test
658     void captureLibraryClassImplementingInterfaceFromAnotherLibrary(@Capturing final ServletContextListener mock) {
659         // noinspection UnnecessaryFullyQualifiedName
660         ServletContextListener contextListener = new org.springframework.web.util.WebAppRootListener();
661         contextListener.contextInitialized(null);
662 
663         new Verifications() {
664             {
665                 mock.contextInitialized(null);
666             }
667         };
668     }
669 
670     /**
671      * The Class BaseGenericReturnTypes.
672      */
673     static class BaseGenericReturnTypes {
674 
675         /**
676          * Method one.
677          *
678          * @return the class
679          */
680         Class<?> methodOne() {
681             return null;
682         }
683 
684         /**
685          * Method two.
686          *
687          * @return the class
688          */
689         Class<?> methodTwo() {
690             return null;
691         }
692     }
693 
694     /**
695      * The Class SubGenericReturnTypes.
696      */
697     static class SubGenericReturnTypes extends BaseGenericReturnTypes {
698     }
699 
700     /**
701      * Capture method with generic return types.
702      *
703      * @param mock
704      *            the mock
705      */
706     @Test
707     void captureMethodWithGenericReturnTypes(@Capturing final BaseGenericReturnTypes mock) {
708         new Expectations() {
709             {
710                 mock.methodOne();
711                 result = BaseGenericReturnTypes.class;
712                 times = 1;
713 
714                 mock.methodTwo();
715                 result = SubGenericReturnTypes.class;
716                 times = 1;
717             }
718         };
719         SubGenericReturnTypes subBaseGenericReturnTypes = new SubGenericReturnTypes();
720         assertEquals(BaseGenericReturnTypes.class, subBaseGenericReturnTypes.methodOne());
721         assertEquals(SubGenericReturnTypes.class, subBaseGenericReturnTypes.methodTwo());
722     }
723 
724     /**
725      * The Class BaseR.
726      */
727     static class BaseR {
728 
729         /**
730          * Foo.
731          */
732         void foo() {
733         }
734 
735         /**
736          * Bar.
737          */
738         void bar() {
739         }
740     }
741 
742     /**
743      * The Class SubR.
744      */
745     static class SubR extends BaseR {
746     }
747 
748     /**
749      * Capture R.
750      *
751      * @param mock
752      *            the mock
753      */
754     @Test
755     void captureR(@Capturing final BaseR mock) {
756         new Expectations() {
757             {
758                 mock.foo();
759                 times = 1;
760 
761                 mock.bar();
762                 times = 1;
763             }
764         };
765         SubR subR = new SubR();
766         subR.foo();
767         subR.bar();
768     }
769 
770 }