How to create a BaseClass that adds logging messages - selenium

I am using serenity BDD for my automation testing and Page Object Model for my framework. I have created a BasePage class which will be inherited by all the other Pages. I want to minimize the logging messages from the Pages by adding all the log.info messages to a central Base page. Example, when calling the click() method, I will log before click and after click methods as shown below in the basePage class:
public class BasePage extends PageObject{
private static final Logger log = LogManager.getLogger(BasePage.class.getClass());
private final WebElementFacade element;
public static void clickBtn(WebElementFacade btnName) {
log.info("About to click " + btnName + " button");
btnName.click();
log.info("Successfully clicked on " + btnName + " button.");
}
Later I figured that instead of individually trying to figure out in advance what actions the user will perform on the webElements, and write new methods for each action (like the one shown above), just implement WebDriverFacade interface, so that I get all the unimplemented method list in BasePage from WebDriverFacade and then write the log messages inside each of them, like so:
public class BasePage extends PageObject implements WebElementFacade{
private static final Logger log = LogManager.getLogger(BasePage.class.getClass());
private final WebElementFacade element;
#Override
public void submit() {
// TODO Auto-generated method stub
}
#Override
public void sendKeys(CharSequence... keysToSend) {
// TODO Auto-generated method stub
}
#Override
public String getTagName() {
// TODO Auto-generated method stub
return null;
}
#Override
public boolean isSelected() {
// TODO Auto-generated method stub
return false;
}
.
.
.
.
.
}
This will serve two purposes:
I will not have to create new methods for every action in BasePage class, example the 'clickBtn()' function in the first code
As I mentioned before, I will not have to figure out what method any other person who adds methods to my code might use and having to change the BasePage class to create the new actions. So basically less maintenance in the long run.
The problem I am facing is an error that I receive in the second use case:
The return types are incompatible for the inherited methods WebElementFacade.withTimeoutOf(int, TimeUnit), PageObject.withTimeoutOf(int, TimeUnit)
Now my question is:
How can solve this problem?
Is this the right way to do things or should I be going with the first method and have maintenance overhead.
Just figured that another scenario where this might be useful. To make sure that subclass methods do not use methods from pageObject and are forced to use the methods from BaseClass only. This can be done by wrapping the WebElementFacade and adding the log messages as an added functionality. Any thought on this would be appreciated.
Thank you!

Honestly it is a neat trick and if you get it working you should be proud.
I think I figured something similar out in a dynamic language.
But you are better off just adding the logging entries and learning the following.
How to name functions so you don't feel like they need renaming.
How to log clearly for debugging use.
This is because loggings power is in its flexibility.
When you learn how to dump something complex like a matrix so you can eye it and go uh-oh you are increasing your overall skills.
I apologize for not giving you code but I felt some "chase the other rabbit" advice was better.

Related

How to access test method annotations from a TestExecutionListener

I'm trying to port Test Management For Jira JUnit Integration to JUnit5. This module generates a JSON report of the test run and associates the results with Jira tickets by using annotations on the test methods, example.
From the TestExecutionListener I'm not sure what the best approach to retrieve the TestCase annotation is.
I looked at Reflection using the TestIdentifier.getSource and doing manipulations to rebuild the method signature and extracting the annotation from there but the approach felt clumsy.
I came across this post Allow extensions to register TestExecutionListeners which proposed the following:
Proposal: Have your extension publish the WebDriver bean's session id, e.g.
String sessionId = ...;
extensionContext.publishReportEntry("webDriverSessionId", sessionId)
In your TestExecutionListener, implement reportingEntryPublished and store it in a Map with the TestIdentifier as a key. In executionFinished report the test outcome along with the value from this Map.
This approach looks promising but I want to make sure there isn't another way that doesn't require both an extension and a test execution listener. Is there a way to retrieve test method annotation information directly in the TestExecutionListener?
#Alex, the following might be used inside the listener...
((MethodSource) testIdentifier.source).javaMethod.getAnnotation(TestCase.class)
Seems like you can't get test annotation from TestExecutionListener but instead you can implement TestWatcher or e.g AfterEachCallback and get custom annotation value like that:
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestWatcher;
public class MyExtention implements TestWatcher, AfterEachCallback {
#Override public void testSuccessful(ExtensionContext context) {
if (context.getElement().isPresent() && context.getElement().get().isAnnotationPresent(MyCustomAnnotation.class)) {
int val = context.getElement().get().getAnnotation(MyCustomAnnotation.class).value();
// Report on success
}
}
#Override public void afterEach(ExtensionContext context) throws Exception {
if (context.getElement().isPresent() && context.getElement().get().isAnnotationPresent(MyCustomAnnotation.class)) {
int val = context.getElement().get().getAnnotation(MyCustomAnnotation.class).value();
// Report each
}
}
}

