español - Parametrización con la matriz en Junit 5(u otra biblioteca Java de prueba) de manera más inteligente
junit java ejemplos (5)
El uso de una combinación de Junit Parameterized Tests y YAML Parsing puede ser algo a considerar.
@RunWith(Parameterized.class)
public class AnotherParameterizedTest {
private final HashMap row;
@Parameterized.Parameters(name="Reverse Lists Tests # {index}:")
public static List<Map<String, Object>> data() {
final TestData testData = new TestData(""+
"| ID | List | Expected | /n"+
"| 0 | [1, 2, 3] | [3, 2, 1] | /n"+
"| 1 | [2, 3, 5] | [3, 2, 1] | /n"+
"| 2 | [5, 6, 7] | [ 7, 6, 5] | /n"
);
// parsing each row using simple YAML parser and create map per row
return testData.getDataTable();
}
// Each row from the stringified table above will be
// split into key=value pairs where the value are parsed using a
// yaml parser. this way, values can be pretty much any yaml type
// like a list of integers in this case.
public AnotherParameterizedTest(HashMap obj) {
this.row = obj;
}
@Test
public void test() throws Exception {
List orgListReversed = new ArrayList((List) row.get("List"));
Collections.reverse(orgListReversed);
assertEquals((List) row.get("Expected"), orgListReversed);
}
}
En lugar de usar una cadena, estoy usando un lector de Excel para hacer lo mismo con tablas simples de Excel. Analizando cada fila en un mapa usando YAML para los valores.
Resultados de la prueba Junit IDE
Lo mismo que se acaba de probar con Junit Jupiter da mejores resultados en el IDE Runner.
import static org.junit.jupiter.api.Assertions.assertEquals;
import de.deicon.yatf.runner.dsl.TestData;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class FirstTest {
@ParameterizedTest
@MethodSource("testTable")
public void test(Map row){
List reversedList = (List) row.get("List");
Collections.reverse(reversedList);
assertEquals((List)row.get("Expected"), reversedList);
}
static List<Map<String, Object>> testTable() {
return new TestData(""+
"|ID| List |Expected | /n"+
"|0 | [1,2,3] | [3,2,1] | /n"+
"|1 | [hans, peter, klaus] | [klaus, peter, hans] | /n"
).getDataTable();
}
}
Estoy tratando de parametrizar esta prueba:
@Test
public void reverseQuote(double[] qsp) throws Exception {
...}
Me parece absurdo que no exista algún método rápido para inicializar la matriz qsp
como, por ejemplo, ValueSource
:
@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testWithValueSource(int argument) {
assertNotNull(argument);
}
mi objetivo es hacer algo como @ValueSource(doublesArray = {new double[]{1.0, 2.0, 3.0}})
(que ahora devuelve un error). ¿No existe nada que permita algo similar? Otras respuestas parecen sugerir solo formas elaboradas, como usar @MethodSource
o @ConvertWith
.
También acepto respuestas implementando otras bibliotecas de prueba.
La documentación de JUnit sugiere usar @MethodSource
@ParameterizedTest
@MethodSource("range")
void testWithRangeMethodSource(double argument) {
assertNotEquals(9.0, argument);
}
static DoubleStream range() {
return DoubleStream.range(0.0, 20.0);
}
De lo contrario, podría considerar usar esto: https://junit.org/junit5/docs/5.0.2/api/org/junit/jupiter/params/provider/ValueSource.html#doubles
@ParameterizedTest
@ValueSource(doubles = { 1, 2, 3 })
public void test(double numberToTest){
//do whatever
}
Me gusta usar Spock para probar el código Java. Es un marco de prueba basado en groovy que se encuentra en la parte superior de JUnit 4. Las pruebas parametrizadas en Spock son una característica integrada:
def "The reverseQuote method doesn''t return null"(double[] qsp) {
when: "reverseQuote is called"
double[] rev = reverseQuote(qsp)
then: "the result is not null"
null != rev
where: "there are various input values"
qsp << [
[0.1, 0.2, 0.3] as double[],
[1.0, 2.0, 3.0] as double[]
]
}
... alternativamente, puede presentar sus datos de prueba en forma tabular:
def "The reverseQuote method reverses the input array"(List qsp, List expected) {
when: "reverseQuote is called"
double[] rev = reverseQuote(qsp as double[])
then: "the result is the reverse of the input"
expected as double[] == rev
where: "there are various input values"
qsp | expected
[0.1, 0.2, 0.3] | [0.3, 0.2, 0.1]
[1.0, 2.0, 3.0] | [3.0, 2.0, 1.0]
}
Tenga en cuenta que la prevalencia de as double[]
es una consecuencia desafortunada de que Groovy convierta automáticamente las matrices a listas, por lo que tenemos que devolverlas explícitamente en los casos particulares en los que interactuamos con el código Java que realmente requiere una matriz.
Ok, esta será una respuesta rara, pero funciona y fue algo divertido de hacer.
Lo primero: tu camino es imposible. No debido a JUnit o cualquier API relacionada, sino a Java: elementos de tipo de anotación válidos (los argumentos de anotación solo pueden ser primitivos, Cadena, Clase, Enumeración, otra anotación y matriz de todos ellos).
Lo segundo: podemos sortear el primero. Mira esto:
@ArraySources(
arrays = {
@ArraySource(array = {1, 2, 3}),
@ArraySource(array = {4, 5, 6}),
@ArraySource(array = {7, 8, 9})
}
)
Como dice, la anotación puede tener otras anotaciones como argumentos y matrices de esos, así que estamos usando esas 2 reglas aquí.
Tercera cosa: ¿cómo ayuda eso? Podemos agregar nuestro propio proveedor de anotación + argumento, JUnit 5 es expansible de esa manera.
Ambas anotaciones:
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(ArrayArgumentsProvider.class)
public @interface ArraySources {
ArraySource[] arrays();
}
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ArraySource {
int[] array() default {};
}
Proveedor de argumentos basado en las anotaciones:
public class ArrayArgumentsProvider implements ArgumentsProvider, AnnotationConsumer<ArraySources> {
private List<int[]> arguments;
public void accept(ArraySources source) {
List<ArraySource> arrays = Arrays.asList(source.arrays());
this.arguments = arrays.stream().map(ArraySource::array).collect(Collectors.toList());
}
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return this.arguments.stream().map(Arguments::of);
}
}
Y la prueba final usando esos:
public class ArraySourcesTest {
@ParameterizedTest
@ArraySources(
arrays = {
@ArraySource(array = {1, 2, 3}),
@ArraySource(array = {4, 5, 6}),
@ArraySource(array = {7, 8, 9})
}
)
void example(int[] array) {
System.out.println(Arrays.toString(array));
System.out.println("Test Over");
}
}
/* Output
[1, 2, 3]
Test Over
[4, 5, 6]
Test Over
[7, 8, 9]
Test Over
*/
Usted mencionó @MethodSource
como complicado, bueno, así que creo que fallé en este asunto, pero funciona. Se podría simplificar y mejorar obviamente (como nombrar los argumentos de anotación como valores predeterminados, valor, y solo lo hice para que int
mostrara la idea). No estoy seguro si podría lograr lo mismo con las características existentes ( ArgumentsProvider
y ArgumentSources
), pero esto parece más específico (usted sabe que está trabajando con matrices) y muestra posibilidades de extender JUnit5, puede ser útil en otro caso.
Puedes revisar TestNg (lo estoy usando en mi proyecto). Ejemplo en mi proyecto puede marcar aquí ingresar la descripción del enlace aquí o abajo:
@DataProvider(name = "processText")
public Object[][] dataProvider() {
return new Object[][]{
new Object[]{"ala/nma/nkota", "grep ma", "ma"},
new Object[]{"ala/nma/nkota", "grep -v ma", "ala/nkota"},
new Object[]{"ala/nma/nkota", "cut -c1-3", "ala/nma/nkot"},
//...
new Object[]{"ala ma kota", "sed s/ma/XX/g", "ala XX kota"},
new Object[]{"ala/nma/nkota", "grep -v ma | sed s/a/G/g", "GlG/nkotG"},
};
}
@Test(dataProvider = "processText")
public void testProcessText(String text, String cli, String expected) {
final String actual = new UnixProcessing().processText(text, cli);
assertEquals(actual, expected);
}
La documentación oficial de TestNg está here