现在有一个名为
CalculatorTestAdd.java
的文件,其中包含一组参数,用于检查Add()
函数是否正常工作。是否有可能将这个参数集“连接”到Add()
函数,并创建一个额外的参数集,用于Subtract()
方法,并将该方法添加到同一个测试类中,从而生成一个名为CalculatorTest.java
的文件?CalculatorTestAdd.java
的文件,其中包含一组参数,用于检查Add()
函数是否正常工作。是否有可能将这个参数集“连接”到Add()
函数,并创建一个额外的参数集,用于Subtract()
方法,并将该方法添加到同一个测试类中,从而生成一个名为CalculatorTest.java
的文件?@RunWith(Parameterized.class)
public class CalculatorTest {
enum Type {SUBSTRACT, ADD};
@Parameters
public static Collection<Object[]> data(){
return Arrays.asList(new Object[][] {
{Type.SUBSTRACT, 3.0, 2.0, 1.0},
{Type.ADD, 23.0, 5.0, 28.0}
});
}
private Type type;
private Double a, b, expected;
public CalculatorTest(Type type, Double a, Double b, Double expected){
this.type = type;
this.a=a; this.b=b; this.expected=expected;
}
@Test
public void testAdd(){
Assume.assumeTrue(type == Type.ADD);
assertEquals(expected, Calculator.add(a, b));
}
@Test
public void testSubstract(){
Assume.assumeTrue(type == Type.SUBSTRACT);
assertEquals(expected, Calculator.substract(a, b));
}
}
{ Type.SUBSTRACT, new ParamsSubstract(3,2), 1}, { Type.SQROOT, new ParamsSquareRoot(4), 2}});
Params 类继承自一个公共类 Params
,并作为构造函数的一部分:
public CalculatorTest(Type type, Params params, Double expected){..}
但这可能会使一切变得过于复杂,在那种情况下,我会考虑是否值得为所有内容创建一个类。 - nessa.gp在我看来,另一个纯JUnit但优雅的解决方案是将每个参数化测试封装在自己的静态内部类中,并在顶层测试类上使用Enclosed测试运行器。这不仅允许您独立地为每个测试使用不同的参数值,而且还可以使用完全不同参数的方法进行测试。
它会像这样:
@RunWith(Enclosed.class)
public class CalculatorTest {
@RunWith(Parameterized.class)
public static class AddTest {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ 23.0, 5.0, 28.0 }
});
}
private Double a, b, expected;
public AddTest(Double a, Double b, Double expected) {
this.a = a;
this.b = b;
this.expected = expected;
}
@Test
public void testAdd() {
assertEquals(expected, Calculator.add(a, b));
}
}
@RunWith(Parameterized.class)
public static class SubstractTest {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ 3.0, 2.0, 1.0 }
});
}
@Parameter(0)
private Double a;
@Parameter(1)
private Double b;
@Parameter(2)
private Double expected;
@Test
public void testSubstract() {
assertEquals(expected, Calculator.substract(a, b));
}
}
@RunWith(Parameterized.class)
public static class MethodWithOtherParametersTest {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ 3.0, 2.0, "OTHER", 1.0 }
});
}
private Double a;
private BigDecimal b;
private String other;
private Double expected;
public MethodWithOtherParametersTest(Double a, BigDecimal b, String other, Double expected) {
this.a = a;
this.b = b;
this.other = other;
this.expected = expected;
}
@Test
public void testMethodWithOtherParametersTest() {
assertEquals(expected, Calculator.methodWithOtherParametersTest(a, b, other));
}
}
public static class OtherNonParameterizedTests {
// here you can add any other test which is not parameterized
@Test
public void otherTest() {
// test something else
}
}
}
请注意在SubstractTest
中使用@Parameter
注解的用法,我认为这样更易于阅读。但这更多是品味问题。public class CalculatorTest{
public static int[][] dataSetForAdd() {
return new int[][] { { 1 , 2, 3 }, { 2, 4, 6 }, { 121, 4, 125 } };
}
public static int[][] dataSetForSubtract() {
return new int[][] { { 1 , 2, -1 }, { 2, 4, -2 }, { 121, 4, 117 } };
}
@ParameterizedTest
@MethodSource(value = "dataSetForAdd")
void testCalculatorAddMethod(int[] dataSetForAdd) {
Calculator calculator= new Calculator();
int m1 = dataSetForAdd[0];
int m2 = dataSetForAdd[1];
int expected = dataSetForAdd[2];
assertEquals(expected, calculator.add(m1, m2));
}
@ParameterizedTest
@MethodSource(value = "dataSetForSubtract")
void testCalculatorAddMethod(int[] dataSetForSubtract) {
Calculator calculator= new Calculator();
int m1 = dataSetForSubtract[0];
int m2 = dataSetForSubtract[1];
int expected = dataSetForSubtract[2];
assertEquals(expected, calculator.subtract(m1, m2));
}
}
@MethodSource
已经不再具有字段names
,而只有字段value
。 - Datz我确定你不再遇到这个问题,但是我想到了三种方法可以解决此问题,每种方法都有其优缺点。使用Parameterized runner, 您将不得不使用一个变通方法。
如果您必须从外部加载参数,则只需添加一个期望结果的参数。
优点: 编码较少,且它运行所有测试。
缺点: 针对每组不同的测试需要新的参数。
@RunWith(Parameterized.class)
public class CalculatorTest extends TestCase {
private Calculator calculator;
private int operator1;
private int operator2;
private int expectedSum;
private int expectedSub;
public CalculatorTest(int operator1, int operator2, int expectedSum, int expectedSub) {
this.operator1 = operator1;
this.operator2 = operator2;
}
@Params
public static Collection<Object[]> setParameters() {
Collection<Object[]> params = new ArrayList<>();
// load the external params here
// this is an example
params.add(new Object[] {2, 1, 3, 1});
params.add(new Object[] {5, 2, 7, 3});
return params;
}
@Before
public void createCalculator() {
calculator = new Calculator();
}
@Test
public void addShouldAddTwoNumbers() {
assertEquals(expectedSum, calculator.add(operator1, operator2));
}
@Test
public void subtractShouldSubtractTwoNumbers() {
assertEquals(expectedSub, calculator.subtract(operator1, operator2));
}
@After
public void endTest() {
calculator = null;
operator1 = null;
operator2 = null;
expectedSum = null;
expectedSub = null;
}
}
如果您通过编程设置参数,那么这是有效的。
优点: 您可以拥有任意数量的测试而无需设置大量的参数。
缺点: 需要编写更多代码,并且它会在第一个失败处停止(这可能不是缺点)。
@RunWith(JUnit4.class)
public class CalculatorTest extends TestCase {
private Calculator calculator;
@Before
public void createCalculator() {
calculator = new Calculator();
}
@Test
public void addShouldAddTwoNumbers() {
int[] operator1 = {1, 3, 5};
int[] operator2 = {2, 7, 9};
int[] expectedResults = {3, 10, 14};
for (int i = 0; i < operator1.length; i++) {
int actualResult = calculator.add(operator1[i], operator2[i]);
assertEquals(expectedResults[i], actualResult);
}
}
@Test
public void subtractShouldSubtractTwoNumbers() {
int[] operator1 = {5, 8, 7};
int[] operator2 = {1, 2, 10};
int[] expectedResults = {4, 6, -3};
for (int i = 0; i < operator1.length; i++) {
int actualResult = calculator.subtract(operator1[i], operator2[i]);
assertEquals(expectedResults[i], actualResult);
}
}
@After
public void endTest() {
calculator = null;
}
}
我与Pragmatists没有关联,几天前刚发现这个框架。该框架在JUnit基础上运行,以不同的方式处理参数化测试。参数直接传递到测试方法中,因此你可以在同一个类中为不同的方法提供不同的参数。
优点: 可以达到与上述解决方案相同的结果,而无需额外解决方案。
缺点: 也许您的公司不允许向项目添加新依赖项,或者强制您使用某些奇怪的编码规则(例如仅使用Parameterized runners)。让我们面对现实吧,这种情况比我们想象的要多。
您可以使用参数与https://github.com/piotrturski/zohhak:
@TestWith({
"1, 7, 8",
"2, 9, 11"
})
public void addTest(int number1, int number2, int expectedResult) {
BigDecimal result = calculator.add(number1, number2);
assertThat(result).isEqualTo...
}
如果你想从文件中加载参数,可以使用 http://code.google.com/p/fuzztester/ 或 http://code.google.com/p/junitparams/
如果你需要真正的灵活性,可以使用JUnit的@Parameterized,但它会使你的代码变得混乱。 另外,你也可以使用JUnit的Theories,但这对于计算器测试来说似乎有些过度。
assume*
一组方法来省略对这些方法的参数运行。然后,它们将被计为“跳过”,就像使用@Ignore一样(但它们可以运行其他参数)。 - Paŭlo Ebermannimport intf.ICalculator;
public class Calculator implements ICalculator {
@Override
public int plus(int a, int b) {return a + b; }
@Override
public int minuis(int a, int b) {return a - b;}
@Override
public int multy(int a, int b) {return a * b;}
@Override // check in junit byZero
public int divide(int a, int b) {return a / b;}
}
测试类:
import static org.junit.Assert.assertEquals;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class CalculatorJupiter5Test {
Calculator calculator = new Calculator();
@DisplayName("Should calculate the correct sum")
@ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}")
@CsvSource({
"5, 3, 8",
"1, 3, 4",
"6, 6, 12",
"2, 3, 5"
})
void sum(int a, int b, int sum) {
assertEquals(sum, calculator.plus(a, b) );
}
@DisplayName("Should calculate the correct multy")
@ParameterizedTest(name = "{index} => a={0}, b={1}, multy={2}")
@CsvSource({
"5, 3, 15",
"1, 3, 3",
"6, 6, 36",
"2, 3, 6"
})
void multy(int a, int b, int multy) {
assertEquals(multy, calculator.multy(a, b) );
}
@DisplayName("Should calculate the correct divide")
@ParameterizedTest(name = "{index} => a={0}, b={1}, divide={2}")
@CsvSource({
"5, 3, 1",
"14, 3, 4",
"6, 6, 1",
"36, 2, 18"
})
void divide(int a, int b, int divide) {
assertEquals(divide, calculator.divide(a, b) );
}
@DisplayName("Should calculate the correct divide by zero")
@ParameterizedTest(name = "{index} => a={0}, b={1}, divide={2}")
@CsvSource({
"5, 0, 0",
})
void divideByZero(int a, int b, int divide) {
assertThrows(ArithmeticException.class,
() -> calculator.divide(a , b),
() -> "divide by zero");
}
@DisplayName("Should calculate the correct minuis")
@ParameterizedTest(name = "{index} => a={0}, b={1}, minuis={2}")
@CsvSource({
"5, 3, 2",
"1, 3, -2",
"6, 6, 0",
"2, 3, -1"
})
void minuis(int a, int b, int minuis) {
assertEquals(minuis, calculator.minuis(a, b) );
}
}
junitparams
,它允许我在每个测试中传递不同的参数集。JunitParams 使用方法返回参数集,在测试中,您提供方法名称作为参数输入的来源,因此更改方法名称将更改数据集。import com.xx.xx.xx.Transaction;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@RunWith(JUnitParamsRunner.class)
public class IpAddressValidatorTest {
private Validator validator;
@Before
public void setUp() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
public static List<String> goodData() {
return Arrays.asList(
"10.10.10.10",
"127.0.0.1",
"10.136.182.1",
"192.168.1.1",
"192.168.1.1",
"1.1.1.1",
"0.0.0.0"
);
}
public static List<String> badData() {
return Arrays.asList(
"01.01.01.01",
"255.255.255.256",
"127.1",
"192.168.0.0"
);
}
@Test
@Parameters(method = "goodData")
public void ipAddressShouldBeValidated_AndIsValid(String ipAddress) {
Transaction transaction = new Transaction();
transaction.setIpAddress(ipAddress);
Set<ConstraintViolation<Transaction>> violations = validator.validateProperty(transaction, "ipAddress");
assertTrue(violations.isEmpty());
}
@Test
@Parameters(method = "badData")
public void ipAddressShouldBeValidated_AndIsNotValid(String ipAddress) {
Transaction transaction = new Transaction();
transaction.setIpAddress(ipAddress);
Set<ConstraintViolation<Transaction>> violations = validator.validateProperty(transaction, "ipAddress");
assertFalse(violations.isEmpty());
}
}
@DataProvider
正好符合你的需求。 - fge