Environment:
Java, Selenium webdriver, Maven, testNG, Log4J, Eclipse
XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="TestAll" parallel = "tests" thread-count = "2">
<test name="postivelogintest_IE">
<parameter name="browser" value="ie"/>
<classes>
<class name="com.dice.LoginTest">
<methods>
<include name="DataDrivenpositiveLoginTest"/>
</methods>
</class>
</classes>
</test>
<test name="postivelogintest_CH">
<parameter name="browser" value="ch"/>
<classes>
<class name="com.dice.LoginTest">
<methods>
<include name="DataDrivenpositiveLoginTest"/>
</methods>
</class>
</classes>
</test>
</suite>
BaseTest.java
package com.diceBase;
import org.apache.log4j.Logger;
import org.openqa.selenium.WebDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Parameters;
public class BaseTest {
protected WebDriver driver;
protected Logger log;
#Parameters ({"browser"})
#BeforeMethod
protected void MethodSetup(String browser){
log.info("method set up"); // line 16
driver = BrowserFactory.getDriver(browser);
}
#AfterMethod
protected void TearDown(){
log.info("method tear down");
try {
Thread.sleep(5000);
driver.quit();
} catch (Exception e) {
}
}
}
I added log4j.properties under src/main/resources.
In the BaseTest.java, I added two lines after importing log4j.
log.info("method set up");
log.info("method tear down");
iMy goal is to be able to use log.info entire project. Before that, I would like to test it by only importing log4j logger in basetest class to see if it works. If it works then I can import log4j in entire project.
I get error if I keep both log messages. But, If I remove both log messages, script passes. How can I print the logs using log4j?
The log variable in your BaseTest.java has not assigned with object of logger class (https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Logger.html) . Since No object is assigned to the variable log, it throws NULL pointer exception. So please create object of logger class to varaiable log
Related
I am trying to rotate my gc.log file every time my application starts up.
I am using this file appender in my logback.xml file.
...
<appender name="GCFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.directory}/gc.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.directory}/gc.log.%d{yyyyMMdd}_%d{HHmmss,aux}.gz</fileNamePattern>
<TimeBasedFileNamingAndTriggeringPolicy class="com.ga.omni.utility.StartupTriggeringPolicy" />
<maxHistory>50</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
...
<root level="INFO">
<appender-ref ref="FILE" />
<appender-ref ref="GCFILE" />
</root>
(the "FILE" ref is a reference to our default logging file for the app.)
The appender references a TimeBasedFileNamingAndTriggeringPolicy named StartupTriggeringPolicy:
#NoAutoStart //won't be autostarted by Joran at config time
public class StartupTriggeringPolicy<E> extends DefaultTimeBasedFileNamingAndTriggeringPolicy<E> {
Logger log = LoggerFactory.getLogger(StartupTriggeringPolicy.class);
public StartupTriggeringPolicy() {
log.info("StartupTriggeringPolicy constructor called");
}
#Override
public void start() {
log.info("StartupTriggeringPolicy start() called... initialting gc.log rollover");
super.start();
//only check this once, on startup.
nextCheck = 0L;
isTriggeringEvent(null, null);
try {
tbrp.rollover();
log.info("StartupTriggeringPolicy start() called... gc.log successfully rolled over.");
} catch (RolloverFailure e) {
log.warn("Error rolling over gc.log file in StartupTriggeringPolicy.start()");
//Do nothing
}
}
}
The trouble that I'm facing is that the app starts up, but the StartupTriggeringPolicy never seems to get instantiated. None of the logs from the constructor or start() method are written, and if I put breakpoints in those methods, the breakpoints don't get hit.
Any suggestions would be greatly apperciated!
I am using spring-boot cucumber with TestNG to write and API test framework ,
wanted to undersatand how can add tags and feature file to executed based on environment selected
Below is my current implementation
#CucumberOptions(
features = {"src/test/resources/Features"},
glue = {"als.system.tests.stepDefinations"},
plugin = {"pretty", "html:target/cucumber-html-report.html"}
)
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
And skipping test based on tags , but this is not ideal solution and also dont want to display skipped test on report
#Before
public void setup(Scenario scenario) {
if (!scenario.getSourceTagNames().contains("#" + productName.toLowerCase())) {
throw new SkipException("Skipping /Ignoring this scenario as not part of executions !!!");
}
}
Is there clean way to achieve this ?
Here's how you do it.
Ensure that you are using the latest released version of TestNG (it is 7.6.1 as of today and it needs JDK11)
Build a data provider interceptor by implementing the TestNG interface com.rationaleemotions.TagBasedInterceptor
Wire in this listener using either the <listener> tag (or) using the service provider interface approach. For details on this, you can refer to the official TestNG documentation here (or) refer to my blog-post here.
Below is a sample implementation of the listener
import io.cucumber.testng.PickleWrapper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import org.testng.IDataProviderInterceptor;
import org.testng.IDataProviderMethod;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
public class TagBasedInterceptor implements IDataProviderInterceptor {
#Override
public Iterator<Object[]> intercept(Iterator<Object[]> original,
IDataProviderMethod dataProviderMethod, ITestNGMethod method, ITestContext iTestContext) {
String rawTag = iTestContext.getCurrentXmlTest().getParameter("tag");
if (rawTag == null || rawTag.trim().isEmpty()) {
return original;
}
List<String> tags = Arrays.asList(rawTag.trim().split(","));
List<Object[]> pruned = new ArrayList<>();
while (original.hasNext()) {
Object[] currentElement = original.next();
Optional<Object> searchResult = findPickleWrapper(currentElement);
if (searchResult.isEmpty()) {
continue;
}
PickleWrapper pickleWrapper = searchResult.map(element -> (PickleWrapper) element).get();
boolean tagPresent = pickleWrapper.getPickle().getTags()
.stream().anyMatch(tags::contains);
if (tagPresent) {
pruned.add(currentElement);
}
}
return pruned.iterator();
}
private Optional<Object> findPickleWrapper(Object[] each) {
return Arrays.stream(each)
.filter(element -> element instanceof PickleWrapper)
.findFirst();
}
}
Here's how the suite xml would look like
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Default Suite" verbose="2">
<listeners>
<listener class-name="com.rationaleemotions.TagBasedInterceptor"/>
</listeners>
<parameter name="tag" value="dragon_warrior"/>
<test name="testng_playground">
<classes>
<class name="com.rationaleemotions.CucumberRunnerTests">
</class>
</classes>
</test>
</suite>
Below are the dependencies that I am using for this sample
<dependencies>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-testng -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>7.8.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.8.0</version>
</dependency>
</dependencies>
Feel free to enhance the listener such that if it also reads from the JVM argument (which you can specify using -D) so that you can have the dynamic behaviour of overriding the tag value in the suite xml with something that can be specified as a tag (or a comma separated list of tags) through the JVM argument.
Is there a way to run same class in parallel with multiple threads, like
<suite name="myTestSuite" verbose="1">
<test name="myTest" parallel="classes" thread-count="5">
<classes>
<class name="myPackage.Test" />
</classes>
</test>
</suite>
I want to the class 'myPackage.Test' to be invoked in 5 parallel threads.I know that it works if I want to executed different classes in parallel, like
<suite name="myTestSuite" verbose="1">
<test name="myTest" parallel="classes" thread-count="5">
<classes>
<class name="myPackage.Test1" />
<class name="myPackage.Test2" />
<class name="myPackage.Test3" />
<class name="myPackage.Test4" />
<class name="myPackage.Test5" />
</classes>
</test>
</suite>
As an alternate to the Factory pattern, you could create a <test> node for each time you want to run the class, then parallelize by test. You'd also want to move your parallelization attributes to the <suite> node. For example:
<suite name="mySuite" parallel="tests" thread-count="5">
<test name="myTest1">
<classes>
<class name="myPackage.Test" />
</classes>
</test>
<!-- Repeat the '<test>' node as many times as you wish to run the class -->
</suite>
You'll have to name each <test> uniquely, but this is decently simple way to run the same class many times and in parallel.
What you can do is using a Factory to create 5 instances of your test class.
public class TestFactory {
#Factory
public Object[] createInstances() {
Object[] result = new Object[5];
for (int i = 0; i < 5; i++) {
result[i] = new Test();
}
return result;
}
}
Then, you can use parallel="instances".
<suite name="myTestSuite" verbose="1">
<test name="myTest" parallel="instances" thread-count="5">
<classes>
<class name="myPackage.TestFactory"/>
</classes>
</test>
</suite>
I'm an inexperienced Java developer writing an application that is handling a backup.
My application opens a gui (StepTwoBackup written using NetBeans Template), which gathers some information from the user, then when I press on the "Next" button, the gui passes these information to an object (BackupRestore which is logging all the operation using Log4J), and then opens another window (StepThreeBackup) and passes the object to it.
In this new window(StepThreeBackup), I automatically launch a method on the object passed (BackupRestore.execute()) which performs the backup.
In this last window (StepThreeBackup) I created a JTextArea where I would like to show the output of the Log4J (which currently writes to a log file and outputs to console).
Is there a way to do this? I've read that I should use an appender, but cannot figure out how to do this correctly.
For the time being I've created the following entry in my working Log4J property file:
<appender name="guiAppender" class="BackupAppGui.StatusMessageAppender">
<param name="Threshold" value="INFO" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{dd MMM yyyy HH:mm:ss} %5p %c{1} - %m%n"/>
</layout>
</appender>
Then the following Class in my Package (following another post):
package BackupAppGui;
/**
*
* #author MSTPA
*/
import javax.swing.JTextArea;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.spi.LoggingEvent;
public class StatusMessageAppender extends AppenderSkeleton {
private final JTextArea jTextA;
public StatusMessageAppender() {
jTextA = StepThreeBackup.getJTextA();
}
protected void append(LoggingEvent event)
{
if(event.getLevel().equals(Level.INFO)){
jTextA.append(event.getMessage().toString());
}
}
public void close()
{
}
public boolean requiresLayout()
{
return false;
}
}
But nothing is written to the jTextArea.
What am I doing wrong? Can someone help me solving this? Thank you for all the help you can give me.
You need to make sure that the instance of JTextArea is not null. Yoy can try adding the appender programmatically (e.g. in the constructor of StepThreeBackup after create the components):
StatusMessageAppender appender = new StatusMessageAppender();
LogManager.getRootLogger().addAppender(appender);
Don't forget delete the entry in the log4j.xml file.
I've a custom log4j layout class that extends PatternLayout, my layout class simply masks the password in the log. It works in a simple console app. Here's the log4j.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="A1" class="org.apache.log4j.ConsoleAppender">
<layout class="com.PortalLog4jFilteringPattern"> <param name="ConversionPattern" value="%t %-5p %c{2} - %m%n"/> </layout>
</appender>
<root>
<priority value ="DEBUG" /> <appender-ref ref="A1" />
</root>
</log4j:configuration>
Here's a snipet of the layout class:
public class PortalLog4jFilteringPattern extends PatternLayout {
// omitted
#Override
public String format(LoggingEvent event) {
System.out.println("in format()...... ");
// rest omitted
Here's the calling code:
import org.apache.log4j.Logger;
public class ProductDemo {
private static Logger logger = Logger.getLogger(ProductDemo.class);
public ProductDemo() {
}
public void processOrder(CustomerOrder order) {
logger.info(order.getProductName());
}
// rest ommited
A sample result log with pswd being masked:
main INFO test.ProductDemo - "password":"*****"},
But once I moved the custom layout class to my webapp (log4j.xml is exactly the same.), it doesn't get called (i.e., no System.out output) and the pswd is still being shown. I'm running the webapp locally with maven on Jetty using this cmd: mvn jetty:run
Here's the calling code:
// original code, but I changed it to import org.apache.log4j.Logger for experiment
//import org.slf4j.LoggerFactory;
//import org.slf4j.Logger;
import org.apache.log4j.Logger;
public class BlahBlahClass extends Blah
// things omitted
private final static Logger log = Logger.getLogger( BlahBlahClass .class );
Any idea? thanks
In a Java EE server environment I would say: it's a class loader issue. Jetty is a servlet container, so it's class loading architecture is simpler; still, it's worth checking. If your log4j is not deployed within the WAR, but comes from the Jetty class path, this is almost certainly the cause.
Try changing the class loading strategy to "parent last", as described in the Jetty manual, see if it helps.
You should import package name of your class in log4j. I added to log4j2.xml as below:
<Configuration packages="package path of your class">