java - pruebas - junit tutorial español
Crear múltiples conjuntos de parámetros en una clase parametrizada(junit) (7)
Actualmente tengo que crear una clase de prueba parametrizada para cada método que quiero probar con varias entradas diferentes. ¿Hay una manera de agregar esto juntos en un archivo?
En este momento, existe CalculatorTestAdd.java
que tiene un conjunto de parámetros que se utilizan para verificar si la función Add()
funciona correctamente. ¿Tengo la posibilidad de ''conectar'' este conjunto a la función Add()
y crear un conjunto adicional destinado al método Subtract()
y agregar este método en la misma clase de prueba, lo que da como resultado un archivo llamado CalculatorTest.java
?
Bueno, ahora JUnit-5 le ofrece una solución para esto, al redefinir una forma de escribir pruebas parametrizadas. Ahora se puede definir una prueba parametrizada a nivel de método usando @ParameterizedTest y se le puede dar una fuente de método usando @MethodSource.
Por lo tanto, en su caso, puede tener 2 métodos de fuente de datos separados para proporcionar datos de entrada para los métodos de prueba add () y restar (), ambos en la misma clase. Su código debe ir algo como esto:
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));
}
}
En mi opinión, otra solución de JUnit pura pero elegante a la vez es encapsular cada prueba (s) parametrizada en su propia clase estática interna y usar el corredor de prueba Enclosed en la clase de prueba de nivel superior. Esto le permite no solo usar valores de parámetros diferentes para cada prueba de forma independiente, sino también métodos de prueba con parámetros completamente diferentes.
Así es como se vería:
@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
}
}
}
Tenga en cuenta el uso de la anotación @Parameter
en el SubstractTest
, que considero más legible. Pero esto es más una cuestión de gustos.
Esta respuesta es similar a la de Tarek (la parte parametrizada), aunque creo que es un poco más extensible. También resuelve su problema y no tendrá pruebas fallidas si todo es correcto:
@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));
}
}
Estoy seguro de que ya no tendrá este problema, pero pensé en tres formas de hacerlo, cada una con sus ventajas y desventajas. Con el corredor parametrizado, tendrás que usar una solución alternativa.
- Utilizando más parámetros con parametrizado.
En caso de que tenga que cargar los parámetros externamente, simplemente agregue un parámetro para los resultados esperados.
Pros : menos codificación, y ejecuta todas las pruebas.
Contras : nuevos parámetros para cada conjunto diferente de pruebas.
@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;
}
}
- No utilizar corredor parametrizado.
Esto funciona bien si configura sus parámetros programáticamente.
Pros : Puedes tener tantas pruebas como quieras sin tener que configurar un gran conjunto de parámetros.
Contras : Más codificación, y se detiene en el primer fallo (lo que podría no ser una estafa).
@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;
}
}
- Utilizando JUnitParams
No tengo ninguna afiliación con los pragmáticos, acabo de encontrar esto hace unos días. Este marco se ejecuta sobre JUnit y maneja las pruebas parametrizadas de manera diferente. Los parámetros se pasan directamente al método de prueba, por lo que puede tener en la misma clase diferentes parámetros para diferentes métodos.
Pros : logra los mismos resultados que las soluciones anteriores sin soluciones provisionales.
Contras : tal vez su compañía no le permita agregar una nueva dependencia al proyecto o lo obligue a usar alguna regla de codificación extraña (como usar los corredores parametrizados exclusivamente). Seamos realistas, sucede más de lo que nos gustaría.
Here un buen ejemplo de JUnitParams en acción, y puede obtener el proyecto / verificar el código en esta página de Github .
Sí. No hay nada especial que tengas que hacer. Para cada conjunto de valores de los parámetros, cada método @Test se ejecuta una vez, así que solo tiene un método test add () y otro método test restar ().
También puedo añadir que la persona que está dictando este requisito está equivocada. Hay poco valor en dictar ciertos patrones de diseño "para todos los casos", ya que también podría contratar monos entrenados.
puede usar parámetros con 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...
}
Si desea cargar parámetros desde un archivo, puede usar http://code.google.com/p/fuzztester/ o http://code.google.com/p/junitparams/
y si necesita flexibilidad real, puede usar @Parameterized de junit pero desordena su código. También puedes usar las Teorías de Junit, pero parece una exageración para las pruebas de calculadora.
Junit Jupiter
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;}
}
// https://www.petrikainulainen.net/programming/testing/junit-5-tutorial-writing-parameterized-tests/
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) );
}
}