一篇文章让你了解基于Spring的测试

Spring测试模块可以结合单元测试框架,对一般桌面应用和Web应用进行测试。针对两种不同类型应用的测试,Spring对应的有通用测试框架和MVC Web的测试框架。使用Spring测试框架,应用上下文可以一次性加载并进行缓存,大大提高了测试的速度。更方便的是其提供了很多便捷的注解辅助测试。

Spring测试模块对单元测试的支持

==================

Spring对单元测试的支持主要有两个方面:提供了多种类型的模拟对象和提供了用于单元测试的一些共用方法。

Spring测试模块的模拟类

==============

对于简单的POJO和服务端的类和方法,可以使用Mock框架模拟创建,但对于复杂的状况,Mock框架不易处理。Spring提供了更高层级的类的模拟,包含:

1、环境的模拟

Java中的Property是以键值对方式存储的数据类型,PropertyResolver是Spring提供的属性解析器,可以通过key查找对应的值,Environment继承自PropertyResolver,但是额外提供了Profile特性,即可以根据不同环境(比如:开发、测试和正式环境)得到相应数据。MockEnvironment就是对Environment的模拟,可以在测试时设置需要的属性键和值。使用的示例如下:

//代码方式的bean配置和注册

DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); //bean工厂

GenericBeanDefinition beanDefinition = new GenericBeanDefinition();//Bean定义

beanDefinition.setBeanClass(User.class); //设置bean定义类

beanDefinition.getPropertyValues().add(“name”, “${name}”) //设置bean属性;使用占位符

bf.registerBeanDefinition(“user”, beanDefinition); //注册bean

//模拟环境并设置属性后,对bean中的占位符进行替换

PropertySourcesPlaceholderConfigurer pc = new PropertySourcesPlaceholderConfigurer();

pc.setEnvironment(new MockEnvironment().withProperty(“name”, “Oscar”));//设置环境属性

pc.postProcessBeanFactory(bf); //替换占位符

上面的示例代码中,使用GenericBeanDefinition类进行Bean的配置和注册,对应的Bean类是User,使用占位符${name}设置该类的属性值。接着创建MockEnvironment和设置了属性name的值,最后设置PropertySourcesPlaceholderConfigurer的环境为创建的模拟环境并替换占位符为模拟环境中设置的属性值。

2、JNDI的模拟

JNDI(Java Naming and Directory Interface),Java命名与目录接口。直观点理解就是给资源(比如数据库资源)一个通用的名字,通过这个名字就可以查找这个资源了。

在开发和测试时,数据源可以通过配置url、name和password在XML中配置,但在正式环境中,为了保障安全,数据源更多的是使用JNDI的方式配置在应用服务器上,而且除了数据源以外的其他资源,类似EJB等就必须使用JNDI的方式了。Spring提供了根据JNDI名称查找资源对象的类JndiObjectFactoryBean,以JNDI:“java:comp/env/jdbc/mydatasource”为例,通过XML配置数据源bean的方式如下:

<!—JNDI属性定义

java:comp/env/jdbc/mydatasource

现在问题是:测试的时候不希望开启应用服务器,怎么找到JNDI对应的资源呢?Spring提供了使用SimpleNamingContextBuilder来构造JNDI资源的模拟。以上面配置的数据源的JNDI模拟为例:

