Создайте несколько наборов параметров в одном параметризованном классе (junit)
В настоящее время мне нужно создать параметризованный тестовый класс для каждого метода, который я хочу протестировать с несколькими различными входами. Есть ли способ добавить это вместе в один файл?
Прямо сейчас есть CalculatorTestAdd.java
, который имеет набор параметров, которые используются для проверки правильности работы функции Add()
. Есть ли возможность для меня "подключить" этот набор к функции Add()
и создать дополнительный набор, предназначенный для метода Subtract()
, и добавить этот метод в один и тот же тестовый класс, в результате чего будет создан один файл с именем CalculatorTest.java
?
Ответы
Ответ 1
Да. Вам нечего делать. Для каждого набора значений параметров каждый метод @Test запускается один раз, поэтому просто один метод test add() и другой метод test subtract().
Могу ли я также добавить, что человек, который диктует это требование, ошибочен. Не имеет большого значения диктует определенные шаблоны проектирования "для всех случаев" - может также нанять обученных обезьян.
Ответ 2
Этот ответ похож на Tarek one (параметризованная часть), хотя я думаю, что он немного расширяем. Также решает вашу проблему, и у вас не будет неудачных тестов, если все будет правильно:
@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;
}
}
- Не использовать параметризованный бегун
Это отлично работает, если вы программно задаете свои параметры.
Плюсы. У вас может быть столько тестов, сколько вы хотите, без необходимости устанавливать огромный набор параметров.
Минусы: больше кодирования, и он останавливается при первом сбое (что может быть не con).
@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;
}
}
- Использование JUnitParams
У меня нет отношения к прагматикам, я только что нашел это несколько дней назад. Эта структура работает поверх JUnit и обрабатывает параметризованные тесты по-разному. Параметры передаются непосредственно методу тестирования, поэтому вы можете иметь в одном классе различные параметры для разных методов.
Плюсы: достигает тех же результатов, что и решения выше, без обходных решений.
Минусы. Возможно, ваша компания не позволяет добавлять новую зависимость к проекту или заставляет использовать какое-то странное правило кодирования (например, использование исключительно определенных параметров). Давайте посмотрим правде в глаза, это произойдет больше, чем мы хотели бы.
Здесь прекрасный пример JUnitParams в действии, и вы можете получить проект/проверить код на этом Github.
Ответ 4
Вы можете использовать параметры с 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 - но это кажется излишним для тестов калькулятора
Ответ 5
Другое чистое JUnit, но все же элегантное решение, на мой взгляд, заключается в том, чтобы инкапсулировать каждый параметризованный тест в их собственный внутренний статический класс и использовать встроенный модуль запуска теста в классе тестирования верхнего уровня. Это позволяет вам не только использовать разные значения параметров для каждого теста независимо друг от друга, но и тестировать методы с совершенно разными параметрами.
Вот как это будет выглядеть:
@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
, которую я считаю более читабельной. Но это больше вопрос вкуса.
Ответ 6
Что ж, теперь 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));
}
}
Ответ 7
С Юнитом Юпитером: 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) );
}
}
Ответ 8
Я использую 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());
}
}