450 likes | 755 Views
吴大瑞 2010-11-07. ASC JTESTER 分享. 大纲. 整体框架介绍 断言模块 Spring 模块 DbFit 模块 问题解答 Q&A. 整体框架介绍. dbFit module. Spring module. Inject module. Junit / TestNG. jTester. jmockit. tracer module. Jtester assert. 整体框架介绍. 框架中的观察者模式. 每个生命周期都是一个观察点。 每个模块都有一个监听器。
E N D
吴大瑞 2010-11-07 ASC JTESTER 分享
大纲 • 整体框架介绍 • 断言模块 • Spring模块 • DbFit模块 • 问题解答 • Q&A
整体框架介绍 dbFitmodule Spring module Injectmodule Junit/ TestNG jTester jmockit tracermodule Jtester assert
框架中的观察者模式 • 每个生命周期都是一个观察点。 • 每个模块都有一个监听器。 • 注册方式:unitils.modules=database,dbfit,jmock,inject,spring,tracer • 当框架执行到每个生命周期时,会通知各个模块执行对应的动作。
Spring模块的监听器 • 在测试类创建初始化spring容器。 • @SpringApplicationContext文件加载 • @AutoBeanInject 自动spring bean查找和注册。 • @SpringBeanFor 类型的bean注册。 • 初始完容器,根据Annotation注入相应的spring bean。 • @SpringBeanByName • @SpringBeanByType • @SpringBeanFor
DbFit模块的监听器 • 每个测试方法前,注册wiki变量;查找要运行的wiki文件。 • 每个测试方法后,注册wiki变量;查找要运行的wiki文件;清除wiki变量。
其他模块的监听器 • 数据库管理监听器。 • Inject模块监听器。 • Spring bean堆栈和SQL log记录监听器。
jTester中的断言 对java assert的思考?
Java Assert的2种用法 • assert <boolean表达式> 如果<boolean表达式>为true,则程序继续执行。 如果为false,则程序抛出AssertionError,并终止执行。 • assert <boolean表达式> : <错误信息表达式> 如果<boolean表达式>为true,则程序继续执行。 如果为false,则程序抛出java.lang.AssertionError,并输入<错误信息表达式>。 jTester的断言本质上就是java 的assert断言。凡是jTester断言可以做的事情,java assert都可以做,无非是提供了便利性。
即时生效的断言 这条语句是立即判断alibabaAddress这个对象的性质的,任何一个性质的不符合都将抛出AssertionError,终止测试的运行。 • want.type(你要断言的值).assert(你希望这些值拥有哪些的性质?) • 举例:
延后生效的断言 定义断言器的时候并没有发生真正的assert行为,只有到程序中调用到mock api是才发生断言。 • the.type().assert(你希望这些值拥有哪些的性质?) 注意:这里并没有传你的期望值进去。只是描述了你的期望值要有的性质。 • 举例:
jTester断言的结构分析 • matche 判断实际值是否符合性质(item) 对于即时生效的断言是马上运行的。 对于延后生效的断言是允许到对应的api时才触发。 • describeMismatch 如果不符合,返回给用户的错误信息的描述。
Hamcrest jTester断言的结构分析 UnitilsPropertyMatcher Contains StartsWith ……
几个复杂对象判断的示例 • 判断pojo对象的属性。 • 判断pojo对象集合。 • 判断map对象。
SpringModule解析 • @SpringApplicationContext 加上这个属性,spring加载将会忽略不存在的bean,提高测试的健壮性
SpringModule解析 jTester根据TestCase @SpringApplicationContext内容 • @SpringBeanByName • @SpringBeanByType • @SpringBean(value=“bean name”) 初始化spring容器 Spring容器 到目前为止,spring bean的注入是单向的。即框架启动spring容器,初始化好spring bean,然后根据@SpringBeanByName等标注将对应的bean从容器中取出,注入到testcase中。 jTester框架从spring容器中将对应的bean取出来注入到TestCase中
SpringModule解析 AutoBeanInject定义了查找bean实现类的规则 • @AutoBeanInject 定义了基本的公用的spring bean jTester框架根据@SpringBeanByName等标注和规则,在spring容器初始化前定义对应字段的bean定义。 此时phoneBookService等bean在spring容器中并没有定义。 jTester框架初始化spring容器 现在,@SpringBeanByName等作用是双向的。 1、往spring容器中定义bean。 2、从spring容器中取出bean。 jTester框架从spring容器中将对应bean取出注入到TestCase中。
@AutoBeanInject的说明 是否启动自动注入(提供一个开关,disabled自动注入) 自动注入的规则@BeanMap 数组 例子规则说明: 这里**表示package的一部分,* 表示classname的一部分。第一个BeanMap规则表示,接口类的package+ “.impl”是实现类的package,第二个BeanMap表示实现类的package就是接口类的package。 二个规则中:接口类的classname+ “Impl” 就是实现类的classname。 可以有任意个** 和 *,但要是可分割的。比如 intf = **.intf.**.*Intf* impl = **.impl.**.*Impl.* 根据依赖字段的属性名称或者字段类型的package,显式的排除依赖的查找。 是否忽略根据规则查找不到的属性依赖。 False:如果查找不到属性依赖,抛出错误。 True:如果查找不到属性依赖,打印一条消息,继续。
jTester如何做到动态注册Bean • 动态注册@SpringBeanFor类型的bean • 动态注册@AutoBeanInject类型的bean
如果我有一个实现类 adfa.dadf.edd.ImdOll和接口类eiu.e384d.adf.IntfId之间根本毫无规则可言怎么办?
@SpringBeanByName详解 • value-bean的名称,为空则取当前字段的名称,双向作用。 • claz – 注册bean实现时的具体实现类,单向作用。
@SpringBeanFor的原理 @SpringBeanFor MyInterface myInterface; 触发动态创建Bean fieldName 告诉spring容器bean的名称 记录@SpringBeanFor字段的名称 type 告诉spring容器bean的类型 用于创建代理类 Spring Bean 工厂类 2个字段,fieldName和type 动态获取@SpringBeanFor的bean的值 Spring容器中的bean只是一个代理类,真正执行对象是测试类中的Field。
@SpringBeanFor的作用 @SpringBeanFor PhoneBookService phoneBookService; 发生什么? @SpringBeanFor @Mocked MyInterface myInterface; Spring bean的实际对象是个Mock对象。 Spring Bean的实际对象是用户new出来的。 @SpringBeanFor MyInterface myInterface = new MyImplement();
Vs. @SpringBeanByName • 二者在重载方法的效果上是一样的,但他们有什么区别吗? • @SpringBeanByName 是否可以做到@SpringBeanFor的效果呢?
如何在Spring加载时做初始化? • 大部分情况下ResourceManager是被mock的,但如果就是要测试ResourceManager该怎么处理?
Inject模块解析 • Targets 表示要将字段值注入到那些对象中。 • Properties 表示字段值注入到target对象的那个属性中,如果为空,则默认按字段名称注入。
Inject模块解析 • 示例
DbFit模块解析 Wiki数据 • Wiki格式 |…|…| • 基于表格驱动 • 以html方式呈现结果 • @DbFit(when=“”,then=“”) Html格式 HTML表格中数据驱动 Html结果呈现
DbFit模块解析 • 容易犯的错误 没有connect |connect| 表格错位 |Insert|table_name| |field1|field2| |value1|value2| |commit| 表格格式不全 |data|data|data
DbFit模块解析 • DbFitwiki文件中可使用的命令基本上都定义在DatabaseFixture这类中。
在DbFit中使用变量 • 动态使用变量 • 内置变量 @{date} 当前日期 yyyy-MM-dd @{datetime} 当前时间 yyyy-MM-dd HH:mm:SS @{space} 空格
jTester中的事务 • DatabaseModule.Transactional.value.default=commit • @Transactional(TransactionMode.ROLLBACK) • 在DbFit的wiki文件中显式的|commit| 或|rollback| • api中抛出了Spring事务定义中回滚的异常。 此时,在test结束commit或rollback都会导致测试错误,解决办法。 TransactionMode.DISABLED
Mock的技术原理 • Java Proxy:java 提供的接口代理机制。 • Asm、cglib的作用:在classloader的时候静态改变class的字节码。 • JDK5 java agent:class loader的时候动态修改class 的行为。 • JDK6 java agent:可以做到class loader后动态改变class的行为。
Mock的使用 • 工具类DateUtil
Mock的使用 • 问题:私有方法的mock • 关键:new MockUp<T>针对的是class实现,因此这种方式无法mock接口。如果一个方法是在父类中定义的,那么应mock父类,而不是子类。 • 所有的mock方法的限定词都是public,而不管原方法是private, public, static还是final.
实现类的静态mock • 工具类DateUtil
接口类的静态mock • 工具类DateUtil
Mock的使用 • 基于行为的测试 • @Mocked / @NonStrict • new Expectations() / new NonStrictExpectations() • 为什么我已经mock了这个接口调用到还会报错? • 为什么我没有调用到这个接口也会报错?
Mock的使用 • 参照官方文档 http://code.google.com/p/jmockit/ • jTester文档及ppt
jTester内置的反射调用方法 • reflector • 调用方法 • 调用静态方法 • 获取/设置变量 • 获取/设置静态变量