public void initForTest() throws IllegalStateException, NamingException { //模拟JNDI方法

DriverManagerDataSource ds = new DriverManagerDataSource(); //创建数据源

ds.setDriverClassName(“com.mysql.cj.jdbc.Driver”); //驱动类设置

ds.setUrl(“jdbc:mysql://localhost:3306/ssmi?serverTimezone=UTC”);//数据源url

ds.setUsername(“root”); //用户名

ds.setPassword(“123456”); //密码

SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();

builder.bind(“java:comp/env/jdbc/mydatasource”, ds); //绑定数据源

builder.activate();//激活

}

以上通过DriverManagerDataSource创建一个数据源,使用SimpleNamingContextBuilder给该资源绑定一个JNDI的名字。以上方法可以使用@Before注解后,读取包含以上JNDI的配置文件就可以使用模拟的数据源了。DB及数据源相关的部分会在后面章节深入介绍。

3、HTTP和Web相关的模拟

Spring提供了Http和Servlet的模拟对象类用于Web测试,相关部分的会在后面Spring Web测试中介绍,另外,还提供了Spring Web Reactive的响应式 Web测试的模拟对象,本书不做探讨。

Spring测试模块的共用方法

===============

org.springframework.test.util包中的ReflectionTestUtils类,提供了基于反射机制的方法,可以修改变量、非公有的属性和访问非公有的方法,还可以调用生命周期回调方法。在基于Spring框架开发中,经常使用注解(包括@Autowired、@Inject和@Resource等)对私有的方法或属性进行依赖注入,这些私有的变量和方法不能直接获取和调用。举例来看,组件类Foo定义如下:

public class Foo { //组件类

@Autowired //自动装载注解

private String name; //字符串变量

@PostConstruct //组件初始化注解

private void onInit(){ //组件初始化回调方法

System.out.println("onInit… " + name);

}

@PreDestroy //组件销毁注解

private void onDestroy(){ //组件销毁方法回调

System.out.println("onDestroy… " + name);

}

}

该组件类使用@Autowired注解了一个私有的属性name,@PostConstruct、@PreDestroy分别注解了两个私有的回调方法。这种代码风格是实际开发中常见的风格,可是在单元测试中不启动Spring容器,无法得到依赖对象也无法执行注解的生命周期回调方法下如何测试呢?答案就是通过ReflectionTestUtils,测试代码如下:

@Test //测试方法注解

public void test () { //测试方法

Foo foo = new Foo(); //组件创建

ReflectionTestUtils.setField(foo, “name”, “Oscar”); //设置私有变量的值

ReflectionTestUtils.invokeMethod(foo, “onInit”); //调用组件初始化方法

ReflectionTestUtils.invokeMethod(foo, “onDestroy”); //调用组件销毁方法

}

Spring测试框架

==========

Spring测试框架在JUnit和TestNG框架之上进行扩展。在测试类似使用JUnit的@RunWith(JUnit4)或@ExtendWith(JUnit5)指定Spring对应的运行器扩展,就可以在测试类中很容易进行上下文的初始化和缓存。通过测试执行监听器,可以在测试类中使用依赖注入和事务管理等注解,也可以自定义注解,简化测试。

Spring测试框架使用

============

使用Spring框架开发的应用,更常使用的是对容器初始化之后的测试,也就是严格意义上的集成测试。Spring测试框架主要位于org.springframework.test.context包中,提供了通用的、注解驱动和集成测试支持。其无缝集成了JUnit和TestNG的测试框架。

以JUnit4为例,在Spring测试框架下编写测试类的步骤如下:

  1. 在测试类上使用@RunWith注解测试运行器。

  2. 使用@ContextConfiguration注解指定Spring的配置(可以是XML配置文件,也可以是配置类)。

  3. 装载需要的Bean并完成JUnit标签@Test注解的测试方法。

完整的代码实例如下:

@RunWith(SpringJUnit4ClassRunner.class) //运行器注解

@ContextConfiguration(locations=“classpath:cn/osxm/ssmi/chp6/applicationContext.xml”)//配置

public class SpringTest { //测试类

@Autowired //自动装载注解

private HelloService helloService;

@Test //测试方法注解

public void hello() { //测试方法

helloService.sayHello();

}

}

@RunWith是JUnit的注解,用于标注测试运行器类。Spring扩展了JUnit4的BlockJUnit4ClassRunner类, 额外实现了容器的测试上下文的初始化和维护(JUnit5使用的是@ExtendWith)。初始化容器依据的配置通过注解@ContextConfiguration指定。在测试类中也可以使用Spring容器依赖注入等(类似@Autowired)注解。这些类在使用Maven批量执行时,应用上下文(容器)会缓存,不需要重复创建,节省测试开销,加快了测试效率。

Spring测试框架原理

============

Spring提供的测试运行器SpringJUnit4ClassRunner会创建测试上下文管理类TestContextManager,TestContextManager主要维护测试上下文TestContext和测试执行的监听TestExecutionListener,TestContext维护了根据配置初始化的容器应用上下文ApplicationContext和测试类、方法、和异常等信息,TestExecutionListener则是测试类和方法执行前后的一些操作,这几个类的关系如图2所示。

图2 Spring测试框架的核心类及关系

对上面的测试框架的核心类说明如下:

  • TestContextManager,测试上下文管理器类,在每次测试时都会创建。提供对测试上下文(TestContext) 实例的管理,还负责测试过程中更新TestContext的状态并代理到TestExecutionListener,用来监测测试的执行,在测试执行点向每个注册的TestExecutionListener发送信号事件。

  • TestContext,测试上下文类,封装测试执行的上下文。提供访问容器和应用上下文的能力,并对applicationContext进行缓存。

  • TestExecutionListener,测试执行监听器,与TestContextManager发布的测试事件进行交互,这个监听器就是注册到TestContextManager上的。提供依赖注入、事务管理等能力。

除了上面的核心类之外,还有以下一些重要的类:

  • ContextLoader:负责根据配置加载 Spring 的 Bean 定义,以构建 applicationContext 实例对象。ContextLoader是Spring 2.5中引入的一个策略接口,用于为Spring TestContext Framework管理的集成测试加载ApplicationContext。

  • SmartContextLoader:Spring 3.1中引入的ContextLoader接口的扩展。可以选择处理资源位置,带注释的类或上下文初始值设定项。支持按照profile加载。

@TestExecutionListeners注解使用在测试类上,用于指定需要的测试执行监听器。测试执行监听器用于实现类执行之前被执行或实现类的测试方法之前被执行测试执行之前进行一些额外的处理。Spring默认提供了TestExecutionListener的三个实现,包括:事务管理的TransactionalTestExecutionListener、依赖注入的DependencyInjectionTestExecutionListener和上下文检查的DirtiesContextTestExecutionListener。

  • TransactionalTestExecutionListener:事务测试执行监听器,用于对事务进行管理,负责解析@Transaction、@NotTransactional 以及 @Rollback 等事务注解的注解。@Transaction 注解让测试方法工作于事务环境中。可以使用 @Rollback(false) 让测试方法返回前提交事务。而@NotTransactional 注解则可以让测试方法不工作于事务环境中。此外,还可以使用类或方法级别的 @TransactionConfiguration 注解改变事务管理策略。

  • DependencyInjectionTestExecutionListener:依赖注入测试执行监听器,该监听器提供了自动注入的功能,它负责解析测试用例类中的@Autowried注解并完成依赖对象自动注入。

  • DirtiesContextTestExecutionListener:脏上下文测试执行监听器,一般情况下测试方法并不会对Spring容器上下文造成破坏(比如改变Bean的配置信息等),如果某个测试方法确实会破坏Spring容器上下文,可以显式地为该测试方法添加 @DirtiesContext注解,以便Spring TestContext在测试该方法后刷新Spring容器的上下文,DirtiesContextTestExecutionListener监听器的工作就是解析 @DirtiesContext注解。

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

最后

小编精心为大家准备了一手资料

以上Java高级架构资料、源码、笔记、视频。Dubbo、Redis、设计模式、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术

【附】架构书籍

  1. BAT面试的20道高频数据库问题解析
  2. Java面试宝典
  3. Netty实战
  4. 算法

BATJ面试要点及Java架构师进阶资料

手资料

[外链图片转存中…(img-Y3LvclQJ-1710405812449)]

[外链图片转存中…(img-qK10hkv1-1710405812449)]

以上Java高级架构资料、源码、笔记、视频。Dubbo、Redis、设计模式、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术

【附】架构书籍

  1. BAT面试的20道高频数据库问题解析
  2. Java面试宝典
  3. Netty实战
  4. 算法

[外链图片转存中…(img-LKd1PYco-1710405812450)]

BATJ面试要点及Java架构师进阶资料

[外链图片转存中…(img-CJvg5kpN-1710405812450)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值