[개발 자동화] Unitils 통합 Feed4junit
19946 단어 unitilsFeed4Junit
Feed4JUnit 은 JUnit 의 매개 변수 화 테스트 를 간편 하 게 만 들 고 이 테스트 에 미리 정의 하거나 무 작위 테스트 데 이 터 를 제공 할 수 있 습 니 다.이 는 업무 분석 원 이 정의 한 CVS 나 Excel 파일 에서 테스트 사례 데 이 터 를 읽 고 구축 / 유닛 테스트 프레임 워 크 에서 테스트 성공 을 보고 할 수 있 습 니 다.Feed4JUnit 을 이용 하여 무 작위 이지 만 검 증 된 데이터 로 연기 테스트 를 실시 하여 코드 코드 보급률 을 높이 고 매우 특수 한 데이터 구조 로 인해 발생 하 는 Bug 를 발견 할 수 있 습 니 다.
그러나 Feed4junit 는 spring - test 나 unitils 처럼 spring 통합 에 대한 지원 이 없 으 며, 트 랜 잭 션 제어 실현 방법 을 통 해 실 행 된 후 환경 을 되 돌 릴 수 없습니다.어떻게 Feef4junit 와 unitils 의 장점 을 결합 시 켜 둘 을 유기 적 으로 결합 시 키 는 지 는 본 고가 해결 해 야 할 문제 이다.
계획
UnitilsJUnit 4 는 Class 에서 @ RunWith (UnitilsJUnit 4 TestClassRunner. class) 를 정의 해 야 하고, Feed4junit 는 Class 에서 @ RunWith (Feeder. class) 를 정의 해 야 합 니 다. 기본 적 인 상황 은 둘 이 병존 할 수 없 으 므 로 변경 이 필요 합 니 다.고 치 는 방식 은 두 가지 가 있 습 니 다. 하 나 는 최신 @ Rule 로 고 치 는 것 입 니 다. 하 나 는 두 가지 코드 를 한 종류 에 합 치 는 것 입 니 다.힘 을 절약 하 는 각도 에서 나 는 뒤의 방안 을 채택 하기 로 결정 했다.
개작 UnitilsJUnit 4 TestClassRunner
UnitilsJUnit 4 TestClassRunner 는 JUnit 4 ClassRunner 에 계승 되 었 으 며, Feeder 는 새로운 BlockJUnit 4 ClassRunner 에 계승 되 었 으 며, 매개 변 수 를 가 진 테스트 방법 을 지원 하려 면 먼저 UnitilsJUnit 4 TestClassRunner 를 BlockJUnit 4 ClassRunner 로 개조 하 는 것 이 좋 습 니 다.
package org.unitils;
import java.util.List;
import org.junit.internal.runners.statements.InvokeMethod;
import org.junit.rules.MethodRule;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.unitils.core.TestListener;
import org.unitils.core.Unitils;
/**
*
* Unitils BlockJUnit4ClassRunner .
*
* <pre><b> :</b>
* BlockJUnit4ClassRunner
* </pre>
*
* <pre><b> :</b>
*
* </pre>
*
* @author <a href="mailto:[email protected]"> </a>
* @since 2013-8-12
* @version 1.0
*
*/
public class UnitilsJUnitBlockRunner extends BlockJUnit4ClassRunner
{
TestListener listener;
public UnitilsJUnitBlockRunner(Class<?> aClass) throws InitializationError {
super(aClass);
listener = Unitils.getInstance().getTestListener();
}
@Override
protected Statement classBlock(RunNotifier runNotifier) {
listener.beforeTestClass(getTestClass().getClass());
return super.classBlock(runNotifier);
}
@Override
protected Object createTest() throws Exception {
Object o = super.createTest();
listener.afterCreateTestObject(o);
return o;
}
@Override
protected Statement methodInvoker(final FrameworkMethod frameworkMethod, final Object o) {
return new InvokeMethod(frameworkMethod, o) {
@Override
public void evaluate() throws Throwable {
listener.beforeTestMethod(o, frameworkMethod.getMethod());
Throwable threw = null;
try {
super.evaluate();
} catch (Throwable t) {
threw = t;
} finally {
listener.afterTestMethod(o, frameworkMethod.getMethod(), threw);
}
if(threw!=null)
throw threw;
}
};
}
@Override
protected List<MethodRule> rules(Object o) {
List<MethodRule> list = super.rules(o);
list.add(new MethodRule() {
public Statement apply(final Statement nextStatement, final FrameworkMethod frameworkMethod, final Object o) {
return new Statement() {
public void evaluate() throws Throwable {
listener.beforeTestSetUp(o, frameworkMethod.getMethod());
nextStatement.evaluate();
listener.afterTestTearDown(o, frameworkMethod.getMethod());
}
};
}
});
return list;
}
}
피 더 개조
Feeder 의 원본 코드 를 복사 하여 UnitilsJUnitBlockRunner 를 직접 계승 합 니 다.
package org.databene.feed4junit;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.databene.benerator.Generator;
import org.databene.benerator.anno.AnnotationMapper;
import org.databene.benerator.anno.DefaultPathResolver;
import org.databene.benerator.anno.PathResolver;
import org.databene.benerator.anno.ThreadPoolSize;
import org.databene.benerator.engine.BeneratorContext;
import org.databene.benerator.engine.DefaultBeneratorContext;
import org.databene.benerator.factory.EquivalenceGeneratorFactory;
import org.databene.benerator.wrapper.ProductWrapper;
import org.databene.commons.ConfigurationError;
import org.databene.commons.IOUtil;
import org.databene.commons.Period;
import org.databene.commons.StringUtil;
import org.databene.commons.converter.AnyConverter;
import org.databene.feed4junit.ChildRunner;
import org.databene.feed4junit.FrameworkMethodWithParameters;
import org.databene.feed4junit.Scheduler;
import org.databene.feed4junit.scheduler.DefaultFeedScheduler;
import org.databene.model.data.DataModel;
import org.databene.platform.java.BeanDescriptorProvider;
import org.databene.platform.java.Entity2JavaConverter;
import org.databene.script.DatabeneScriptParser;
import org.databene.script.Expression;
import org.junit.Test;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.FrameworkField;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerScheduler;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
import org.unitils.UnitilsJUnitBlockRunner;
/**
*
*
* Feeder Unitils.
*
* <pre><b> :</b>
* Unitils Feed4JUnit
* </pre>
*
* <pre><b> :</b>
*
* </pre>
*
* @author <a href="mailto:[email protected]"> </a>
* @since 2013-8-12
* @version 1.0
*
*/
public class Feeder extends UnitilsJUnitBlockRunner
{
public static final String CONFIG_FILENAME_PROPERTY = "feed4junit.properties";
private static final String DEFAULT_CONFIG_FILENAME = "feed4junit.properties";
private static final String FEED4JUNIT_BASE_PATH = "feed4junit.basepath";
private static final long DEFAULT_TIMEOUT = Period.WEEK.getMillis();
static {
ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true);
}
private BeneratorContext context;
private PathResolver pathResolver;
private AnnotationMapper annotationMapper;
private List<FrameworkMethod> children;
private RunnerScheduler scheduler;
public Feeder(Class<?> aClass) throws InitializationError
{
super(aClass);
this.children = null;
}
@Override
protected String testName(FrameworkMethod method) {
return (method instanceof FrameworkMethodWithParameters ? method.toString() : super.testName(method));
}
@Override
public void setScheduler(RunnerScheduler scheduler) {
this.scheduler = scheduler;
super.setScheduler(scheduler);
}
/**
* Instantiates a test class and initializes attributes
* which have been marked with a @Source annotation.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected Object createTest() throws Exception {
Object testObject = super.createTest();
for (FrameworkField attribute : getTestClass().getAnnotatedFields(org.databene.benerator.anno.Source.class)) {
if ((attribute.getField().getModifiers() & Modifier.PUBLIC) == 0)
throw new ConfigurationError("Attribute '" + attribute.getField().getName() + "' must be public");
Generator<?> generator = getAnnotationMapper().createAndInitAttributeGenerator(attribute.getField(), getContext());
if (generator != null) {
ProductWrapper wrapper = new ProductWrapper();
wrapper = generator.generate(wrapper);
if (wrapper != null)
attribute.getField().set(testObject, wrapper.unwrap());
}
}
return testObject;
}
@Override
protected List<FrameworkMethod> computeTestMethods() {
if (children == null) {
children = new ArrayList<FrameworkMethod>();
TestClass testClass = getTestClass();
BeneratorContext context = getContext();
context.setGeneratorFactory(new EquivalenceGeneratorFactory());
getAnnotationMapper().parseClassAnnotations(testClass.getAnnotations(), context);
for (FrameworkMethod method : testClass.getAnnotatedMethods(Test.class)) {
if (method.getMethod().getParameterTypes().length == 0) {
// standard JUnit test method
children.add(method);
continue;
} else {
// parameterized Feed4JUnit test method
List<? extends FrameworkMethod> parameterizedTestMethods;
parameterizedTestMethods = computeParameterizedTestMethods(method.getMethod(), context);
children.addAll(parameterizedTestMethods);
}
}
}
return children;
}
@Override
protected void validateTestMethods(List<Throwable> errors) {
validatePublicVoidMethods(Test.class, false, errors);
}
// test execution --------------------------------------------------------------------------------------------------
protected Statement childrenInvoker(final RunNotifier notifier) {
return new Statement() {
@Override
public void evaluate() {
runChildren(notifier);
}
};
}
private void runChildren(final RunNotifier notifier) {
RunnerScheduler scheduler = getScheduler();
for (FrameworkMethod method : getChildren())
scheduler.schedule(new ChildRunner(this, method, notifier));
scheduler.finished();
}
public RunnerScheduler getScheduler() {
if (scheduler == null)
scheduler = createDefaultScheduler();
return scheduler;
}
protected RunnerScheduler createDefaultScheduler() {
TestClass testClass = getTestClass();
Scheduler annotation = testClass.getJavaClass().getAnnotation(Scheduler.class);
if (annotation != null) {
String spec = annotation.value();
Expression<?> bean = DatabeneScriptParser.parseBeanSpec(spec);
return (RunnerScheduler) bean.evaluate(null);
} else {
return new DefaultFeedScheduler(1, DEFAULT_TIMEOUT);
}
}
@Override
public void runChild(FrameworkMethod method, RunNotifier notifier) {
super.runChild(method, notifier);
}
// helpers ---------------------------------------------------------------------------------------------------------
private PathResolver configuredPathResolver() {
if (pathResolver != null)
return pathResolver;
String configuredConfigFileName = System.getProperty(CONFIG_FILENAME_PROPERTY);
String configFileName = configuredConfigFileName;
if (StringUtil.isEmpty(configFileName))
configFileName = DEFAULT_CONFIG_FILENAME;
if (IOUtil.isURIAvailable(configFileName)) {
// load individual or configured config file
return configuredPathResolver(configFileName);
} else if (StringUtil.isEmpty(configuredConfigFileName)) {
// if no explicit config file was configured, then use defaults...
return createDefaultResolver();
} else {
// ...otherwise raise an exception
throw new ConfigurationError("Feed4JUnit configuration file not found: " + configuredConfigFileName);
}
}
private PathResolver createDefaultResolver() {
return applyBasePath(new DefaultPathResolver());
}
private PathResolver configuredPathResolver(String configFileName) {
try {
Map<String, String> properties = IOUtil.readProperties(configFileName);
String pathResolverSpec = properties.get("pathResolver");
if (pathResolverSpec != null) {
PathResolver resolver;
resolver = (PathResolver) DatabeneScriptParser.parseBeanSpec(pathResolverSpec).evaluate(getContext());
return applyBasePath(resolver);
} else
return createDefaultResolver();
} catch (IOException e) {
throw new ConfigurationError("Error reading config file '" + configFileName + "'", e);
}
}
private PathResolver applyBasePath(PathResolver resolver) {
String confdBasePath = System.getProperty(FEED4JUNIT_BASE_PATH);
if (confdBasePath != null)
resolver.setBasePath(confdBasePath);
return resolver;
}
private void validatePublicVoidMethods(Class<? extends Annotation> annotation, boolean isStatic, List<Throwable> errors) {
List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(annotation);
for (FrameworkMethod eachTestMethod : methods)
eachTestMethod.validatePublicVoid(isStatic, errors);
}
private List<FrameworkMethodWithParameters> computeParameterizedTestMethods(Method method, BeneratorContext context) {
Integer threads = getThreadCount(method);
long timeout = getTimeout(method);
List<FrameworkMethodWithParameters> result = new ArrayList<FrameworkMethodWithParameters>();
Class<?>[] parameterTypes = method.getParameterTypes();
Generator<Object[]> paramGenerator = getAnnotationMapper().createAndInitMethodParamsGenerator(method, context);
Class<?>[] expectedTypes = parameterTypes;
ProductWrapper<Object[]> wrapper = new ProductWrapper<Object[]>();
int count = 0;
while ((wrapper = paramGenerator.generate(wrapper)) != null) {
Object[] generatedParams = wrapper.unwrap();
if (generatedParams.length > expectedTypes.length) // imported data may have more columns than the method parameters, ...
generatedParams = Arrays.copyOfRange(generatedParams, 0, expectedTypes.length); // ...so cut them
for (int i = 0; i < generatedParams.length; i++) {
generatedParams[i] = Entity2JavaConverter.convertAny(generatedParams[i]);
generatedParams[i] = AnyConverter.convert(generatedParams[i], parameterTypes[i]);
}
// generated params may be to few, e.g. if an XLS row was imported with trailing nulls,
// so create an array of appropriate size
Object[] usedParams = new Object[parameterTypes.length];
System.arraycopy(generatedParams, 0, usedParams, 0, Math.min(generatedParams.length, usedParams.length));
result.add(new FrameworkMethodWithParameters(method, usedParams, threads, timeout));
count++;
}
if (count == 0)
throw new RuntimeException("No parameter values available for method: " + method);
return result;
}
private Integer getThreadCount(Method method) {
ThreadPoolSize methodAnnotation = method.getAnnotation(ThreadPoolSize.class);
if (methodAnnotation != null)
return methodAnnotation.value();
Class<?> testClass = method.getDeclaringClass();
ThreadPoolSize classAnnotation = testClass.getAnnotation(ThreadPoolSize.class);
if (classAnnotation != null)
return classAnnotation.value();
return null;
}
private long getTimeout(Method method) {
return DEFAULT_TIMEOUT;
}
private AnnotationMapper getAnnotationMapper() {
// lazy initialization is necessary since the constructor is not executed by JUnit
if (annotationMapper == null) {
PathResolver pathResolver = configuredPathResolver();
annotationMapper = new AnnotationMapper(new EquivalenceGeneratorFactory(), getDataModel(), pathResolver);
}
return annotationMapper;
}
private BeneratorContext getContext() {
// lazy initialization is necessary since the constructor is not executed by JUnit
if (context == null) {
context = new DefaultBeneratorContext();
DataModel dataModel = context.getDataModel();
new BeanDescriptorProvider(dataModel);
}
return context;
}
private DataModel getDataModel() {
return getContext().getDataModel();
}
}
Example
현재 우 리 는 테스트 클래스 에서 Feeder 의 주해 와 Unitils 의 주 해 를 동시에 사용 합 니 다. 아래 열 은 feed4junit 를 통 해 엑셀 파일 에서 테스트 사례 를 읽 고 모든 사례 에 대해 save 방법 을 테스트 합 니 다. 테스트 클래스 는 Spring 용기 관 리 를 통 해 테스트 가 끝 난 후에 사 무 는 자동 으로 스크롤 백 되 어 테스트 환경 을 복원 합 니 다.
연기 가 나 는 통합 테스트 방법 이 생 겨 서 엄 마 는 더 이상 나의 코드 품질 을 걱정 할 필요 가 없다.
package com.litt.cidp.system.service;
import java.util.Map;
import org.databene.benerator.anno.Source;
import org.databene.feed4junit.Feeder;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.jdbc.core.JdbcTemplate;
import org.unitils.spring.annotation.SpringBeanByType;
import com.litt.cidp.system.po.Role;
import com.litt.core.test.BaseServiceTester;
/**
*
* .
*
* <pre><b> :</b>
*
* </pre>
*
* <pre><b> :</b>
*
* </pre>
*
* @author <a href="mailto:[email protected]"> </a>
* @since 2013-8-9
* @version 1.0
*
*/
@SpringApplicationContext("spring/applicationContext-*.xml")
@RunWith(Feeder.class)
public class RoleServiceSmokeTest
{
@SpringBeanByType
private IRoleService roleService;
@SpringBeanByType
private JdbcTemplate jdbcTemplate;
@Test
@Source("com/litt/cidp/system/service/Role-smoke-data.xls")
public void save(String roleName, String remark) {
Role role = new Role();
role.setRoleName(roleName);
role.setRemark(remark);
roleService.save(role);
this.validate(role);
}
private void validate(Role role)
{
Map<String, Object> rsMap = jdbcTemplate.queryForMap("SELECT * FROM ROLE WHERE ROLE_NAME=?", new Object[]{role.getRoleName()});
Assert.assertEquals(role.getRemark(), rsMap.get("REMARK"));
}
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Feed4Junit 의 간단 한 사용 (3) 데이터 원본 은 데이터베이스 에서When applying the annotation to a class, the defined database is available to all test methods. A basic configuration ca...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.