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.config.JFinalConfig; 019import com.jfinal.core.JFinalFilter; 020import com.jfinal.kit.LogKit; 021import com.jfinal.kit.PathKit; 022import io.jboot.aop.JbootAopFactory; 023import io.jboot.aop.cglib.JbootCglibProxyFactory; 024import io.jboot.aop.javassist.JbootJavassistProxyFactory; 025import io.jboot.app.PathKitExt; 026import io.jboot.app.config.JbootConfigManager; 027import io.jboot.test.web.MockFilterChain; 028import io.jboot.test.web.MockFilterConfig; 029import io.jboot.test.web.MockJFinalFilter; 030import io.jboot.utils.ClassScanner; 031import io.jboot.utils.FileUtil; 032import io.jboot.utils.ReflectUtil; 033 034import javax.servlet.ServletException; 035import javax.servlet.ServletRequest; 036import javax.servlet.ServletResponse; 037import java.io.File; 038import java.io.IOException; 039import java.lang.reflect.Method; 040import java.util.ArrayList; 041import java.util.LinkedList; 042import java.util.List; 043 044class MockApp { 045 046 public static final String DEFAULT_WEB_ROOT_PATH = "../classes/webapp"; 047 public static final String DEFAULT_CLASS_PATH = "../classes"; 048 049 private static final MockApp app = new MockApp(); 050 051 private JFinalConfig config; 052 private final JFinalFilter filter; 053 054 private Object testInstance; 055 056 private boolean isInit = false; 057 058 059 private MockApp() { 060 filter = new MockJFinalFilter(); 061 } 062 063 public static MockApp getInstance() { 064 return app; 065 } 066 067 static void mockRequest(ServletRequest req, ServletResponse res) { 068 try { 069 app.filter.doFilter(req, res, new MockFilterChain()); 070 } catch (IOException | ServletException e) { 071 e.printStackTrace(); 072 } 073 } 074 075 public void setTestInstance(Object testInstance) { 076 this.testInstance = testInstance; 077 } 078 079 public Object getTestInstance() { 080 return testInstance; 081 } 082 083 void start(Class<?> testClass) { 084 if (!isInit) { 085 init(testClass); 086 isInit = true; 087 } 088 } 089 090 private void init(Class<?> testClass) { 091 try { 092 TestConfig testConfig = testClass.getAnnotation(TestConfig.class); 093 094 if (testConfig != null) { 095 JbootConfigManager.parseArgs(testConfig.launchArgs()); 096 JbootConfigManager.me().setDevMode(testConfig.devMode()); 097 ClassScanner.setPrintScannerInfoEnable(testConfig.printScannerInfo()); 098 } else { 099 ClassScanner.setPrintScannerInfoEnable(false); 100 } 101 102 doInitJFinalPathKit(testConfig); 103 104 105 boolean autoMockInterface = testConfig != null && testConfig.autoMockInterface(); 106 107 //support cglib and javassist 108 JbootCglibProxyFactory.setMethodInterceptor(aClass -> new MockMethodInterceptor(autoMockInterface)); 109 JbootJavassistProxyFactory.setMethodInterceptor(aClass -> new MockMethodInterceptor(autoMockInterface)); 110 111 List<MockMethodInfo> mockMethodInfos = getMockMethodInfoList(testClass); 112 if (!mockMethodInfos.isEmpty()) { 113 mockMethodInfos.forEach(MockMethodInterceptor::addMethodInfo); 114 } 115 116 List<MockClassInfo> mockClassInfos = getMockClassInfoList(testClass); 117 if (mockClassInfos != null && !mockClassInfos.isEmpty()) { 118 mockClassInfos.forEach(mockClassInfo -> { 119 JbootAopFactory.me().addMapping(mockClassInfo.getTargetClass(), mockClassInfo.getMockClass()); 120 MockMethodInterceptor.addMockClass(mockClassInfo.getMockClass()); 121 }); 122 } 123 124 filter.init(new MockFilterConfig()); 125 config = ReflectUtil.getFieldValue(filter, "jfinalConfig"); 126 } catch (ServletException e) { 127 LogKit.error(e.toString(), e); 128 } 129 } 130 131 private List<MockClassInfo> getMockClassInfoList(Class<?> testClass) { 132 List<Class> scanedClasses = ClassScanner.scanClassByAnnotation(MockClass.class, true); 133 134 LinkedList<Class> mockClasses = new LinkedList<>(scanedClasses); 135 Class<?>[] declardClasses = testClass.getDeclaredClasses(); 136 if (declardClasses.length > 0) { 137 for (Class<?> declardClass : declardClasses) { 138 if (declardClass.getAnnotation(MockClass.class) != null) { 139 mockClasses.remove(declardClass); 140 mockClasses.addLast(declardClass); 141 } 142 } 143 } 144 145 if (mockClasses.isEmpty()) { 146 return null; 147 } 148 149 List<MockClassInfo> classInfoList = new ArrayList<>(); 150 for (Class<?> mockClass : mockClasses) { 151// MockClass annotation = mockClass.getAnnotation(MockClass.class); 152// Class<?>[] targetClasses = annotation.value(); 153// if (targetClasses.length == 0) { 154// targetClasses = mockClass.getInterfaces(); 155// } 156 Class<?>[] targetClasses = mockClass.getInterfaces(); 157 if (targetClasses.length == 0) { 158 throw new IllegalStateException("@MockClass() in \"" + mockClass.getName() + "\" must implementation interface."); 159 } 160 161 for (Class<?> targetClass : targetClasses) { 162 classInfoList.add(new MockClassInfo(mockClass, targetClass)); 163 } 164 } 165 166 return classInfoList; 167 } 168 169 private List<MockMethodInfo> getMockMethodInfoList(Class<?> testClass) { 170 List<MockMethodInfo> methodInfoList = new ArrayList<>(); 171 searchMockMethods(testClass, methodInfoList); 172 return methodInfoList; 173 } 174 175 176 private void searchMockMethods(Class<?> searchClass, List<MockMethodInfo> tolist) { 177 Method[] methods = searchClass.getDeclaredMethods(); 178 for (Method method : methods) { 179 MockMethod mockMethod = method.getAnnotation(MockMethod.class); 180 if (mockMethod != null) { 181 tolist.add(new MockMethodInfo(searchClass, method, mockMethod)); 182 } 183 } 184 185 Class<?> superClass = searchClass.getSuperclass(); 186 if (superClass != Object.class && superClass != null) { 187 searchMockMethods(superClass, tolist); 188 } 189 } 190 191 192 void stop() { 193 if (config != null) { 194 config.onStop(); 195 } 196 } 197 198 199 private void doInitJFinalPathKit(TestConfig testConfig) { 200 try { 201 String configWebRootPath = testConfig != null ? testConfig.webRootPath() : DEFAULT_WEB_ROOT_PATH; 202 String configClassPath = testConfig != null ? testConfig.classPath() : DEFAULT_CLASS_PATH; 203 204 //相对路径,是相对 /target/test-classes 进行判断的 205 if (!FileUtil.isAbsolutePath(configWebRootPath)) { 206 configWebRootPath = new File(PathKitExt.getWebRootPath(), configWebRootPath).getCanonicalPath(); 207 } 208 //设置 webRootPath 209 PathKit.setWebRootPath(configWebRootPath); 210 211 212 if (!FileUtil.isAbsolutePath(configClassPath)) { 213 configClassPath = new File(PathKitExt.getRootClassPath(), configClassPath).getCanonicalPath(); 214 } 215 //设置 classPath 216 PathKit.setRootClassPath(configClassPath); 217 218 } catch (Exception ex) { 219 throw new RuntimeException(ex); 220 } 221 } 222 223 224}