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
34
35 final class CapturingImplementationsTest {
36
37
38 private static final Logger logger = LoggerFactory.getLogger(CapturingImplementationsTest.class);
39
40
41
42
43 interface ServiceToBeStubbedOut {
44
45
46
47
48
49 int doSomething();
50 }
51
52
53
54 @Capturing
55 ServiceToBeStubbedOut unused;
56
57
58
59
60 static final class ServiceLocator {
61
62
63
64
65
66
67
68
69
70
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
81 return (S) service;
82 }
83 }
84
85
86
87
88 @Test
89 void captureImplementationLoadedByServiceLocator() {
90 ServiceToBeStubbedOut service = ServiceLocator.getInstance(ServiceToBeStubbedOut.class);
91 assertEquals(0, service.doSomething());
92 }
93
94
95
96
97 public interface Service1 {
98
99
100
101
102
103 int doSomething();
104 }
105
106
107
108
109 static final class Service1Impl implements Service1 {
110 @Override
111 public int doSomething() {
112 return 1;
113 }
114 }
115
116
117 @Capturing
118 Service1 mockService1;
119
120
121
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
140
141 public interface Service2 {
142
143
144
145
146
147 int doSomething();
148 }
149
150
151
152
153 static final class Service2Impl implements Service2 {
154 @Override
155 public int doSomething() {
156 return 1;
157 }
158 }
159
160
161
162
163
164
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
183
184 public abstract static class AbstractService {
185
186
187
188
189
190 protected abstract boolean doSomething();
191 }
192
193
194
195
196 static final class DefaultServiceImpl extends AbstractService {
197 @Override
198 protected boolean doSomething() {
199 return true;
200 }
201 }
202
203
204
205
206
207
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
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
227 return (Class<? extends Service2>) defineClass(name, bytecode, 0, bytecode.length);
228 }
229 }.findClass(Service2Impl.class.getName());
230
231
232 Service2 service2;
233
234
235
236
237
238
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
249
250
251
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
267
268 public interface Service3 {
269
270
271
272
273
274 int doSomething();
275 }
276
277
278 static Service3 proxyInstance;
279
280
281
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
297
298
299
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
315
316 interface Interface {
317
318
319
320 void op();
321 }
322
323
324
325
326 interface SubInterface extends Interface {
327 }
328
329
330
331
332 static class Implementation implements SubInterface {
333 @Override
334 public void op() {
335 throw new RuntimeException();
336 }
337 }
338
339
340
341
342
343
344
345 @Test
346 void captureClassImplementingSubInterfaceOfCapturedInterface(@Capturing Interface base) {
347 Interface impl = new Implementation();
348 impl.op();
349 }
350
351
352
353
354
355
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
367
368
369
370
371
372
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
391
392
393
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
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
411
412 interface Interface2 {
413
414
415
416
417
418 int doSomething();
419 }
420
421
422
423
424 interface SubInterface2 extends Interface2 {
425 }
426
427
428
429
430 static class ClassImplementingSubInterfaceAndExtendingUnrelatedBase extends Implementation
431 implements SubInterface2 {
432 @Override
433 public int doSomething() {
434 return 123;
435 }
436 }
437
438
439
440
441
442
443
444 @Test
445 void captureClassWhichImplementsCapturedBaseInterfaceAndExtendsUnrelatedBase(@Capturing Interface2 captured) {
446 int i = new ClassImplementingSubInterfaceAndExtendingUnrelatedBase().doSomething();
447
448 assertEquals(0, i);
449 }
450
451
452
453
454
455
456
457 static class Base<T> {
458
459
460
461
462
463
464 T doSomething() {
465 return null;
466 }
467
468
469
470
471
472
473
474 void doSomething(T t) {
475 logger.info("test");
476 }
477
478
479
480
481
482
483
484
485
486 T doSomethingReturn(T t) {
487 return t;
488 }
489 }
490
491
492
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
512
513
514
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
537
538 static class Base2 {
539
540
541
542 void base() {
543 }
544 }
545
546
547
548
549 static class Sub extends Base2 {
550 }
551
552
553
554
555 static class Sub2 extends Sub {
556 @Override
557 void base() {
558 throw new RuntimeException();
559 }
560 }
561
562
563
564
565
566
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
582
583 public interface BaseItf {
584
585
586
587 void base();
588 }
589
590
591
592
593 public interface SubItf extends BaseItf {
594 }
595
596
597
598
599
600
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
620
621
622
623
624 static final class MyActionListener implements ActionListener {
625 @Override
626 public void processAction(ActionEvent event) {
627 }
628
629
630
631
632
633
634 boolean doSomething() {
635 return true;
636 }
637 }
638
639
640
641
642
643
644
645 @Test
646 void captureUserDefinedClassImplementingExternalAPI(@Capturing ActionListener actionListener) {
647 boolean notCaptured = new MyActionListener().doSomething();
648 assertFalse(notCaptured);
649 }
650
651
652
653
654
655
656
657 @Test
658 void captureLibraryClassImplementingInterfaceFromAnotherLibrary(@Capturing final ServletContextListener mock) {
659
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
672
673 static class BaseGenericReturnTypes {
674
675
676
677
678
679
680 Class<?> methodOne() {
681 return null;
682 }
683
684
685
686
687
688
689 Class<?> methodTwo() {
690 return null;
691 }
692 }
693
694
695
696
697 static class SubGenericReturnTypes extends BaseGenericReturnTypes {
698 }
699
700
701
702
703
704
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
726
727 static class BaseR {
728
729
730
731
732 void foo() {
733 }
734
735
736
737
738 void bar() {
739 }
740 }
741
742
743
744
745 static class SubR extends BaseR {
746 }
747
748
749
750
751
752
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 }