Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[FR] Controlling the inclusion of the listener at runtime #2381

Closed
shaburov opened this issue Oct 7, 2020 · 9 comments · Fixed by #3088
Closed

[FR] Controlling the inclusion of the listener at runtime #2381

shaburov opened this issue Oct 7, 2020 · 9 comments · Fixed by #3088

Comments

@shaburov
Copy link
Contributor

shaburov commented Oct 7, 2020

An annotation Listeners is added to the class

@Listeners(IntellijIdeaTestNgPluginListener.class)
public abstract class BaseTest { }

Expected behavior

ITestNGListener extended/implemented IParameterizable should allow disabling the listener at runtime

public final class IntellijIdeaTestNgPluginListener 
        implements ITestNGListener, IParameterizable {

    public IntellijIdeaTestNgPluginListener() {
        if (this.getEnabled()) {
            JUtils.initConfigurationYml();
        }
    }

    @Override
    public boolean getEnabled() {
        return JUtils.isJetBrainsIdeTestNGPluginRun();
    }

    @Override
    public void setEnabled(boolean enabled) {}
}
@krmahadevan
Copy link
Member

krmahadevan commented Oct 7, 2020

@shaburov - I am not sure I quite understood the issue.

org.testng.annotations.IParameterizable is not a user facing interface (As in, its not a interface that you are expected to consume as a TestNG listener. A )

TestNG listener is essentially any TestNG interface that extends org.testng.ITestNGListener.

IParameterizable does not extend this interface. So when you try to wire in your IntellijIdeaTestNgPluginListener implementation, TestNG recognises this as some TestNG listener, but since it doesnt implement any of the user facing TestNG listeners, TestNG doesn't know what to do with this. This is the current behavior.

I realise that you are asking for a new feature. So before we tread down that path can you please explain what exactly are you trying to do here ?

@shaburov
Copy link
Contributor Author

shaburov commented Oct 7, 2020

@krmahadevan Hello!
IParameterizable works for annotations in terms of launch control.
Similar logic should be present for listeners.
If in runtime the condition for activating the listener is triggered, then the listener is considered active and is added to the pipeline.
My example shows this.
I need the listener to work only in the IDE and be disabled when launched from the console.
But my solution looks terrible! So I made a feature request so that it was like with annotations launch control.
Selection_012

@krmahadevan
Copy link
Member

@shaburov - Wouldn't a org.testng.IAnnotationTransformer work for you which is specifically meant to deal with annotations of TestNG ?

IParameterizable works for annotations in terms of launch control.

I still didnt understand this statement. AFAIK all the interfaces under org.testng.annotations are specifically meant to be used within TestNG so as to simulate as if attributes of annotations are being altered at runtime (which in the usual case is not possible in a straight forward way). So I am not able to understand/visualise as to what would be the usecase here.

@shaburov
Copy link
Contributor Author

shaburov commented Oct 8, 2020

I need the @listeners(IntellijIdeaTestNgPluginListener.class) to work only in the IDE and be disabled when launched from the console.

How I can do that? Do you have any idea?

@krmahadevan
Copy link
Member

You can do something like this:

  1. Define a JVM argument (which is by default enabled) and use that to determine if the listener IntellijIdeaTestNgPluginListener should be executed or skipped.
  2. Enhance your build file (Maven/Gradle) wherein you disable the JVM argument defined in (1).

That should take care of your requirement.

@juherr - WDYT ?

@juherr
Copy link
Member

juherr commented Oct 11, 2020

Like suite.xml or the TestNG api, @Listeners is just another way to specify listeners.

Another way for optional listeners could be something like:

@OptionalListeners(IntellijIdeaTestNgPluginListener.class)
@Listeners(OptionalListener.class)
public abstract class BaseTest { }

where OptionalListener will be responsible for instantiating and using listeners from @OptionalListeners.

@shaburov
Copy link
Contributor Author

My friends.
It is easier to call a special method after creating an instance of the listener and add the listener to suiteRunner based on the result of the method call (for example, boolean isEnable ()).

@krmahadevan
Copy link
Member

@shaburov - Would you be willing to submit a PR that shows us what you are proposing ?

The method interceptor or the annotation transformer exists for this very reason.

krmahadevan added a commit to krmahadevan/testng that referenced this issue Mar 9, 2024
@krmahadevan
Copy link
Member

Here's how this can be done.

Version: 7.9.0

Sample IAnnotationTransformer being used

import org.testng.IAnnotationTransformer;
import org.testng.annotations.IListenersAnnotation;

import java.util.Arrays;

public class SampleAnnotationTransformer implements IAnnotationTransformer {
    private static final String intellijClassName = "com.intellij.rt.testng.IDEARemoteTestNG";

    @SuppressWarnings("unchecked")
    @Override
    public void transform(IListenersAnnotation annotation, Class<?> testClass) {
        if (isRunningInIntelliJ()) {
            annotation.setValue(new Class[]{});
        }
    }
    private static boolean isRunningInIntelliJ() {
        return Arrays.stream(Thread.currentThread().getStackTrace())
                .map(StackTraceElement::getClassName)
                .anyMatch(intellijClassName::equalsIgnoreCase);
    }
}

The above annotation transformer checks if the current execution is from within IntelliJ by inspecting the current thread's stack trace and looking for the IntelliJ specific class in it. Once it finds it, it disables the listener values found in the @Listeners annotation. This sample can further be enhanced by also taking into account the testClass parameter and further filtering the enabling/disabling of listener.

This annotation transformer needs to be wired in via the service loading approach that TestNG supports.

This should now help us with dynamically enabling/disabling listeners that are being wired in ONLY via the @Listeners annotation. This approach WILL NOT work if the listeners are being wired in via

  • <listeners> tag (or)
  • via the service loaders

@krmahadevan krmahadevan added this to the 7.10.0 milestone Mar 9, 2024
krmahadevan added a commit to krmahadevan/testng that referenced this issue Mar 14, 2024
krmahadevan added a commit to krmahadevan/testng that referenced this issue Mar 14, 2024
krmahadevan added a commit to krmahadevan/testng that referenced this issue Mar 14, 2024
krmahadevan added a commit that referenced this issue Mar 14, 2024
# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants