我正在学习如何使用 JUnit 5 在 Kotlin 中编写测试。当我编写 Java 时@Nested
,我喜欢使用 、@ParametrizedTest
等功能。@MethodSource
但是当我切换到 Kotlin 时,我遇到了一个问题:
我想出了如何使用
@Nested
通过在运行 gradle 测试时未找到嵌套 Kotlin 类中引用此JUnit 测试@ParametrizedTest
并@MethodSource
参考适用于 Kotlin 开发人员的 JUnit 5 - 第 4.2 节但当我把这些放在一起时,我得到了
这里不允许使用伴随对象。
在内部类里面。
测试重现错误
internal class SolutionTest {
@Nested
inner class NestedTest {
@ParameterizedTest
@MethodSource("testCases")
fun given_input_should_return_expected(input: Int, expected: Int) {
// assert
}
// error in below line
companion object {
@JvmStatic
fun testCases(): List<Arguments> {
return emptyList()
}
} …
Run Code Online (Sandbox Code Playgroud) 我很好奇在 Kotlin 中,在 Junit5 的参数化测试中,我是否可以使用类外的方法作为 @MethodSource。
我知道在 Kotlin 中使用 @MethodSource 的 2 种方法 - 伴随对象和 @TestInstance(TestInstance.Lifecycle.PER_CLASS)。我想知道是否可以通过不同的方式来完成,例如通过在类之外声明一个方法并使用一些注释?我试图这样做,但它不起作用,我想知道是否可以做类似的事情。
class GenerationStatusTest {
@ParameterizedTest
@MethodSource("provideStatusesToTest")
internal fun shouldStatusesHaveExpectedAbilities(generationStatus: GenerationStatus, assertions:(GenerationStatus)->Unit) {
assertions(generationStatus)
}
}
fun provideStatusesToTest(): Stream<Arguments> {
return Stream.of(
Arguments.of(WAITING, canDoNothing),
Arguments.of(PROCESSING, canDoNothing)
)
}
Run Code Online (Sandbox Code Playgroud)
org.junit.platform.commons.JUnitException: Could not find factory method [provideStatusesToTest] in class [com.test.enums.GenerationStatusTest]
at org.junit.jupiter.params.provider.MethodArgumentsProvider.lambda$getMethod$4(MethodArgumentsProvider.java:83)
Run Code Online (Sandbox Code Playgroud) 首先,它们在 Junit 5 动态测试指南的结论中是什么意思?
参数化测试可以替代本文中的许多示例。但是,动态测试与参数化测试不同,因为它们支持完整的测试生命周期,而参数化测试不支持。
我浏览了JUnit 5 – 参数化测试,相信我理解了语法级别的差异,并且相信我明白了这一点:
此外,动态测试在如何生成输入和如何执行测试方面提供了更大的灵活性。
但是看起来,为什么有人更喜欢参数化测试而不是动态测试?
我正在尝试构建一个小的 pytest 测试,以确保 redis 中存在所有预期的键。我有一个预期键列表,我将其存储为 YML 文件。测试本身将查询 redis 以确保列表中的每个预期键都存在。
最初,我将此设置为test_keys.py
文件中的一个巨大列表。这是这样设置的:
expected_keys = ['key1','key2','key3']
@pytest.mark.parametrize('expected_key', expected_keys)
def test_expected_key(expected_key):
...
Run Code Online (Sandbox Code Playgroud)
这有效。由于我想为 redis 环境的其他一些检查复制这种类型的测试,因此我不想将包含几百个键的多个列表放入这些文件中。
我想我可以将它们提取到 YML 文件中并通过装置加载键。
我的夹具看起来像这样:
@pytest.fixture
def expected_keys_fixture():
with open('expected_keys.yml'), 'r') as f:
return yaml.safe_load(f)['keys']
Run Code Online (Sandbox Code Playgroud)
YML 看起来像这样:
keys:
- key1
- key2
- key3
Run Code Online (Sandbox Code Playgroud)
我的测试装饰器更改为:
@pytest.mark.parametrize("expected_keys", [
(pytest.lazy_fixture('expected_keys_fixture'))
])
def test_expected_key(expected_key):
...
Run Code Online (Sandbox Code Playgroud)
我正在pytest-lazy-fixture
为此使用该软件包。
我在这里expected_keys
遇到的问题是现在等于预期键的整个列表。当静态列表在我的测试文件中时,它不再是每个单独的密钥。
我试图按照 Oleh Rybalchenko 的回答建议去做
@pytest.mark.parametrize("expected_keys", pytest.lazy_fixture('expected_keys_fixture')
)
def test_expected_key(expected_key):
...
Run Code Online (Sandbox Code Playgroud)
但是,这失败了TypeError: 'LazyFixture' object is not …
我无法确定它是否是一个缺失的功能,请参阅JUnit 问题 1154和我在那里的评论,或者只是我无法正确编写 JUnit5 中的名称语法@ParameterizedTest
。鉴于这个问题已经开放了三年,恐怕我等不及得到答案,甚至无法等到那里的实施,所以我也想在这里问。
在我的示例 ParameterizedTest 中,我使用@MethodSource
,它返回一个整数数组流,我想仅使用这些数组的属性,而不是{arguments}
使用 时所有相同的属性{0}
。这里很难用语言解释,最好使用下面的代码示例。
package net.delphym.unittests;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
class DynamicTestCreationTestParam {
private static Stream<int[]> data() {
return Stream.of(new int[][] { {1, 2, 2}, {5, 3, 15}, {121, 4, 484} });
}
@DisplayName("Multiplication test")
@ParameterizedTest(name = "#{index} for {arguments}: {0} x {1} = {2}")
@MethodSource("data")
void testWithStringParameter(int[] data) {
MyClass tester = new …
Run Code Online (Sandbox Code Playgroud) 我正在尝试使用 Pytest 编写动态测试套件,其中测试数据保存在单独的文件中,例如 YAML 文件或 .csv。我想运行多个测试,所有这些测试都从同一个文件进行参数化。假设我有一个测试文件test_foo.py
,如下所示:
import pytest
@pytest.mark.parametrize("num1, num2, output", ([2, 2, 4], [3, 7, 10], [48, 52, 100]))
def test_addnums(num1, num2, output):
assert foo.addnums(num1, num2) == output
@pytest.mark.parametrize("foo, bar", ([1, 2], ['moo', 'mar'], [0.5, 3.14]))
def test_foobar(foo, bar):
assert type(foo) == type(bar)
Run Code Online (Sandbox Code Playgroud)
使用参数化装饰器,我可以在 pytest 中运行多个测试,并且按预期工作:
test_foo.py::test_addnums[2-2-4] PASSED
test_foo.py::test_addnums[3-7-10] PASSED
test_foo.py::test_addnums[48-52-100] PASSED
test_foo.py::test_foobar[1-2] PASSED
test_foo.py::test_foobar[moo-mar] PASSED
test_foo.py::test_foobar[0.5-3.14] PASSED
Run Code Online (Sandbox Code Playgroud)
但我想动态地参数化这些测试。我的意思是,我想将所有测试的测试数据写入一个单独的文件中,以便当我运行 pytest 时,它将应用我写入每个测试函数的所有测试数据。假设我有一个类似于以下内容的 YAML 文件:
test_addnums:
params: [num1, num2, output]
values:
- [2, 2, 4]
- [3, …
Run Code Online (Sandbox Code Playgroud) 当我使用夹具运行测试时,我想在运行时动态命名测试@pytest.mark.parametrize("value",values_list)
。例如:
values_list=['apple','tomatoes','potatoes']
@pytest.mark.parametrize("value",values_list)
def test_xxx(self,value):
assert value==value
Run Code Online (Sandbox Code Playgroud)
我想看到的最终结果是 3 个测试,名称如下:
测试苹果
测试番茄
测试土豆
我尝试查看 pytest 文档,但我还没有发现任何可能阐明这个问题的内容。
我有一个 pytest 测试,可以针对两个不同的数据库测试多个输入。我使用参数化标记两次来做到这一点:
@pytest.mark.parametrize(
"input_type",
[
pytest.param("input_1"),
pytest.param("input_2"),
],
)
@pytest.mark.parametrize(
"db_type",
[
pytest.param("db_type_1"),
pytest.param("db_type_2"),
],
)
Run Code Online (Sandbox Code Playgroud)
我所经历的只是当运行input_1
(db_type_2
例如)测试由于错误而失败但使用不同的数据库传递运行相同的输入时。input_1
我只想将和组合标记db_type_2
为 xfail,而所有其他组合不应标记为 xfail。我找不到如何做到这一点。
如果标记db_type_2
为 xfail:
@pytest.mark.parametrize(
"db_type",
[
pytest.param("db_type_1"),
pytest.param("db_type_2", marks=pytest.mark.xfail)
],
)
Run Code Online (Sandbox Code Playgroud)
所有输入都将失败,这不是我正在寻找的行为。有人可以帮我解决这个问题吗?
我有 pytest 测试,其结果可能取决于环境变量。我想测试它们的这个环境变量的多个值。
我只想有一个设置此环境变量的固定装置,但我希望能够为每个测试而不是每个固定装置配置这些值。
我该怎么做?
python environment-variables pytest python-3.x parametrized-testing