1
2
3
4
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
41
42 @ExtendWith(JMockitExtension.class)
43 class CapturingImplementationsTest {
44
45
46 private static final Logger logger = LoggerFactory.getLogger(CapturingImplementationsTest.class);
47
48
49
50
51 interface ServiceToBeStubbedOut {
52
53
54
55
56
57 int doSomething();
58 }
59
60
61 @Capturing
62 ServiceToBeStubbedOut unused;
63
64
65
66
67 static final class ServiceLocator {
68
69
70
71
72
73
74
75
76
77
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
88 return (S) service;
89 }
90 }
91
92
93
94
95 @Test
96 void captureImplementationLoadedByServiceLocator() {
97 ServiceToBeStubbedOut service = ServiceLocator.getInstance(ServiceToBeStubbedOut.class);
98 assertEquals(0, service.doSomething());
99 }
100
101
102
103
104 public interface Service1 {
105
106
107
108
109
110 int doSomething();
111 }
112
113
114
115
116 static final class Service1Impl implements Service1 {
117 @Override
118 public int doSomething() {
119 return 1;
120 }
121 }
122
123
124 @Capturing
125 Service1 mockService1;
126
127
128
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
147
148 public interface Service2 {
149
150
151
152
153
154 int doSomething();
155 }
156
157
158
159
160 static final class Service2Impl implements Service2 {
161 @Override
162 public int doSomething() {
163 return 1;
164 }
165 }
166
167
168
169
170
171
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
190
191 public abstract static class AbstractService {
192
193
194
195
196
197 protected abstract boolean doSomething();
198 }
199
200
201
202
203 static final class DefaultServiceImpl extends AbstractService {
204 @Override
205 protected boolean doSomething() {
206 return true;
207 }
208 }
209
210
211
212
213
214
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
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
234 return (Class<? extends Service2>) defineClass(name, bytecode, 0, bytecode.length);
235 }
236 }.findClass(Service2Impl.class.getName());
237
238
239 Service2 service2;
240
241
242
243
244
245
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
256
257
258
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
274
275 public interface Service3 {
276
277
278
279
280
281 int doSomething();
282 }
283
284
285 static Service3 proxyInstance;
286
287
288
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
304
305
306
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
322
323 interface Interface {
324
325
326
327 void op();
328 }
329
330
331
332
333 interface SubInterface extends Interface {
334 }
335
336
337
338
339 static class Implementation implements SubInterface {
340 @Override
341 public void op() {
342 throw new RuntimeException();
343 }
344 }
345
346
347
348
349
350
351
352 @Test
353 void captureClassImplementingSubInterfaceOfCapturedInterface(@Capturing Interface base) {
354 Interface impl = new Implementation();
355 impl.op();
356 }
357
358
359
360
361
362
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
374
375
376
377
378
379
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
398
399
400
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
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
418
419 interface Interface2 {
420
421
422
423
424
425 int doSomething();
426 }
427
428
429
430
431 interface SubInterface2 extends Interface2 {
432 }
433
434
435
436
437 static class ClassImplementingSubInterfaceAndExtendingUnrelatedBase extends Implementation
438 implements SubInterface2 {
439 @Override
440 public int doSomething() {
441 return 123;
442 }
443 }
444
445
446
447
448
449
450
451 @Test
452 void captureClassWhichImplementsCapturedBaseInterfaceAndExtendsUnrelatedBase(@Capturing Interface2 captured) {
453 int i = new ClassImplementingSubInterfaceAndExtendingUnrelatedBase().doSomething();
454
455 assertEquals(0, i);
456 }
457
458
459
460
461
462
463
464 static class Base<T> {
465
466
467
468
469
470
471 T doSomething() {
472 return null;
473 }
474
475
476
477
478
479
480
481 void doSomething(T t) {
482 logger.info("test");
483 }
484
485
486
487
488
489
490
491
492
493 T doSomethingReturn(T t) {
494 return t;
495 }
496 }
497
498
499
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
519
520
521
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
544
545 static class Base2 {
546
547
548
549 void base() {
550 }
551 }
552
553
554
555
556 static class Sub extends Base2 {
557 }
558
559
560
561
562 static class Sub2 extends Sub {
563 @Override
564 void base() {
565 throw new RuntimeException();
566 }
567 }
568
569
570
571
572
573
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
589
590 public interface BaseItf {
591
592
593
594 void base();
595 }
596
597
598
599
600 public interface SubItf extends BaseItf {
601 }
602
603
604
605
606
607
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
627
628
629
630
631 static final class MyActionListener implements ActionListener {
632 @Override
633 public void processAction(ActionEvent event) {
634 }
635
636
637
638
639
640
641 boolean doSomething() {
642 return true;
643 }
644 }
645
646
647
648
649
650
651
652 @Test
653 void captureUserDefinedClassImplementingExternalAPI(@Capturing ActionListener actionListener) {
654 boolean notCaptured = new MyActionListener().doSomething();
655 assertFalse(notCaptured);
656 }
657
658
659
660
661
662
663
664 @Test
665 void captureLibraryClassImplementingInterfaceFromAnotherLibrary(@Capturing final ServletContextListener mock) {
666
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
679
680 static class BaseGenericReturnTypes {
681
682
683
684
685
686
687 Class<?> methodOne() {
688 return null;
689 }
690
691
692
693
694
695
696 Class<?> methodTwo() {
697 return null;
698 }
699 }
700
701
702
703
704 static class SubGenericReturnTypes extends BaseGenericReturnTypes {
705 }
706
707
708
709
710
711
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
733
734 static class BaseR {
735
736
737
738
739 void foo() {
740 }
741
742
743
744
745 void bar() {
746 }
747 }
748
749
750
751
752 static class SubR extends BaseR {
753 }
754
755
756
757
758
759
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 }