目前,我必须为每个我想用几个不同输入测试的方法创建一个参数化测试类。有没有办法在一个文件中添加它?
现在有CalculatorTestAdd.java
,它有一组参数,用于检查Add()
功能是否正常工作。我是否有可能将这个集合“连接”到Add()
函数并为Subtract()
方法创建一个额外的集合,并在同一个测试类中添加此方法,从而生成一个名为CalculatorTest.java
的文件?
是。你不需要做什么特别的事。对于参数的每组值,每个@Test方法运行一次,因此只需要一个方法测试add()和另一个方法测试subtract()。
我还要补充一点,指明这一要求的人是误入歧途的。在“适用于所有情况”中规定某些设计模式几乎没有价值 - 不妨雇用经过培训的猴子。
这个答案类似于Tarek的一个(参数化部分),尽管我认为它更具有可扩展性。也可以解决您的问题,如果一切正确,您将不会失败测试:
@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));
}
}
我相信你不再有这个问题了,但我想到了你可以做到的3种方法,每种方式都有其优点和缺点。使用参数化跑步者,您将不得不使用变通方法。
如果必须在外部加载参数,只需为预期结果添加参数即可。
优点:减少编码,并运行所有测试。
缺点:每组不同测试的新参数。
@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;
}
}
我与实用主义者没有任何关系,我几天前就发现了这一点。该框架在JUnit之上运行,并以不同方式处理参数化测试。参数直接传递给测试方法,因此您可以在同一个类中为不同的方法使用不同的参数。
优点:实现与上述解决方案相同的结果,无需解决方法。
缺点:也许您的公司不允许您向项目添加新的依赖项或强制您使用某些奇怪的编码规则(例如仅使用参数化运行程序)。让我们面对它,它发生的比我们想要的更多。
你可以使用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的理论 - 但它似乎对计算器测试来说太过分了
在我看来,另一个纯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
}
}
}
请注意@Parameter
中SubstractTest
注释的用法,我认为它更具可读性。但这更多的是品味问题。
那么,现在JUnit-5为您提供了一个解决方案 - 通过重新定义编写参数化测试的方法。现在可以使用@ParameterizedTest在方法级别定义参数化测试,并且可以使用@MethodSource为方法源提供参数化测试。
因此,在您的情况下,您可以使用2个单独的数据源方法,为同一类中的add()和subtract()测试方法提供输入数据。你的代码应该是这样的:
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(names = "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(names = "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));
}
}
与Junit Jupiter:https://www.petrikainulainen.net/programming/testing/junit-5-tutorial-writing-parameterized-tests/
import 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());
}
}