All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mockit.internal.mockups.MockClassSetup Maven / Gradle / Ivy

Go to download

JMockit is a Java toolkit for developer (unit/integration) testing. It contains mocking APIs and other tools, supporting both JUnit and TestNG. The mocking APIs allow all kinds of Java code, without testability restrictions, to be tested in isolation from selected dependencies.

There is a newer version: 1.7
Show newest version
/*
 * Copyright (c) 2006-2013 Rogério Liesenfeld
 * This file is subject to the terms of the MIT license (see LICENSE.txt).
 */
package mockit.internal.mockups;

import java.lang.reflect.*;
import java.lang.reflect.Type;
import java.util.*;

import org.jetbrains.annotations.*;

import mockit.*;
import mockit.external.asm4.*;
import mockit.internal.*;
import mockit.internal.startup.*;
import mockit.internal.state.*;
import mockit.internal.util.*;

public final class MockClassSetup
{
   @NotNull private final Class realClass;
   @Nullable private ClassReader rcReader;
   @NotNull private final MockMethods mockMethods;
   @NotNull private final MockUp mock;
   private final boolean forStartupMock;

   public MockClassSetup(@NotNull Class> mockClass)
   {
      mock = ConstructorReflection.newInstance(mockClass);

      Type typeToMock = getTypeToMock(mockClass);
      ParameterizedType mockedType;

      if (typeToMock instanceof Class) {
         mockedType = null;
         realClass = (Class) typeToMock;
      }
      else if (typeToMock instanceof ParameterizedType){
         mockedType = (ParameterizedType) typeToMock;
         realClass = (Class) mockedType.getRawType();
      }
      else {
         throw new IllegalArgumentException(
            "Invalid target type specified in mock-up " + mockClass + ": " + typeToMock);
      }

      mockMethods = new MockMethods(realClass, mockedType);
      forStartupMock = true;

      new MockMethodCollector(mockMethods).collectMockMethods(mockClass);
   }

   @NotNull public static Type getTypeToMock(@NotNull Class mockUpClass)
   {
      Class currentClass = mockUpClass;

      do {
         Type superclass = currentClass.getGenericSuperclass();

         if (superclass instanceof ParameterizedType) {
            return ((ParameterizedType) superclass).getActualTypeArguments()[0];
         }
         else if (superclass == MockUp.class) {
            throw new IllegalArgumentException("No type to be mocked");
         }

         currentClass = (Class) superclass;
      }
      while (true);
   }

   public MockClassSetup(
      @NotNull Class realClass, @Nullable ParameterizedType mockedType, @NotNull MockUp mockUp,
      @Nullable byte[] realClassCode)
   {
      this.realClass = realClass;
      mockMethods = new MockMethods(realClass, mockedType);
      mock = mockUp;
      forStartupMock = false;
      rcReader = realClassCode == null ? null : new ClassReader(realClassCode);

      new MockMethodCollector(mockMethods).collectMockMethods(mockUp.getClass());
   }

   @NotNull public Set> redefineMethods()
   {
      Set> redefinedClasses = redefineMethodsInClassHierarchy();
      validateThatAllMockMethodsWereApplied();
      return redefinedClasses;
   }

   @NotNull private Set> redefineMethodsInClassHierarchy()
   {
      Set> redefinedClasses = new HashSet>();
      Class classToModify = realClass;

      while (classToModify != null && mockMethods.hasUnusedMocks()) {
         byte[] modifiedClassFile = modifyRealClass(classToModify);

         if (modifiedClassFile != null) {
            applyClassModifications(classToModify, modifiedClassFile);
            redefinedClasses.add(classToModify);
         }

         Class superClass = classToModify.getSuperclass();
         classToModify = superClass == Object.class || superClass == Proxy.class ? null : superClass;
         rcReader = null;
      }

      return redefinedClasses;
   }

   @Nullable private byte[] modifyRealClass(@NotNull Class classToModify)
   {
      if (rcReader == null) {
         rcReader = createClassReaderForRealClass(classToModify);
      }

      MockupsModifier modifier = new MockupsModifier(rcReader, classToModify, mock, mockMethods, forStartupMock);

      // Work-around for classes containing JSR/RET instructions, not supported when computing frames.
      try {
         rcReader.accept(modifier, ClassReader.SKIP_FRAMES);
      }
      catch (RuntimeException e) {
         modifier = new MockupsModifier(rcReader, classToModify, mock, mockMethods, forStartupMock, false);
         rcReader.accept(modifier, 0);
      }

      return modifier.wasModified() ? modifier.toByteArray() : null;
   }

   @NotNull private ClassReader createClassReaderForRealClass(@NotNull Class classToModify)
   {
      if (classToModify.isInterface() || classToModify.isArray()) {
         throw new IllegalArgumentException("Not a modifiable class: " + classToModify.getName());
      }

      return ClassFile.createReaderFromLastRedefinitionIfAny(classToModify);
   }

   private void applyClassModifications(@NotNull Class classToModify, @NotNull byte[] modifiedClassFile)
   {
      Startup.redefineMethods(classToModify, modifiedClassFile);
      mockMethods.registerMockStates(forStartupMock);

      if (forStartupMock) {
         CachedClassfiles.addClassfile(classToModify, modifiedClassFile);
      }
      else {
         String mockClassDesc = mockMethods.getMockClassInternalName();
         TestRun.mockFixture().addRedefinedClass(mockClassDesc, classToModify, modifiedClassFile);
      }
   }

   private void validateThatAllMockMethodsWereApplied()
   {
      List remainingMocks = mockMethods.getUnusedMockSignatures();

      if (!remainingMocks.isEmpty()) {
         String classDesc = mockMethods.getMockClassInternalName();
         String mockSignatures = new MethodFormatter(classDesc).friendlyMethodSignatures(remainingMocks);

         throw new IllegalArgumentException(
            "Matching real methods not found for the following mocks:\n" + mockSignatures);
      }
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy