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.internal.expectations.mocking;
7   
8   import static mockit.internal.util.AutoBoxing.isWrapperOfPrimitiveType;
9   import static mockit.internal.util.GeneratedClasses.getMockedClass;
10  import static mockit.internal.util.GeneratedClasses.isGeneratedImplementationClass;
11  
12  import edu.umd.cs.findbugs.annotations.NonNull;
13  
14  import java.util.ArrayList;
15  import java.util.HashMap;
16  import java.util.List;
17  import java.util.Map;
18  
19  import mockit.internal.expectations.MockingFilters;
20  import mockit.internal.state.TestRun;
21  
22  public final class PartialMocking extends BaseTypeRedefinition {
23      @NonNull
24      public final List<Object> targetInstances;
25      @NonNull
26      private final Map<Class<?>, byte[]> modifiedClassfiles;
27  
28      public PartialMocking() {
29          targetInstances = new ArrayList<>(2);
30          modifiedClassfiles = new HashMap<>();
31      }
32  
33      public void redefineTypes(@NonNull Object[] instancesToBePartiallyMocked) {
34          for (Object instance : instancesToBePartiallyMocked) {
35              redefineClassHierarchy(instance);
36          }
37  
38          if (!modifiedClassfiles.isEmpty()) {
39              TestRun.mockFixture().redefineMethods(modifiedClassfiles);
40              modifiedClassfiles.clear();
41          }
42      }
43  
44      private void redefineClassHierarchy(@NonNull Object mockInstance) {
45          if (mockInstance instanceof Class) {
46              targetClass = (Class<?>) mockInstance;
47              applyPartialMockingToGivenClass();
48          } else {
49              targetClass = getMockedClass(mockInstance);
50              applyPartialMockingToGivenInstance(mockInstance);
51  
52              InstanceFactory instanceFactory = createInstanceFactory(targetClass);
53              instanceFactory.lastInstance = mockInstance;
54  
55              TestRun.mockFixture().registerInstanceFactoryForMockedType(targetClass, instanceFactory);
56              TestRun.getExecutingTest().getCascadingTypes().add(false, targetClass);
57          }
58      }
59  
60      private void applyPartialMockingToGivenClass() {
61          validateTargetClassType();
62          redefineMethodsAndConstructorsInTargetType();
63          TestRun.mockFixture().registerMockedClass(targetClass);
64          TestRun.getExecutingTest().getCascadingTypes().add(false, targetClass);
65      }
66  
67      private void applyPartialMockingToGivenInstance(@NonNull Object instance) {
68          validateTargetClassType();
69          redefineMethodsAndConstructorsInTargetType();
70          targetInstances.add(instance);
71      }
72  
73      private void validateTargetClassType() {
74          if (targetClass.isInterface() || targetClass.isAnnotation() || targetClass.isArray()
75                  || targetClass.isPrimitive() || targetClass.isSynthetic()
76                  || MockingFilters.isSubclassOfUnmockable(targetClass) || isWrapperOfPrimitiveType(targetClass)
77                  || isGeneratedImplementationClass(targetClass)) {
78              throw new IllegalArgumentException("Invalid type for partial mocking: " + targetClass);
79          }
80      }
81  
82      @Override
83      void configureClassModifier(@NonNull MockedClassModifier modifier) {
84          modifier.useDynamicMocking();
85      }
86  
87      @Override
88      void applyClassRedefinition(@NonNull Class<?> realClass, @NonNull byte[] modifiedClass) {
89          modifiedClassfiles.put(realClass, modifiedClass);
90      }
91  }