001/** 002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.jboot.test; 017 018import com.jfinal.kit.LogKit; 019import io.jboot.utils.ReflectUtil; 020import io.jboot.utils.StrUtil; 021 022import java.lang.reflect.Constructor; 023import java.lang.reflect.InvocationTargetException; 024import java.lang.reflect.Method; 025import java.util.Arrays; 026 027class MockMethodInfo { 028 029 private Class<?> targetClass; 030 private Method targetMethod; 031 private boolean firstArgIsTarget; 032 033 private Class<?> mockClass; 034 private Method mockMethod; 035 036 public MockMethodInfo(MockMethodInfo methodInfo,Class<?> newTargetClass) { 037 this.targetClass = newTargetClass; 038 this.targetMethod = methodInfo.targetMethod; 039 this.firstArgIsTarget = methodInfo.firstArgIsTarget; 040 this.mockClass = methodInfo.mockClass; 041 this.mockMethod = methodInfo.mockMethod; 042 } 043 044 045 public MockMethodInfo(Class<?> testClass, Method testClassMethod, MockMethod mockMethod) { 046 this.targetClass = mockMethod.targetClass(); 047 048 String targetMethodName = StrUtil.isNotBlank(mockMethod.targetMethod()) ? mockMethod.targetMethod() : testClassMethod.getName(); 049 this.targetMethod = ReflectUtil.searchMethod(targetClass, m -> { 050 if (!m.getName().equals(targetMethodName)) { 051 return false; 052 } 053 054 Class<?>[] testClassMethodParaTypes = testClassMethod.getParameterTypes(); 055 056 if (Arrays.equals(m.getParameterTypes(), testClassMethodParaTypes)) { 057 return true; 058 } 059 060 if (testClassMethodParaTypes.length > 0 && testClassMethodParaTypes[0] == mockMethod.targetClass()) { 061 Class<?>[] newClassMethodParaTypes = new Class[testClassMethodParaTypes.length - 1]; 062 System.arraycopy(testClassMethodParaTypes, 1, newClassMethodParaTypes, 0, newClassMethodParaTypes.length); 063 064 if (Arrays.equals(m.getParameterTypes(), newClassMethodParaTypes)) { 065 this.firstArgIsTarget = true; 066 return true; 067 } 068 } 069 return false; 070 }); 071 072 if (targetMethod == null) { 073 throw new IllegalStateException("Can not mock the method: \"" + targetMethodName + "\" in class: " + mockMethod.targetClass()); 074 } 075 076 this.mockClass = testClass; 077 this.mockMethod = testClassMethod; 078 079 } 080 081 082 public Class<?> getTargetClass() { 083 return targetClass; 084 } 085 086 public void setTargetClass(Class<?> targetClass) { 087 this.targetClass = targetClass; 088 } 089 090 public Method getTargetMethod() { 091 return targetMethod; 092 } 093 094 public void setTargetMethod(Method targetMethod) { 095 this.targetMethod = targetMethod; 096 } 097 098 public Class<?> getMockClass() { 099 return mockClass; 100 } 101 102 public void setMockClass(Class<?> mockClass) { 103 this.mockClass = mockClass; 104 } 105 106 public Method getMockMethod() { 107 return mockMethod; 108 } 109 110 public void setMockMethod(Method mockMethod) { 111 this.mockMethod = mockMethod; 112 } 113 114 public Object invokeMock(Object obj, Object... args) throws InvocationTargetException, IllegalAccessException { 115 if (firstArgIsTarget) { 116 Object[] newArgs = new Object[args.length + 1]; 117 newArgs[0] = obj; 118 System.arraycopy(args, 0, newArgs, 1, args.length); 119 args = newArgs; 120 } 121 122 Object testInstance = MockApp.getInstance().getTestInstance(); 123 if (testInstance == null) { 124 testInstance = newInstance(this.mockClass); 125 } 126 127 if (testInstance == null) { 128 return null; 129 } 130 131 return mockMethod.invoke(testInstance, args); 132 } 133 134 135 private static <T> T newInstance(Class<T> clazz) { 136 try { 137 Constructor<T> constructor = clazz.getDeclaredConstructor(); 138 constructor.setAccessible(true); 139 return constructor.newInstance(); 140 } catch (Exception e) { 141 LogKit.logNothing(e); 142 } 143 return null; 144 } 145 146}