How to register ORMObjectListener in Intershop7

We have implemented several custom ORM objects in our webshop implementation that have references (dependencies) to Intershop Product system object.
When a user tries to delete a certain product in back-office, it causes problems because references to that product may still exist in our custom objects. Naturally, deleting a product that is referenced from one of our custom objects generates an exception like this:
java.sql.SQLTransactionRollbackException: ORA-02091: transaction rolled back ORA-02292: integrity constraint (INTERSHOP.A1POSTPAIDPRICE_CO_002) violated - child record found
We have figured that we could solve that by implementing an ORMObjectListener and overriding objectDeleting method to delete all the references before the product actually gets deleted.
Intershop cookbook for ORM layer states:
"Instances must implement the interface ORMObjectListener for a given ORM object type and register at the factory. The listener is called when instances of the given type are created, changed or removed."
(https://support.intershop.com/kb/index.php/Display/2G3270#Cookbook-ORMLayer-Recipe:NotificationofPersistentObjectChanges)
However, we cannot find a cookbook for registering the listener at the factory. What do we need to do to register the listener?
Also, if there is some better way for handling dependencies to system objects on our custom objects during delete event, I'm open to suggestions.
UPDATE:
This is the listener class I have tried with so far:
public class ProductDeleteListener implements ORMObjectListener<ProductPO> {
#Inject
ProductPOFactory productPOFactory;
/** The Constant LOGGER. */
private static final Logger LOGGER = LoggerFactory.getLogger(ProductDeleteListener.class);
public ProductDeleteListener() {
productPOFactory.addObjectListener(this, new AttributeDescription[0]);
}
#Override
public boolean isOldStateNeeded() {
// TODO Auto-generated method stub
return false;
}
#Override
public void objectChanged(ProductPO object, Map<AttributeDescription, Object> previousValues) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("PRODUCT LISTENER TEST - CHANGE");
}
}
#Override
public void objectChanging(ProductPO object, Map<AttributeDescription, Object> previousValues) {
// TODO Auto-generated method stub
}
#Override
public void objectCreated(ProductPO object) {
// TODO Auto-generated method stub
}
#Override
public void objectCreating(ProductPO object) {
// TODO Auto-generated method stub
}
#Override
public void objectDeleted(ORMObjectKey objectKey) {
// TODO Auto-generated method stub
}
#Override
public void objectDeleting(ProductPO object) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("PRODUCT LISTENER TEST - PRE DELETE");
}
}
}
But it is not working. Nothing gets logged when object changes or gets deleted.
In addition to what Willem Evertse wrote you need to place your registration code in a class that gets instantiated via Intershop Component Framework.
implementation.component:
<components xmlns="http://www.intershop.de/component/2010" scope="global">
<implementation name="ProductDeleteListenerRegistrar"
class="your.fullqualifed.ProductDeleteRegistrar" start="start" stop="stop"></implementation>
instances.component:
<components xmlns="http://www.intershop.de/component/2010"> <instance name="ORMValidator" with="ORMValidator" scope="global"/></components>
You need to write a class, e.g. ProductDeleteRegistrar and provide start method in which you can add registration calls like Willem described. As for stop method you need to safely unregister your object listener. Make sure both methods are declared to be synchronized.
I think registering a listen would be the right approach. Maybe just look out for performance problems.
You are right that there are no examples of this, but here is an example.
Get the factory that you want to receive messages from. In your case, it is ProductPOFactory
ProductPOFactory productFactory = (ProductPOFactory) NamingMgr.getInstance().lookupFactory(ProductPO.class);
productFactory.addObjectListener(new MyProductChangeListener());
MyProductChangeListener needs to extend AbstractORMObjectListener<ProductPO>
and implement the method public void objectDeleting(T object)
Every time a product gets deleted your listener should be called and then you can clean up your custom orm objects. You can have a look at ImageSetDefinitionPOListener as an example

