可测试应用架构设计(二)
在可测试应用架构设计(一)中有介绍过 Booster 是如何解决 Transformer
在本地单元测试环境和编译环境中的复用问题,在本节中,我们来一起探索如何利用 Booster 提供的 TransformerClassLoader
来解决应用架构的可测试性问题。
单元测试框架
在 Java 的世界里,最流行的单元测试框架非 JUnit 和 TestNG 莫属了,但对于国内的大多数开发者来说,可能只听说过 JUnit,对 TestNG 并不熟悉,在我看来,二者之间并没有太大的区别。很多人对于用 JUnit 写单元测试是那么的熟悉而又陌生,说起来都知道,但正儿八经写过单元测试的人是凤毛麟角,我们先来看看用 JUnit 如何写单元测试:
1 | public class Calculator { |
1 | import static org.junit.Assert.assertEquals; |
在 IDE 中运行 evaluatesExpression
默认就会启动 JUnit,而如果要对测试的类进行 Mock 的话,就需要用到 Mocking 框架,例如:Mockito 或者 PowerMock。在用这些 Mocking 框架写 UT 时,或多或少的用到 JUnit 里的一个组件 —— Test Runner,通常是通过 @RunWith
来标注测试用例类,例如:
MockitoJUnitRunner
1 |
|
PowerMockRunner
1 |
|
而为运行在 Android 平台上的 Java Library 工程写 Local Unit Test,通常会用到 Robolectric,和 Mockito / PowerMock 一样,它也提供了 Test Runner —— RobolectricTestRunner
,如下所示:
1 |
|
Mocking 的本质
对于这些具有 Mocking 能力的框架,都不可避免的需要用到 Test Runner,为什么呢?
要 Mock 别的类中的属性或者方法,就需要对真实的属性或者方法进行偷梁换柱,什么时机偷换?怎么偷换?一般会有两种时机 —— 编译时或者运行时
编译时注入
对于普通的 Java 工程,编译时注入一般需要用到从 Java 6 开始提供的 Instrumentation 机制,通过 Java Agent 将包含有 ClassFileTransformer
的 JAR 包以插件的形式通过命令行参数提供给 JVM 的:
1 | java -javaagent:my-agent.jar -jar my-app.jar |
虽然这样也能实现,对于开发者来说,需要在 IDE 运行环境里配置命令行参数,开发体验不是很友好。
运行时注入
运行时注入一般需要用到自定义 ClassLoader
,例如:前面提到的 TransformerClassLoader
,通过字节码操作框架在加载 class 的时候在内存中对 class 进行修改替换,通过调研发现,前面的几个 Mocking 框架都是在运行时注入的。
Test Runner 的意义
JUnit 提供了 @RunWith
注解,让开发者可以使用指定的 Runner
来运行单元测试用例,而且,JUnit 还提供了很多内置的 Runner
:
BlockJUnit4ClassRunner
BlockJUnit4ClassRunnerWithParameters
Suite
- …
要实现运行时注入,就需要自定义一个 Runner
,为了实现方便,我们可以继承自 BlockJUnit4ClassRunner
:
1 | class BoosterTestRunner(clazz: Class<*>) : BlockJUnit4ClassRunner(clazz) { |
那如何才能在运行标注有 @Test
方法时执行 BoosterTestRunner
呢?我们来看一下 BlockJUnit4ClassRunner
到底是如何运行的:
至此,我们已经搞清楚了 JUnitTestClassExecutor
的运行时序,既然要在每个被 @Test
标注的方法执行前做点啥,那我们要做的便是重写 BlockJUnit4ClassRunner
的 methodBlock(FrameworkMethod)
方法,将 FrameworkMethod
引用的 Method
替换成修改后的 Method
。
修改后的
Method
从哪里来呢?
当然是从修改后的
Class
中获得,如下所示:
1 | override fun methodBlock(method: FrameworkMethod): Statement { |
采用 BoosterTestRunner
后,整个单元测试用例的调用时序则如下所示:
- 本文链接:https://johnsonlee.io/2021/12/18/testable-app-architecture-design-2/
- 版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。