junit测试起名字规则
最近发布的JUnit 5(又名JUnit Lambda) alpha发行版引起了我的兴趣,在浏览文档时,我注意到规则以及运行程序和类规则都消失了。 根据文档,这些部分竞争的概念已被单个一致的扩展模型取代。
多年来, Frank和我写了一些规则来帮助执行重复性任务,例如测试SWT UI , 忽略某些环境中的测试 , 注册(测试)OSGi服务 , 在单独的线程中运行测试等等。
因此,我对将现有规则转换为新概念以使它们可以在JUnit 5上本地运行特别感兴趣,为了探索扩展的功能,我选择了两个特性完全不同的规则,并尝试将其迁移到JUnit 5 。
这些实验的重点是查看规则和扩展之间的概念已发生了变化。 因此,我选择重写JUnit 4意味着不考虑向后兼容性。
如果您有兴趣从JUnit 4迁移到5或探索在JUnit 5中运行现有规则的可能性,则可能需要参加各自的讨论。
第一个候选对象是ConditionalIgnoreRule ,它与@ConditionalIgnore批注一起使用。 该规则评估需要用注释指定的条件,并据此确定是否执行测试。
另一个候选者是内置的TemporaryFolder规则 。 顾名思义,它允许创建在测试完成时删除的文件和文件夹。
因此,它在测试执行之前和之后挂接,以创建一个根目录以在其中存储文件和文件夹并清理该目录。 另外,它提供了实用程序方法来在根目录中创建文件和文件夹。
扩展说明
在详细介绍向扩展的迁移规则之前,让我们简要了解一下新概念。
测试执行遵循一定的生命周期。 可以延长生命周期的每个阶段都由一个接口表示。 扩展可以实现某些阶段的兴趣,因为它们实现了相应的接口。
使用ExtendWith
批注,测试方法或类可以表示它在运行时需要特定的扩展。 所有扩展都有一个公共的超级接口: ExtensionPoint
。 ExtensionPoint
的类型层次结构列出了扩展当前可以挂接到的所有位置。
例如,下面的代码应用了一个虚构的MockitoExtension
来注入模拟对象:
@ExtendWith(MockitoExtension.class)
class MockTest {@MockFoo fooMock; // initialized by extension with mock( Foo.class )
}
MockitoExtension
将提供一个默认的构造函数,以便它可以在运行时实例化并实现必要的扩展接口,以便能够将@Mock
注入到所有@Mock
注释的字段中。
条件忽略
规则的重复模式是提供带有注释的串联服务,该注释用于标记和/或配置希望使用该服务的测试方法。 在这里,ConditionalIgnoreRule检查其运行的所有测试方法,并寻找ConditinalIgnore批注。 如果找到这样的注释,则评估其条件,如果满足,则忽略测试。
这是ConditionalIgnoreRule实际运行的样子:
@Rule
public ConditionalIgnoreRule rule = new ConditionalIgnoreRule();@Test
@ConditionalIgnore( condition = IsWindowsPlatform.class )
public void testSomethingPlatformSpecific() {// ...
}
现在,让我们看一下代码在JUnit 5中的外观:
@Test
@DisabledWhen( IsWindowsPlatform.class )
void testSomethingPlatformSpecific() {// ...
}
首先,您会注意到注释已更改其名称。 为了匹配使用术语Disabled而不是忽略的JUnit 5约定,该扩展还将其名称更改为DisabledWhen
。
尽管DisabledWhen注释是由DisabledWhenExtension驱动的,但是没有任何东西表明需要扩展。 其原因被称为元注释,并且在查看DisabledWhen的声明方式时可以最好地说明它们:
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(DisabledWhenExtension.class)
public @interface DisabledWhen {Class<? extends DisabledWhenCondition> value();
}
注释(元)带有处理它的扩展名注释。 在运行时,JUnit 5测试执行器负责其余的工作。 如果遇到带注释的测试方法,并且该注释又由ExtendWith
元注释,则实例化各个扩展并将其包含在生命周期中。
真的很整洁吗? 在不指定相应规则的情况下注释测试方法时,此技巧还可以避免疏忽。
在幕后, DisabledWhenExtension
实现了TestExexutionCondition
接口。 对于每个测试方法,都将调用其唯一的evaluate()
方法,并且必须返回一个ConditionEvaluationResult
,该ConditionEvaluationResult
确定是否应该执行测试。
其余代码与以前基本相同。 查找并找到DisabledWhen
注释时,将创建指定条件类的实例,并询问是否应执行测试。 如果执行被拒绝,则返回一个禁用的ConditionEvaluationResult
,并且框架将相应地执行操作。
临时文件夹
在将TemporaryFolder规则转换为异常之前,让我们看一下该规则的组成。 首先,该规则将在测试设置和拆卸期间进行设置并清理一个临时文件夹。 但是,它还为测试提供了访问在该根文件夹内创建(临时)文件和文件夹的方法的权限。
迁移到扩展后,不同的职责变得更加明显。 以下示例显示了如何使用它:
@ExtendWith(TemporaryFolderExtension.class)
class InputOutputTestprivate TemporaryFolder tempFolder;@Testvoid testThatUsesTemporaryFolder() {File file = tempFolder.newFile();// ...}
}
TemporaryFolderExtension
挂接到测试执行生命周期中,以提供和清除临时文件夹,并为所有TemporaryFolder
字段提供此类实例。 而TemporaryFolder
允许访问在根文件夹中创建文件和文件夹的方法。
为了注入TemporaryFolder
,该扩展实现了InstancePostProcessor
接口。 创建测试实例后立即调用其postProcessTestInstance
方法。 在该方法中,它可以通过TestExtensionContext
参数访问测试实例,并且可以将TemporaryFolder
注入所有匹配的字段中。
对于一个类声明多个TemporaryFolder
字段的极少数情况,每个字段都被分配一个新实例,并且每个实例都有其自己的根文件夹。
在此过程中创建的所有注入的TemporaryFolder
实例都保存在一个集合中,以便稍后进行清理时可以对其进行访问。
要在执行测试后进行清理,需要实现另一个扩展接口: AfterEachExtensionPoint
。 每次测试完成后,将调用其唯一的afterEach
方法。 并且此处的TemporaryFolderExtension
实现清除了所有已知的TemporaryFolder
实例。
现在我们可以与TemporaryFolder
规则的功能相提并论,现在还可以支持新功能:方法级依赖注入。
在JUnit 5中,现在允许方法具有参数。
这意味着我们的扩展程序不仅应该能够注入字段,而且还应该能够注入TemporaryFolder
类型的方法参数。 希望创建临时文件的测试可以请求注入TemporaryFolder
如以下示例所示:
class InputOutputTest {@Test@ExtendWith(TemporaryFolderExtension.class)void testThatUsesTemporaryFolder( TemporaryFolder tempFolder ) {File file = tempFolder.newFile();// ...}
}
通过实现MethodParameterResolver
接口,扩展可以参与解析方法参数。 对于测试方法的每个参数,都会调用扩展的supports()
方法来确定它是否可以为给定参数提供值。 对于TemporaryFolderExtension
,实现将检查参数类型是否为TemporaryFolder
并在这种情况下返回true
。 如果需要更广泛的上下文,则当前方法调用上下文和扩展上下文还提供了supports()
方法。
现在,该扩展程序决定支持某个参数,它的resolve()
方法必须提供一个匹配的实例。 同样,提供了周围的环境。 TemporaryFolderExtension
仅返回一个唯一的TemporaryFolder
实例,该实例知道(临时)根文件夹并提供在其中创建文件和子文件夹的方法。
但是请注意,声明无法解析的参数被视为错误。 因此,如果遇到没有匹配解析器的参数,则会引发异常。
在扩展中存储状态
您可能已经注意到, TemporaryFolderExtension
保持其状态(即,它已创建的临时文件夹的列表),当前是一个简单字段。 尽管测试表明这确实可行,但是文档中没有地方指出在调用不同扩展名时都使用同一实例。 因此,如果JUnit 5此时更改其行为,则在这些调用期间状态可能会丢失。
好消息是,JUnit 5提供了一种维护称为Store
的扩展状态的方法。 正如文档所述,它们为扩展提供了保存和检索数据的方法 。
该API与简化Map
的API类似,并且允许存储键值对,获取与给定键关联的值以及删除给定键。 键和值都可以是任意对象。 可以通过将TestExtensionContext
作为参数传递给每个扩展方法(例如beforeEach
, afterEach
)来到达存储。每个TestExtensionContext
实例都封装了正在执行当前测试的上下文 。
例如,在beforeEach
,值将存储在扩展上下文中,如下所示:
@Override
public void beforeEach( TestExtensionContext context ) {context.getStore().put( KEY, ... );
}
以后可以像这样检索:
@Override
public void afterEach( TestExtensionContext context ) {Store store = context.getStore();Object value = store.get( KEY );// use value...
}
为了避免可能发生的名称冲突,可以为某些命名空间创建存储。 上面使用的context.getStore()
方法获取默认名称空间的存储。 要获取特定命名空间的商店,请使用
context.getStore( Namespace.of( MY, NAME, SPACE );
名称空间是通过对象数组{ MY, NAME, SPACE }
在此示例中。
返还TemporaryFolderExtension
以使用Store
的练习留给读者。
运行代码
- 可以在以下GitHub存储库中找到此处讨论的两个扩展的扩展实现: https : //github.com/rherrmann/junit5-experiments
该项目设置为在安装了Maven支持的Eclipse中使用。 但是在具有Maven支持的其他IDE中编译和运行代码并不难。
很自然,在这种早期状态下,尚不支持直接在Eclipse中运行JUnit 5测试。 因此,要运行所有测试,您可能需要使用“使用ConsoleRunner运行所有测试”启动配置。 如果遇到麻烦,请参考我以前关于JUnit 5的文章中的“ 使用JUnit 5运行测试”部分,以获得更多提示或发表评论。
总结如何在JUnit 5中替换规则
在这个小小的实验过程中,我给人的印象是,扩展是JUnit 4中规则和朋友的不错且完全的替代品。最后,使用新方法很有趣,并且比现有功能更简洁。
如果您发现用扩展尚无法完成的用例,我相信如果让他们知道 JUnit 5团队将不胜感激。
但是请注意,截至撰写本文时,扩展程序仍在进行中 。 该API被标记为实验性的,如有更改,恕不另行通知。 因此,现在实际迁移JUnit 4帮助程序可能还为时过早-除非您不介意将代码调整为可能更改的API。
如果JUnit 5扩展引起了您的兴趣,您可能还需要继续阅读文档的相应章节 。
翻译自: https://www.javacodegeeks.com/2016/04/replace-rules-junit-5.html
junit测试起名字规则