Cucumber step definition with asterisk - java

We have feature files that have long tests that validate multiple things written in non-english language, the Given -> When -> Then structure doesn't make sense.
I tried replacing feature file keywords with *, and that works just fine, however here are the problems:
When writing a new step in a form of * Some step, and using Alt->Enter shortcut to generate a step definition, IntelliJ IDEA does... Nothing. It only opens the file where I wanted to put the definition without any added code. I've updated IDE and plugins to be latest.
There doesn't seem to be any way to have a "universal" annotation to use for asterisk steps, only Given, When, Then, And, But exists. It's not very logical to have a * Some step feature and #Given("Some step") definition.
Is there any workaround that I might use?

I think you can achieve this with few coding steps.
Say you have the scenario like this:
Feature: Generic annotation
Scenario: Testing annotations
* having asterix
* use custom generic annotation
Add custom annotation to your sources
package click.webelement.cucumber;
import io.cucumber.java.StepDefinitionAnnotation;
import io.cucumber.java.StepDefinitionAnnotations;
import org.apiguardian.api.API;
import java.lang.annotation.*;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#StepDefinitionAnnotation
#Documented
#Repeatable(MyStep.MySteps.class)
#API(status = API.Status.STABLE)
public #interface MyStep {
String value();
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#StepDefinitionAnnotations
#Documented
#interface MySteps {
MyStep[] value();
}
}
Now use it in your Step Definition
package click.webelement.cucumber;
public class StepDef {
#MyStep("having asterix")
public void doOne(){
System.out.println("Running having asterix");
}
#MyStep("use custom generic annotation")
public void doTwo(){
System.out.println("Running use custom generic annotation");
}
}
UPD
In order to make your Idea plugin work with your custom annotation you need to place that annotation to
io.cucumber.java.LANG package where LANG is the sub-package for your chosen language.
To make everything work as default you place it to
io.cucumber.java.en

Related

java class.getAnnotation(Class<?> class) returns null

I have written an annotation:
#Documented
#Retention(RUNTIME)
#Target(TYPE)
/**
* #author christian
*
*/
public #interface HeraklesObject {
}
I use it in an osgi environment (eclipse oxygen with tycho) in a bundle.
In another bundle (a test fragment to be specific) i check classes from the first bundle for this annotation, using:
class.getAnnotation(HeraklesObject.class)
I am sure, that the queried class has this annotation, but I get always null result.
The retention policy seems right to me, but I am not sure if a class loader issue is present and I don't know how I check this. Or do you see another cause? Thanks
Christian
Try to use
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE })
Also from How to get class annotation in java?
use
Class newClass = X.class;
for (Annotation annotation : newClass.getDeclaredAnnotations()) {
System.out.println(annotation.toString());
}
Does the bundle with the class whose runtime annotations you want to check contain or import the package with the HeraklesObject annotation class?
A class loader will load a class and, if the class of a runtime annotation class is not visible, just ignore the annotation. So you end up with a silent "failure". So check to make sure the package holding the runtime annotation is visible to the bundle holding the annotated class.
If you use Bnd to build the bundle, it should properly import the runtime annotation's package.

Any annotation to mark a class as a testclass?

I am new to Junit4, I am wondering if there is some annotations to mark a class as a test class just like using
#Test to mark a method as a test method.
You can use #Category annotation at class level, like:
#Category({PerformanceTests.class, RegressionTests.class})
public class ClassB {
#Test
public void test_b_1() {
assertThat(1 == 1, is(true));
}
}
I quoted this example from https://www.mkyong.com/unittest/junit-categories-test/
Also if you run Spring tests, Mockito test with JUnit, then you have to use #RunWith annotation at class level.
For example in Spring boot test I use this:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
public class ControllerTest {
In Mockito (without spring test) test I used:
#RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
Annotation #TestOnly, but it does come with a warning, as seen below.
import org.jetbrains.annotations.TestOnly;
WARNING
/**
* A member or type annotated with TestOnly claims that it should be used from testing code only.
* <p>
* Apart from documentation purposes this annotation is intended to be used by static analysis tools
* to validate against element contract violations.
* <p>
* This annotation means that the annotated element exposes internal data and breaks encapsulation
* of the containing class; the annotation won't prevent its use from production code, developers
* won't even see warnings if their IDE doesn't support the annotation. It's better to provide
* proper API which can be used in production as well as in tests.
*/
WORKAROUND
If there are bodies of code or classes that I use specifically for testing and need to remove them at release I add a custom TODO using Android Studio (Not sure if other IDEs have the same functionality), follow the screenshot below and in the TODO tab at the bottom, you will see a filter for each custom TODO on the left. This is by no means the best way to do it, but I find it the fastest manual way to remove code on release.
P.S I know the patterns are all sorts of messed up in this screenshot.

Java annotations fundamentals

I am a newbie to annotations. I have gone through a lot of tutorials explaining the concept of annotations. But nowhere do i find information about defining multiple annotations within a class. So pls give me some insight on defining and accessing multiple annotations.Below is the code where I define two annotations in a class and eclipse IDE presents me an error "The public type SampleAnn must be defined in its own file".. Is the reason for this error because of the java convention that "there should only one public annotation per class in the name of the class-name"?
#Documented
#Target(ElementType.LOCAL_VARIABLE)
#Inherited
#Retention(RetentionPolicy.RUNTIME)
public #interface MethodInfo{
int number1;
}
#Documented
#Target(ElementType.LOCAL_VARIABLE)
#Retention(RetentionPolicy.RUNTIME)
public #interface SampleAnn{
int number2;
}
You are right, you can have only a single top-level class in one file.
But what you can do:
public class MyAnnotations {
public #interface SampleAnn { ... }
public #interface MethodInfo { ... }
}
There should more generally be ONE public CLASS per class file and annotations are no exception. It is also important that any publicly defined entity has the same name as its java file's name, so I don't see how you could have two in the same file.
The annotations need to be in separate compilation units (files).
The regarding top-level classes the specification states:
This restriction implies that there must be at most one such type per
compilation unit. This restriction makes it easy for a Java compiler
to find a named class within a package. In practice, many programmers
choose to put each class or interface type in its own compilation
unit, whether or not it is public or is referred to by code in other
compilation units.
Specification

