-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ability do define description at runtime #54
Comments
My use case:I would like to create such a test: @Test
@XrayTest(key = "XXXX-1000", summary = "Simple permissions test AAA, BBB, CCC")
@CreateUserWithPermissions({Perm.AAA, Perm.BBB, Perm.CCC}) // custom annotation - specifies the permissions of the user to be created before the test
@XrayTestGenerator(PermissionsDescriptionGenerator.class)
@ExpectedPermissionTestResult("""
* button A visible
* button B invisible
""") // custom annotation for expected behavior
void test1() {
// code here...
} CreateUserWithPermissions.java: @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CreateUserWithPermissions {
String[] value();
} ExpectedPermissionTestResult.java: @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExpectedPermissionTestResult {
String value();
} PermissionsDescriptionGenerator.java: public class PermissionsDescriptionGenerator implements XrayGenerator { // custom implementation of XrayGenerator
@Override
public String generateDescription(TestInfo testInfo) {
var permissions = testInfo.getTestMethod()
.map(m -> m.getAnnotation(CreateUserWithPermissions.class))
.map(CreateUserWithPermissions::value)
.map(List::of)
.orElse(List.of());
var result = new StringBuilder();
result.append("""
Some description...
Testing with user with permissions:
""");
permissions.forEach(perm -> {
result.append("* ").append(perm).append("\n");
});
result.append("Expected result:\n");
testInfo.getTestMethod()
.map(m -> m.getAnnotation(ExpectedPermissionTestResult.class))
.map(ExpectedPermissionTestResult::value)
.ifPresent(result::append);
return result.toString();
}
} this code will create description:
Such automation of the creation of descriptions will simplify their maintenance which becomes problematic with dozens of such similar tests |
This proposal may also help with #33 - we can generate custom description for each parameterized test... Hmm, we can even add the ability to generate a custom summary/key like this: interface XrayGenerator {
default String generateKey(TestInfo testInfo) {
return null;
}
default String generateSummary(TestInfo testInfo) {
return null;
}
default String generateDescription(TestInfo testInfo) {
return null;
}
} |
Hi @mk868 , thanks for your suggestion.
|
Hi,
// for this example I would like the results to be synced for 4 tests in Jira: ABC-100, ABC-101, ABC-102, ABC-103
private static Stream<Arguments> dataSource() {
return Stream.of(
// first argument: Jira key
// second argument: actual test parameter
of("ABC-100", "PRODUCT_TABLE"),
of("ABC-101", "ITEM_TABLE"),
of("ABC-102", "USERS_TABLE"),
of("ABC-103", "PRODUCT_TABLE")
);
}
@XrayTest
@ParameterizedTest
@MethodSource("dataSource")
void sortTableTest(String _key, String tableName) {
// _key param is ignored in the test code
// tableName is used in the code
// test code...
}
Or do you have any other suggestions? |
I was looking at JUnit's 5 |
From what I see Just thinking:We can also prepare an abstraction of reading public interface XrayTestInfoReader {
Optional<String> getId(TestIdentifier testIdentifier);
Optional<String> getKey(TestIdentifier testIdentifier);
Optional<String> getSummary(TestIdentifier testIdentifier);
Optional<String> getDescription(TestIdentifier testIdentifier);
} The default implementation (I simply copied that from the import app.getxray.xray.junit.customjunitxml.annotations.XrayTest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.TestFactory;
import org.junit.platform.commons.support.AnnotationSupport;
import org.junit.platform.engine.TestSource;
import org.junit.platform.engine.support.descriptor.MethodSource;
import org.junit.platform.launcher.TestIdentifier;
import java.lang.reflect.Method;
import java.util.Optional;
import java.util.stream.Stream;
import static java.util.function.Predicate.not;
public class DefaultXrayTestInfoReader implements XrayTestInfoReader {
@Override
public Optional<String> getId(TestIdentifier testIdentifier) {
final Optional<TestSource> testSource = testIdentifier.getSource();
final Optional<Method> testMethod = testSource.flatMap(this::getTestMethod);
Optional<XrayTest> xrayTest = AnnotationSupport.findAnnotation(testMethod, XrayTest.class);
return xrayTest
.map(XrayTest::id)
.filter(not(String::isEmpty));
}
@Override
public Optional<String> getKey(TestIdentifier testIdentifier) {
final Optional<TestSource> testSource = testIdentifier.getSource();
final Optional<Method> testMethod = testSource.flatMap(this::getTestMethod);
Optional<XrayTest> xrayTest = AnnotationSupport.findAnnotation(testMethod, XrayTest.class);
return xrayTest
.map(XrayTest::key)
.filter(not(String::isEmpty));
}
@Override
public Optional<String> getSummary(TestIdentifier testIdentifier) {
final Optional<TestSource> testSource = testIdentifier.getSource();
final Optional<Method> testMethod = testSource.flatMap(this::getTestMethod);
final Class testClass = ((MethodSource) testSource.get()).getJavaClass();
Optional<XrayTest> xrayTest = AnnotationSupport.findAnnotation(testMethod, XrayTest.class);
Optional<String> test_summary = xrayTest
.map(XrayTest::summary)
.filter(not(String::isEmpty));
if (test_summary.isPresent()) {
return test_summary;
}
Optional<DisplayName> displayName = AnnotationSupport.findAnnotation(testMethod, DisplayName.class);
if (displayName.isPresent()) {
return Optional.of(displayName.get().value());
}
Optional<TestFactory> dynamicTest = AnnotationSupport.findAnnotation(testMethod, TestFactory.class);
Optional<DisplayNameGeneration> displayNameGenerator = AnnotationSupport.findAnnotation(testClass, DisplayNameGeneration.class);
if (dynamicTest.isPresent() || displayNameGenerator.isPresent()) {
return Optional.of(testIdentifier.getDisplayName());
}
return Optional.empty();
}
@Override
public Optional<String> getDescription(TestIdentifier testIdentifier) {
final Optional<TestSource> testSource = testIdentifier.getSource();
final Optional<Method> testMethod = testSource.flatMap(this::getTestMethod);
Optional<XrayTest> xrayTest = AnnotationSupport.findAnnotation(testMethod, XrayTest.class);
return xrayTest
.map(XrayTest::description)
.filter(not(String::isEmpty));
}
protected Optional<Method> getTestMethod(final TestSource source) {
if (source instanceof MethodSource) {
return getTestMethod((MethodSource) source);
}
return Optional.empty();
}
protected Optional<Method> getTestMethod(final MethodSource source) {
try {
final Class<?> aClass = Class.forName(source.getClassName());
return Stream.of(aClass.getDeclaredMethods()).filter(method -> MethodSource.from(method).equals(source))
.findAny();
} catch (ClassNotFoundException e) {
//logger.error(e, () -> "Could not get test method from method source " + source);
}
return Optional.empty();
}
} Then Then user can customize the provider like that:import org.junit.jupiter.params.ParameterizedTest;
import org.junit.platform.commons.support.AnnotationSupport;
import org.junit.platform.engine.TestSource;
import org.junit.platform.launcher.TestIdentifier;
import java.lang.reflect.Method;
import java.util.Optional;
public class CustomXrayTestInfoReader extends DefaultXrayTestInfoReader {
@Override
public Optional<String> getKey(TestIdentifier testIdentifier) {
Optional<String> defaultKey = super.getKey(testIdentifier);
if (defaultKey.isPresent()) {
return defaultKey;
}
final Optional<TestSource> testSource = testIdentifier.getSource();
final Optional<Method> testMethod = testSource.flatMap(this::getTestMethod);
Optional<ParameterizedTest> parameterizedTest = AnnotationSupport.findAnnotation(testMethod, ParameterizedTest.class);
if (parameterizedTest.isPresent() && parameterizedTest.get().name().contains(":")) {
return Optional.of(testIdentifier.getDisplayName().split(":")[0]);
}
return Optional.empty();
}
@Override
public Optional<String> getSummary(TestIdentifier testIdentifier) {
Optional<String> defaultSummary = super.getSummary(testIdentifier);
if (defaultSummary.isPresent()) {
return defaultSummary;
}
final Optional<TestSource> testSource = testIdentifier.getSource();
final Optional<Method> testMethod = testSource.flatMap(this::getTestMethod);
Optional<ParameterizedTest> parameterizedTest = AnnotationSupport.findAnnotation(testMethod, ParameterizedTest.class);
if (parameterizedTest.isPresent() && parameterizedTest.get().name().contains(":")) {
return Optional.of(testIdentifier.getDisplayName().split(":")[1].trim());
}
return Optional.empty();
}
} And use in the tests like that: public class Play3Test {
private static Stream<Arguments> dataSource() {
return Stream.of(
// first argument: Jira key
// second argument: actual test parameter
of("ABC-100", "PRODUCT_TABLE"),
of("ABC-101", "ITEM_TABLE"),
of("ABC-102", "USERS_TABLE"),
of("ABC-103", "PRODUCT_TABLE")
);
}
@XrayTest
@ParameterizedTest(name = "{0}: Sorting {1} table")
@MethodSource("dataSource")
void sortTableTest(String _key, String tableName) {
// _key param is ignored in the test code
// tableName is used in the code
// test code...
}
} |
@mk868 I like that last approach you suggested; it seems clean and flexible. Do you want to make a PR for this? |
Sure, I'll prepare a PR soon |
thanks, I'm with a bunch of stuff right now. Please include also tests for this new behavior. |
So, I've prepared some initial implementation in PR, |
Thanks, will do. |
Note: there are 3 initial issues that were "my fault" as they're related with names of variables like "test_id", "test_summary"... |
|
thanks. I'll have a more in-depth look next week |
fixed on #55 |
Hello,
In my test code, I have a lot of descriptions most of which are repetitive and could be generated from the Java code.
I would expect something similar to the
@DisplayNameGeneration
from JUnit5 - the annotation that specifies what class will create a description string:This allows me to create an implementation of the
XrayGenerator
interface and define a description based on other test method annotations.What do you think about it?
The text was updated successfully, but these errors were encountered: