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              throw new IllegalArgumentException(
47                      "Invalid Class argument for partial mocking (use a MockUp instead): " + mockInstance);
48          }
49  
50          targetClass = getMockedClass(mockInstance);
51          applyPartialMockingToGivenInstance(mockInstance);
52  
53          InstanceFactory instanceFactory = createInstanceFactory(targetClass);
54          instanceFactory.lastInstance = mockInstance;
55  
56          TestRun.mockFixture().registerInstanceFactoryForMockedType(targetClass, instanceFactory);
57          TestRun.getExecutingTest().getCascadingTypes().add(false, targetClass);
58      }
59  
60      private void applyPartialMockingToGivenInstance(@NonNull Object instance) {
61          validateTargetClassType();
62          redefineMethodsAndConstructorsInTargetType();
63          targetInstances.add(instance);
64      }
65  
66      private void validateTargetClassType() {
67          if (targetClass.isInterface() || targetClass.isAnnotation() || targetClass.isArray()
68                  || targetClass.isPrimitive() || targetClass.isSynthetic()
69                  || MockingFilters.isSubclassOfUnmockable(targetClass) || isWrapperOfPrimitiveType(targetClass)
70                  || isGeneratedImplementationClass(targetClass)) {
71              throw new IllegalArgumentException("Invalid type for partial mocking: " + targetClass);
72          }
73      }
74  
75      @Override
76      void configureClassModifier(@NonNull MockedClassModifier modifier) {
77          modifier.useDynamicMocking();
78      }
79  
80      @Override
81      void applyClassRedefinition(@NonNull Class<?> realClass, @NonNull byte[] modifiedClass) {
82          modifiedClassfiles.put(realClass, modifiedClass);
83      }
84  }