Are annotation class files needed in the runtime class path?

If a class is annotated with an annotation, does the definition of that annotation have to be in the runtime classpath in order to use the class? For example, given the annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface Component {}
can I execute the program
#Component
public class Test {
public static void main(String[] args) {
System.out.println("It worked!");
}
}
without having Component.class in the classpath? (In my test I could, but is this behavior defined by the spec?)
I ask because there are conflicting claims whether using an annotation from a library creates a dependency on that library.
Runtime annotations are meta information to be processed by annotation processor at the runtime. If there is an access to annotation at runtime, you definitely add annotations in the classpath. For example junit definitely need the annotations in the class path determine test methods.
If there is no processing of annotation is done, there is no need to have it the classpath.
Yes you can execute program without having Component.class in the classpath. More details here: Why doesn't a missing annotation cause a ClassNotFoundException at runtime?

Understanding annotation in Java

I was trying to go through some online material to learn annotation in java.
In the following code, what happened to my dear "Hello world" string which I passed in this line: #Test_Target(doTestTarget="Hello World !")?
#Target(ElementType.METHOD)
public #interface Test_Target {
public String doTestTarget();
}
above is the annotation defined and below is its usage
public class TestAnnotations {
#Test_Target(doTestTarget="Hello World !")
private String str;
public static void main(String arg[]) {
new TestAnnotations().doTestTarget();
}
public void doTestTarget() {
System.out.printf("Testing Target annotation");
}
}
When I run this code it is only printing Testing Target annotation
Please help me out, I am completely new to annotation.
Annotations are basically bits of data you can attach to fields, methods, classes, etc.
The syntax for declaring annotations in Java is a little awkward. They look a bit like interfaces (they are, after all, declared with #interface), but they aren't really interfaces. I think you might have put the doTestTarget() method in your TestAnnotations class because you thought your annotation was an interface and you needed to implement it. This isn't true - you can delete this method and the call to it from your code if you wish and doing so won't cause you any problems.
Also, you might not have intended to put the annotation on the field str. Annotations apply only to what immediately follows them. As a result, your code doesn't compile, because you've applied your annotation to a field but declared that your annotation can only be applied to methods. Change #Target(ElementType.METHOD) to #Target(ElementType.FIELD) and your code should then compile.
As for what happens to the string Hello World !, it gets written to the .class file and is available to any tool that reads in Java classes. However, it wouldn't necessarily be available in the JVM at runtime. This happens because you didn't specify a #Retention for your #Test_Target annotation. The default value for #Retention is RetentionPolicy.CLASS, which means that the JVM might not bother to load them out of the class file. (See the Javadoc for the RetentionPolicy enum.)
I imagine you want to see some way of reading the value out of this annotation at runtime. If so, I'd recommend adding #Retention(RetentionPolicy.RUNTIME) to your annotation to make sure it will be available at runtime.
To access your annotation and the value contained within it at runtime, you need to use reflection. I've rewritten your TestAnnotations class as follows to give a quick demonstration:
import java.lang.reflect.Field;
public class TestAnnotations {
#Test_Target(doTestTarget="Hello World !")
private String str;
public static void main(String[] args) throws Exception {
// We need to use getDeclaredField here since the field is private.
Field field = TestAnnotations.class.getDeclaredField("str");
Test_Target ann = field.getAnnotation(Test_Target.class);
if (ann != null) {
System.out.println(ann.doTestTarget());
}
}
}
When I run this code, it gives me the following output:
Hello World !
In principle, adding an annotation by itself does not fundamentally alter the programs behaviour.
In your case, you created a new annotation type #Test_Target, which can by used on any method (as indicated by its #Target annotation).
Then you applied this not to a method, but to the str field (which should give a compiler error, I think).
Independently of this, you are creating an object with a doTestTarget method, and invoke it, and get the expected result (i.e. the method is executed).
If you want your annotation to do something more than simply be there and provide some information for the reader of the source, you have to use it - either with an annotation processor at compile time, or using reflection on run time (then you would need also #Retention(RUNTIME) as an annotation on Test_Target.)
In the spirit of learning, another way is to use the annotated class without targeting a method or field.
First declare your interface with the method you need and Retention Policy to Runtime
import java.lang.annotation.*;
#Retention(RetentionPolicy.RUNTIME)
public #interface Test_Target {
public String doTestTarget() default "default string";
}
then annotate the interface created to your class. From your class find the annotated class and then call the method with it.
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
#Test_Target(doTestTarget="Hello World !")
public class TestAnnotations {
public static void main(String[] args) throws Exception
{
AnnotatedElement c = TestAnnotations.class;
if(c.isAnnotationPresent(Test_Target.class))
{
Annotation singleAnnotation = c.getAnnotation(Test_Target.class);
Test_Target tt = (Test_Target) singleAnnotation;
System.out.println(tt.doTestTarget());
}
}
}
the result is:
Hello World !

Categories