How to get hold of AutomatedInstallData within IzPack 5 InstallerListener methods?

I've tried to find any info on that but failed, maybe someone here can help.
I'm using IzPack 5 since couple of weeks and that's what I started with, so I have no prior IzPack 4 experience.
What I want to do is the following:
Give the user an opportunity to select data directory via
UserInputPanel (works fine)
Validate the entry by checking if the
database already resides there (works fine)
Depending on whether
the DB already exists and if "force" flag specified on the
UserInputPanel create the database after the packs have been
installed
This last step, that's what I can't see how to do.
I hava a java class that implements InstallerListener interface:
public class IzPackInstaller implements com.izforge.izpack.api.data.DynamicInstallerRequirementValidator,
com.izforge.izpack.api.event.InstallerListener {
It's the same class I use for both data validation / db existance check on step 2 and creation on step 3, just for convinience reasons, but it shouldn't matter
I override
#Override
public void afterInstallerInitialization(AutomatedInstallData data)
throws Exception {
System.out.println("Called afterInstallerInitialization");
System.out.println("db.location=" + data.getVariable("db.location"));
System.out.println("db.force.creation=" + data.getVariable("db.force.creation"));
}
but it seems to be deprecated alltogether and is never called in runtime - checked with System.out's.
The same is valid for:
#Override
public void afterPacks(AutomatedInstallData data,
AbstractUIProgressHandler handler) throws Exception {
System.out.println("Never called!");
}
I also override
#Override
public void afterPacks(List<Pack> packs, ProgressListener listener) { }
which is called allright, but how to get hold of AutomatedInstallData within this method? Or how else can I read installer variables at this stage?
I thought of creating a singleton, which I would initialize with the variables during DynamicInstallerRequirementValidator.validateData() call and get the variables at a later point in time, but it's ugly and sounds like a nasty workaround - there should be a way to implement InstallerListener interface and be able to use the variables, shouldn't it?
I'd be really grateful for any hints...
Anton
This is not a very clean solution but there is a way to get a hold of AutomatedInstallData really anywhere in running izpack java without actually overriding some method etc. I would just not suggest it in the first place because it is a little tricky :)
public class Test {
InstallerContainer container = new ConsoleInstallerContainer();
AutomatedInstaller automatedInstaller = container.getComponent(AutomatedInstaller.class);
AutomatedInstallData installData;
public Test() throws IllegalAccessException, NoSuchFieldException {
Field f = AutomatedInstaller.class.getDeclaredField("installData");
f.setAccessible(true);
installData = (AutomatedInstallData)f.get(automatedInstaller);
}
etc...
Now you will have access to the object AutomatedInstallData and its methods.

Guice Names.bindProperties(binder(), properties) on output of a module?

I use an external service to provide properties, but want to make those properties available as #Named(..) vars. Trying to do this in a configure method fails with npe:
Names.bindProperties(binder(), myPropRetriever.getProperties());
is failing because the myPropRetriever isn't appearing until guice has done it's work. I can see why this makes sense - anyone know of any funky hacks that might work around though? Would be handy in this instance..
Thanks to durron597 for the pointer to the related question which gave me enough to figure out. The answer is to use a child injector to take action on the previous injectors output. Example below:
Injector propInjector = Guice.createInjector(new PropertiesModule());
PropertiesService propService = propInjector.getInstance(PropertiesService.class);
Injector injector = propInjector.createChildInjector(new MyModule(Objects.firstNonNull(propService.getProperties(), new Properties())));
Injector is now your injector for the remainder of the app.
And then in MyModule you can take action on the created objects:
public class MyModule extends AbstractModule {
private final Properties properties;
public MyModule(Properties properties){
this.properties=properties;
}
#Override
protected void configure() {
// export all the properties as bindings
Names.bindProperties(binder(), properties);
// move on to bindings
// bind(..);
}
}
In case it helps anyone else..!

get Annotation of test method in testNG ITestListener

I am trying to integrate TestLink with TestNG
Approach is below
1>Write ITestListner with onTestFailure and onTestSuccess
2> get Annotation of the method(like testName which will be equivalent to test name in testlink) which is being failed/success in a variable
3>Make connection with TestLink using API available and update the test case.
However I am struggling to find method Annotation value in ITestListner and requirement is to get annotation values in ITestListner only so that correct test cases can be updated in Test_link
Can someone please help me how to get Test Method annotation value in ITestListner or any other approach in which i can integrate testlink update with TestNG
Hi Thanks niharika for help
,First of all you are correct in explaining use of TestNG but we are using TestNG for Selenium and already there are around 1000 test cases writen in test Methods and we have to live with that
Some how i have figured the solution ,we can still get the testName of the test method using two listners
This is just work around I am not sure if this is the best approach but as of now solving my purpose
package com.automation.testng.listner;
import org.testng.*;
public class MyIInvokeMethodListner_TestName_TestLink implements IInvokedMethodListener {
public static String testName;
public void afterInvocation(IInvokedMethod arg0, ITestResult arg1) {
// TODO Auto-generated method stub
}
public void beforeInvocation(IInvokedMethod m, ITestResult tr) {
// TODO Auto-generated method stub
//This give the Annotation Test object
org.testng.annotations.Test t=m.getTestMethod().getMethod().getAnnotation(org.testng.annotations.Test.class);
MyIInvokeMethodListner_TestName_TestLink.testName = t.testName().toString();
}
}
MyITestListner goes like below
package com.automation.testng.listner;
import org.testng.*;
public class MyITestListner_TestLink extends TestListenerAdapter {
/*IAnnotationTransformer at;
public Listner_1()
{
this.at = new Annotation_listner();
}*/
#Override
public void onTestFailure(ITestResult tr)
{
System.out.println("Hurray !I am being inboked from Test listner");
MyIInvokeMethodListner_TestName_TestLink a = new MyIInvokeMethodListner_TestName_TestLink();
System.out.println(MyIInvokeMethodListner_TestName_TestLink.testName);
}
public void onTestSuccess(ITestResult tr)
{
MyIInvokeMethodListner_TestName_TestLink a = new MyIInvokeMethodListner_TestName_TestLink();
System.out.println(MyIInvokeMethodListner_TestName_TestLink.testName);
}
}
Basically we are getting the method and then using Test Annotation class setting the static variable which can be used in MyITestListner
The ITestListener is the one which is used after <test> tag. For getting the method name and annotation specifics, you need to implement IInvokedMethodListener and in the after/before methods of this interface, and use something like method.getTestMethod().getMethodName() to get the executing method name.
If you are adding testName at the method level, I think you are doing it wrong since the help of testng mentions this "The name of the test this test class should be placed in. This attribute is ignore if #Test is not at the class level."
If you are indeed specifying the #Test at your class level then you can get it as below :
method.getTestMethod().getTestClass().getTestName()
A bit ugly and you probably want to wrap those parts in null checks in your code but this is how you get the testName specified in the annotation from the ITestResult:
iTestResult.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class).testName()