View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
4    */
5   package mockit.internal.expectations.mocking;
6   
7   import static mockit.internal.util.AutoBoxing.isWrapperOfPrimitiveType;
8   import static mockit.internal.util.GeneratedClasses.getMockedClass;
9   import static mockit.internal.util.GeneratedClasses.isGeneratedImplementationClass;
10  
11  import edu.umd.cs.findbugs.annotations.NonNull;
12  
13  import java.util.ArrayList;
14  import java.util.HashMap;
15  import java.util.List;
16  import java.util.Map;
17  
18  import mockit.internal.expectations.MockingFilters;
19  import mockit.internal.state.TestRun;
20  
21  public final class PartialMocking extends BaseTypeRedefinition {
22      @NonNull
23      public final List<Object> targetInstances;
24      @NonNull
25      private final Map<Class<?>, byte[]> modifiedClassfiles;
26  
27      public PartialMocking() {
28          targetInstances = new ArrayList<>(2);
29          modifiedClassfiles = new HashMap<>();
30      }
31  
32      public void redefineTypes(@NonNull Object[] instancesToBePartiallyMocked) {
33          for (Object instance : instancesToBePartiallyMocked) {
34              redefineClassHierarchy(instance);
35          }
36  
37          if (!modifiedClassfiles.isEmpty()) {
38              TestRun.mockFixture().redefineMethods(modifiedClassfiles);
39              modifiedClassfiles.clear();
40          }
41      }
42  
43      private void redefineClassHierarchy(@NonNull Object mockInstance) {
44          if (mockInstance instanceof Class) {
45              throw new IllegalArgumentException(
46                      "Invalid Class argument for partial mocking (use a MockUp instead): " + mockInstance);
47          }
48  
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      private void applyPartialMockingToGivenInstance(@NonNull Object instance) {
60          validateTargetClassType();
61          redefineMethodsAndConstructorsInTargetType();
62          targetInstances.add(instance);
63      }
64  
65      private void validateTargetClassType() {
66          if (targetClass.isInterface() || targetClass.isAnnotation() || targetClass.isArray()
67                  || targetClass.isPrimitive() || targetClass.isSynthetic()
68                  || MockingFilters.isSubclassOfUnmockable(targetClass) || isWrapperOfPrimitiveType(targetClass)
69                  || isGeneratedImplementationClass(targetClass)) {
70              throw new IllegalArgumentException("Invalid type for partial mocking: " + targetClass);
71          }
72      }
73  
74      @Override
75      void configureClassModifier(@NonNull MockedClassModifier modifier) {
76          modifier.useDynamicMocking();
77      }
78  
79      @Override
80      void applyClassRedefinition(@NonNull Class<?> realClass, @NonNull byte[] modifiedClass) {
81          modifiedClassfiles.put(realClass, modifiedClass);
82      }
83  }