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