- 浏览: 328996 次
- 性别:
- 来自: 南太平洋
最新评论
-
yhxf_ie:
Ace Jump和IdeaVim,您都是如何安装的啊?在int ...
IDEA Intellij小技巧和插件 -
xiduoli:
这里没有看懂。我对着设置了一下。 然后查了一下 setting ...
IdeaVim插件使用技巧 -
kutekute:
分享快乐,谢啦
推荐一个颇好用的Scala REPL脚本控制台 -
lord_is_layuping:
“很难找到这样的两种语言(Pascal和Lisp),它们能如 ...
SICP读书笔记(2)——扉页,序 -
793059909:
kidneyball 写道793059909 写道下载页面打不 ...
推荐一个颇好用的Scala REPL脚本控制台
无聊写了个单元测试的基类,准备拿回公司用
package net.daniel.test; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.jmock.Mockery; import org.jmock.lib.legacy.ClassImposteriser; import org.junit.Before; import org.objenesis.Objenesis; import org.objenesis.ObjenesisStd; /** * 本测试用例适用于JUnit4,提供以下功能: * 1. 通过标注(Annotation)进行测试用例对象域的创建和注入。提供以下标注: * a)@Mock private MyType myField; 自动创建MyType类型的Mock对象,赋值给myField; * b) @Create private MyType myField; 自动创建MyType类型的对象实例,赋值给myField; * c)@Create @Inject private MyType myField; 自动创建MyType类型的对象,赋值给myField,并且注入到被测试用例的同名对象域中。 * 2. 可通过反射机制直接访问被测试用例(或其它对象)的不可见对象域,支持继承的对象域; * 3. 可通过反射机制直接调用被测试用例(或其它对象)的不可见方法,支持继承的方法,支持自动匹配与参数表最接近的重载方法签名,无须另行指定参数类型列表。 * 4. 支持通过Objnesis机制创建类实例 * 5. 支持按测试用例对象域名称获取合适的mockery对象类型 * 6. 提供若干集合处理的便利方法 * * @author Daniel Deng * * @param <CUT> 被测试的类型 * */ public abstract class AbstractTestCase<CUT> { /** * 被测试的对象 */ protected CUT toTest; /** * 用于Mock接口的Mockery */ protected Mockery interfaceMockery = new Mockery(); /** * 用于Mock具体类的Mockery */ protected Mockery classMockery = new Mockery(){{ setImposteriser(ClassImposteriser.INSTANCE); }}; /** * 当前TestCase对象域的缓存,包括祖先类中的对象域。 */ private Map<String, Field> mockedFields, createFields, injectFields, allFields; /** * 被测试对象的对象域缓存,包括祖先类中的对象域。 */ private Map<String, Field> sutFields; /** * 用于强行创建类实例的Objenesis对象,详情参考 http://code.google.com/p/objenesis/ */ private Objenesis objenesis; /** * 每次测试前执行的初始化方法,JUnit将保证此方法会在子类的@Before方法之前执行。 * 在本基类中,此方法负责处理标注与注入 * @throws Exception */ @Before public void init() throws Exception { applyAnntation(); toTest = createSUT(); applyInjection(); } /** * 子类应覆盖此方法,创建并返回被测试类的实例。 * 此方法将在@Mock与@Create标注处理完成后才被调用。如果需要的话,在创建被测试类实例时可使用这些被标注的对象域。 */ protected abstract CUT createSUT(); /** * 处理注入({@link Inject})标注。此方法应在createSUT()方法之后调用。 * * * @throws Exception */ protected void applyInjection() throws Exception { prepareSUTFields(); for (Field f : injectFields.values()) { Inject injectAnno = f.getAnnotation(Inject.class); String injectFieldName = injectAnno.value(); if (injectFieldName == null) injectFieldName = f.getName(); Field fieldToInject = sutFields.get(injectFieldName); if (fieldToInject == null) { throw new NoSuchFieldException("Field [" + injectFieldName + "] is not found in Class [" + toTest.getClass().getName() + "]."); } fieldToInject.set(toTest, f.get(this)); } } private void prepareSUTFields() { if (sutFields == null) { Class<?> clazz = toTest.getClass(); sutFields = newHashMap(); while (notAtTopLevel(clazz)) { Field[] fields = clazz.getDeclaredFields(); for (Field f : fields) { f.setAccessible(true); String fname = f.getName(); if (!sutFields.containsKey(fname)) { sutFields.put(fname, f); } } clazz = clazz.getSuperclass(); } } } /** * 通过 Objenesis机制来强制创建类实例,不会执行构造方法. * @param clazz 需要创建实例的类 * @return */ @SuppressWarnings("unchecked") protected <T> T forceCreate(Class<T> clazz) { if (objenesis == null) objenesis = new ObjenesisStd(); return ((T) objenesis.getInstantiatorOf(clazz).newInstance()); } private void performCreateFields() throws Exception { for (Field f : createFields.values()) { Create create = f.getAnnotation(Create.class); assert (create != null); Class<?> clazz = create.value(); if (clazz == Object.class) { clazz = f.getType(); } Constructor<?> constructor = null; Object newInstance = normalInstanciate(clazz, constructor); if (newInstance == null) { newInstance = forceCreate(clazz); } if (newInstance != null) { f.set(this, newInstance); } else { throw new RuntimeException("Type [" + clazz.getName() + "] of field [" + f.getName() + "] cannot instanciate."); } } } private Object normalInstanciate(Class<?> clazz, Constructor<?> constructor) { Object newInstance = null; try { constructor = clazz.getConstructor(); } catch (Exception e) { // Will be handled by objenesis later } if (constructor != null) { try { newInstance = clazz.newInstance(); } catch (Exception e) { // Will be handled by objenesis later } } return newInstance; } /** * 处理{@link Create}与{@link Mock}标注,实例化测试用例的对象域 * @throws Exception */ protected void applyAnntation() throws Exception { prepareTestCaseFields(); performCreateFields(); performMockFields(); } private void performMockFields() throws Exception{ for (Field f : mockedFields.values()) { Class<?> clazz = f.getType(); if (clazz.isInterface()) { f.set(this, interfaceMockery.mock(clazz)); } else { f.set(this, classMockery.mock(clazz)); } } } /** * JMock需要根据Mock对象的类型不同而选用不同的mockery实例,容易引起混淆和错误。此方法将根据传入的测试用例对象域名称自动选用合适的mockery实例。 * @param fieldName 采用了Mock对象的测试用例对象域名称 * @return 与指定对象域类型对应的Mockery实例 * @throws Exception */ protected Mockery mockery(String fieldName) throws Exception { prepareTestCaseFields(); Field f = allFields.get(fieldName); if (f == null) throw new NoSuchFieldException("Field [" + fieldName + "] is not found in Class [" + this.getClass().getName() + "]"); if (f.getType().isInterface()) { return interfaceMockery; } else { return classMockery; } } private void prepareTestCaseFields() { if (mockedFields == null) { Class<?> clazz = this.getClass(); mockedFields = newHashMap(); createFields = newHashMap(); injectFields = newHashMap(); allFields = newHashMap(); while (notAtTopLevel(clazz)) { Field[] fields = clazz.getDeclaredFields(); for (Field f : fields) { f.setAccessible(true); String fname = f.getName(); if (!allFields.containsKey(fname)) { allFields.put(fname, f); } if (f.getAnnotation(Mock.class) != null && !mockedFields.containsKey(fname)) { mockedFields.put(fname, f); } if (f.getAnnotation(Create.class) != null && !createFields.containsKey(fname)) { createFields.put(fname, f); } if (f.getAnnotation(Inject.class) != null && !injectFields.containsKey(fname)) { injectFields.put(fname, f); } } clazz = clazz.getSuperclass(); } } } /** * 绕过可见性限制,为被测试对象的对象域赋值 * @param fieldName 要赋值的对象域名称 * @param value 将要赋予对象域的值 * @throws Exception */ protected void setSUTField(String fieldName, Object value) throws Exception { Field f = findSUTField(fieldName); f.set(toTest, value); } /** * 绕过可见性限制,获取被测试对象的对象域取值 * @param fieldName 要获取的对象域名称 * @return 被测试对象中指定对象域的取值 * @throws Exception */ @SuppressWarnings("unchecked") protected <T> T getSUTField(String fieldName) throws Exception { Field f = findSUTField(fieldName); return (T) f.get(toTest); } private Field findSUTField(String fieldName) throws NoSuchFieldException { prepareSUTFields(); Field f = sutFields.get(fieldName); if (f == null) throw new NoSuchFieldException("Field [" + fieldName + "] is not found in Class [" + toTest.getClass().getName() + "]"); f.setAccessible(true); return f; } /** * 绕过可见性限制,为一个指定对象的对象域赋值 * @param obj 需要赋值的对象实例 * @param fieldName 要赋值的对象域名称 * @param value 将要赋予对象域的值 * @throws Exception */ protected void setField(Object obj, String fieldName, Object value) throws Exception { Field f = findField(obj, fieldName); f.set(obj, value); } /** * 绕过可见性限制,获取一个指定对象的对象域取值 * @param obj 需要获取对象域取值的对象实例 * @param fieldName 要获取的对象域名称 * @return 指定对象中指定对象域的取值 * @throws Exception */ @SuppressWarnings("unchecked") protected <T> T getField(Object obj, String fieldName) throws Exception { Field f = findField(obj, fieldName); return (T) f.get(obj); } /** * 获取指定对象中指定名称的对象域反射引用 * @param obj 指定的对象实例 * @param fieldName 对象域名称 * @return * @throws Exception */ protected Field findField(Object obj, String fieldName) throws Exception { Class<?> clazz = obj.getClass(); Field field = null; while (notAtTopLevel(clazz)) { try { field = clazz.getDeclaredField(fieldName); }catch (NoSuchFieldException e) { //leave the field as null } if (field != null) { field.setAccessible(true); return field; } clazz = clazz.getSuperclass(); } throw new NoSuchFieldException("Field [" + fieldName + "] is not found in Class [" + toTest.getClass().getName() + "]"); } private boolean notAtTopLevel(Class<?> clazz) { return clazz != null && clazz != Object.class; } /** * 绕过可见性限制,调用被测试对象实例中的指定方法(包括父类中的方法)。本方法将根据传入的参数表自动选择匹配的重载方法。 * @param methodName 方法名称 * @param parameters 调用阐述表 * @return 调用方法的返回值 * @throws Exception */ protected <T> T invokeSUTMethod(String methodName, Object... parameters) throws Exception { return invokeMethod(toTest, methodName, parameters); } /** * 绕过可见性限制,调用指定对象实例中的指定方法(包括父类中的方法)。本方法将根据传入的参数表自动选择匹配的重载方法。 * @param obj 指定的对象实例 * @param methodName 方法名称 * @param parameters 调用阐述表 * @return 调用方法的返回值 * @throws Exception */ @SuppressWarnings("unchecked") protected <T> T invokeMethod(Object obj, String methodName, Object... parameters) throws Exception { Method m = findMethod(obj.getClass(), methodName, map(Arrays.asList(parameters), new Fun1<Object, Class<?>>(){ public Class<?> f(Object arg0) throws Exception { return arg0 != null ? arg0.getClass() : null; } })); return (T) m.invoke(obj, parameters); } /** * 获取指定基础类型的对应装箱类型。 * @param clazz 需要装箱的类型 * @return 如果clazz为java基础类型,返回其对应的装箱类型。否则,返回clazz本身。 */ protected Class<?> Boxing(Class<?> clazz) { if (clazz == null) return null; if (clazz == Boolean.TYPE) return Boolean.class; if (clazz == Byte.TYPE) return Byte.class; if (clazz == Short.TYPE) return Short.class; if (clazz == Integer.TYPE) return Integer.class; if (clazz == Long.TYPE) return Long.class; if (clazz == Float.TYPE) return Float.class; if (clazz == Double.TYPE) return Double.class; return clazz; } private Method findMethod(final Class<? extends Object> clazz, final String methodName, final List<Class<?>> argumentClasses) throws Exception { Class<?> c = clazz; while (notAtTopLevel(c)) { List<Method> methods = filter(Arrays.asList(c.getDeclaredMethods()), new Fun1<Method, Boolean>() { public Boolean f(Method arg0) throws Exception { return methodName.equals(arg0.getName()) && (arg0.getParameterTypes().length == argumentClasses.size()); } }); for (Method m : methods) { Class<?>[] paramTypes = m.getParameterTypes(); boolean matched = true; for (Pair<Class<?>, Class<?>> paramPair : zip(argumentClasses, Arrays.asList(paramTypes))) { if (paramPair.first != null && !Boxing(paramPair.second).isAssignableFrom(paramPair.first)) { matched = false; break; } } if (matched) { m.setAccessible(true); return m; } } c = c.getSuperclass(); } throw new NoSuchMethodException("Method [" + methodName + "] is not found in class [" + clazz.getName() + "]."); } //一些便利的集合初始方法。可免去在创建时重复指定泛型参数的麻烦。 /** * 创建一个{@link HashMap}实例 * @return */ protected static <K, V> Map<K, V> newHashMap() { return new HashMap<K, V>(); } /** * 创建一个{@link ArrayList}实例 */ protected static <E> List<E> newArrayList() { return new ArrayList<E>(); } /** * 创建一个{@link HashSet}实例 * @return */ protected static <E> Set<E> newHashSet() { return new HashSet<E>(); } //标注定义 /** * 标记了此标注的测试用例对象域会由{@link AbstractTestCase#applyAnntation()}方法自动赋予对应的Mock对象实例。 * @author Daniel Deng * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) protected @interface Mock { } /** * 标记了此标注的测试用例对象域会由{@link AbstractTestCase#applyAnntation()}方法自动创建一个对象实例。如果指定了value属性, * 则创建由该属性所指定类型的对象实例,否则将创建被标注的对象域所定义类型的对象实例。如果该类型无法通过{@link Class#newInstance()}直 * 接实例化,则将尝试采用Objnesis机制强制实例化。 * @author Daniel * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) protected @interface Create { public Class<?> value() default Object.class; } /** * 标记了此标注的测试用例对象域会由{@link AbstractTestCase#applyInjection()}方法自动注入到被测试对象实例中。可以用value属性 * 来指定被测试对象的对象域名称。如果未指定该属性,则默认注入到与被标注的测试用例对象域同名的对象域中。 * @author Daniel * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) protected @interface Inject { public String value() default ""; } //一些进行简单的函数式集合处理的通用接口 protected static interface Fun<R> { R f() throws Exception; } protected static interface Fun1<P0, R> { R f(P0 arg0) throws Exception; } protected static interface Fun2<P0, P1, R> { R f(P0 arg0, P1 arg1) throws Exception; } protected static interface VoidFun { void f() throws Exception; } protected static interface VoidFun1<P0> { void f(P0 arg0) throws Exception; } protected static interface VoidFun2<P0, P1> { void f(P0 arg0, P1 arg1) throws Exception; } protected static class Pair<P1, P2> { public P1 first; public P2 second; } // 一些简单的模拟函数式集合处理的方法 /** * 对原始集合中的所有元素进行转换,映射成新的集合 * @param collection 原始集合实例 * @param mapper 一个接受原始集合中的元素,将其转换为结果结合中的元素的函数对象 * @return 经过转换后的新集合 * @throws Exception */ protected static <S, T> List<T> map(Collection<S> collection, Fun1<S, T> mapper) throws Exception { List<T> converted = newArrayList(); if (collection != null && collection.size() > 0) { Iterator<S> it = collection.iterator(); while (it.hasNext()) { S element = it.next(); converted.add(mapper.f(element)); } } return converted; } /** * 返回由原始集合中满足特定条件的元素所组成的新集合 * @param collection 原始集合实例 * @param condition 一个接受原始集合中的元素,返回该元素是否满足条件的函数对象。如果该函数对象返回true,则对应的元素会被保留到结果集合中 * @return 由原始集合中满足特定条件的元素所组成的新集合 * @throws Exception */ protected static <S> List<S> filter(Collection<S> collection, Fun1<S, Boolean> condition) throws Exception { List<S> filtered = newArrayList(); if (collection != null && collection.size() > 0) { Iterator<S> it = collection.iterator(); while (it.hasNext()) { S el = it.next(); if (Boolean.TRUE.equals(condition.f(el))) { filtered.add(el); } } } return filtered; } /** * 将两个原始集合的元素按顺序两两配对,将配对结果形成新的集合 * @param collection1 原始集合1 * @param collection2 原始集合2 * @return 一个由集合1与集合2中的元素两两配对所形成的新集合。该集合的元素为{@link Pair}类型,其中first域为集合1中的对应元素,second域为集合2中的对应元素。 * 如果两个原始集合长度不同,则结果集合的长度等于最长的原始集合长度。在结果集合中,超出较短的原始集合长度部分对应的值{@link Pair}域值为null。 * @throws Exception */ protected static <P1, P2> List<Pair<P1, P2>> zip(Collection<P1> collection1, Collection<P2> collection2) throws Exception { Iterator<P1> it1 = collection1.iterator(); Iterator<P2> it2 = collection2.iterator(); List<Pair<P1, P2>> result = newArrayList(); boolean is1HasNext = it1.hasNext(); boolean is2HasNext = it2.hasNext(); while (is1HasNext || is2HasNext) { Pair<P1, P2> pair = new Pair<P1, P2>(); if (is1HasNext) pair.first = it1.next(); if (is2HasNext) pair.second = it2.next(); result.add(pair); is1HasNext = it1.hasNext(); is2HasNext = it2.hasNext(); } return result; } }
pom.xml
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-all</artifactId> <version>1.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.jmock</groupId> <artifactId>jmock</artifactId> <version>2.5.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.jmock</groupId> <artifactId>jmock-legacy</artifactId> <version>2.5.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.objenesis</groupId> <artifactId>objenesis</artifactId> <version>1.2</version> <scope>test</scope> </dependency> </dependencies>
相关推荐
当一个类同时继承自两个或更多的具有相同基类的类时,如果没有使用虚继承,则会存在多份相同的基类成员数据,导致资源浪费,并可能引发逻辑错误。 #### 二、虚基类的实现原理 虚基类的主要作用是确保即使在多继承的...
接着,我们需要创建一个测试类,继承自NUnit的`TestFixture`基类,并使用`Test`特性标记测试方法。 例如,假设我们的WindowsApplication2项目有一个名为`MyDialog`的模态对话框,我们可能会有以下测试代码: ```...
以上知识点只是C#基类的一小部分,实际上,C#基类还涵盖了网络编程、多线程、图形界面设计、XML处理、反射等多个方面。通过深入理解和熟练运用这些基类,开发者可以更高效地编写出健壮、安全的C#应用程序。在实践中...
More Effective C++的Item M26:限制某个类所能产生的对象数量,里面有一个模板类可作为基类,可作为参考学习内容,如果使用VS2005对模板类进行编译,注意类声明和定义如果分开写在.h和.cpp,子类不能只#include ...
在C++编程中,使用基类来管理一个时钟列表是一种常见的面向对象设计策略。这种设计允许我们通过统一的接口来处理不同的时钟类型,同时保持代码的灵活性和可扩展性。下面我们将深入探讨这个话题。 首先,让我们定义...
在Android开发中,RecyclerView是一个非常重要的组件,它用于展示可滚动的数据列表,具有高效和灵活的特点。本项目主要探讨如何对RecyclerView进行封装,创建基类适配器(Adapter)和基类ViewHolder,以及如何添加...
每个.aspx页面都继承自Page基类,它管理页面生命周期,包括初始化、加载、回发、呈现等关键阶段。Page基类提供了诸如FindControl()方法来查找控件,以及Context属性来访问HTTP上下文,这在处理用户输入和服务器端...
在面向对象编程(Object-Oriented Programming, OOP)中,基类与派生类是两个核心概念。基类(Base Class)也被称为父类或超类,它定义了一组通用的方法和属性,为其他类提供了共享行为的基础。派生类(Derived ...
"多继承和虚基类PPT" 本资源总结了C++中的多继承和虚基类的概念和应用。...在多条继承路径上有一个公共的基类时,可以使用虚基类机制来避免基类子对象的重复存储。虚基类机制可以减少内存的使用,提高程序的效率。
c# xml 操作 基类
AJAX的基类 可以直接使用这个基类处理AJAX的操作
虚基类的简单应用 这是平时的一个简单的代码 是自己写着玩的东西
在提供的"BusApp"子文件中,可能会包含一个示例应用程序,演示如何使用这个串口基类来实现具体的功能,如与硬件设备通信、数据交换等。通过分析和学习这个源码,开发者可以更好地理解和应用Rs232串口通信技术,并...
这里提到的"Viewpage+Fragment MVC 抽取5个基类"是一种设计模式的应用,旨在提高代码的可复用性和模块化。下面将详细阐述这一主题。 首先,`ViewPager`是Android SDK提供的一种可以展示多个视图,并允许用户通过...
在多重继承中,如果没有使用虚基类,可能会导致“菱形问题”——一个派生类有多个相同的基类实例,从而占用额外的内存并可能导致行为混乱。虚基类解决了这个问题,确保只有一个基类的实例。声明虚基类的语法是在继承...
其中包含的`TestCase`类主要用于定义Android单元测试的基础结构,它是一个抽象类,为所有Android特定的单元测试提供基类支持。 ##### 3.2 Android单元测试类继承体系 - **继承结构**:Android的单元测试类通常继承...
最后,我们需要编写一个主程序(Main Program)来测试这些类的功能,确保它们正确地计算了体积、侧面积和质量。 ```cpp int main() { Cylinder cy(5.0, 10.0); std::cout () ; std::cout () ; Pillar pillar...
这个压缩包文件“C#基类整理C#比较全的基类集合”可能包含了一系列C#基础类的代码示例和解释,帮助开发者理解和运用这些类。 首先,我们来看一下C#中的几个关键基类: 1. **System.Object**:所有C#类的终极基类,...
1.定义基类Shape,这有求面积的虚方法Mianji()...5.使用Winform窗体程序进行测试: 点击按钮,根据用户所选择的形状接收用户输入的参数,申明基类引用,通过基类引用接收派生类对象将求出的面积和周长值显示到窗体。