Figuring Out How To Take Screenshots with TestNG on Test Failure - java

Okay, I've looked on this topic for quite a bit and have tried multiple solutions to my problem, but I seem to keep getting into roadblocks. I'm trying to setup a general TakeScreenshot after it hits the max of retry attempts with my RetryAnalyzer class. However, I keep getting hit with ClasscastExceptions when adding the Driver and I'm having trouble how to get that same driver initialized in the Base Test, (besides setting to static). Here are my classes I have setup for Screenshot Capture:
Driver Class:
public class Driver implements WebDriver {
protected WebDriver driver;
String browserName;
public static JavascriptExecutor js;
public Driver (String browserName) {
this.browserName = browserName;
if(browserName.equalsIgnoreCase("chrome")) {
System.setProperty("webdriver.chrome.driver", "./resources/webdrivers/chromedriver_win32/chromedriver.exe");
this.driver = new ChromeDriver();
js = (JavascriptExecutor) this.driver;
}
// Other methods for different browsers
BaseTest (Start Portion):
#Listeners({TestMethodListener.class, ScreenshotListstener.class})
public class BaseTest {
protected Driver driver;
DummyTestAccount dummyAccount = new DummyTestAccount();
String browserName = getParamater("browser");
#BeforeClass(alwaysRun = true)
public void setUp() {
driver = new Driver(browserName);
driver.manage().window().maximize();
}
private String getParamater(String name) {
String value = System.getProperty(name);
if(value == null) {
throw new RuntimeException(name + "missing parameter");
}
return value;
}
//Further setup for my extended test classes
RetryAnalyzer:
public class RetryAnalyzer extends BaseTest implements IRetryAnalyzer {
int count = 0;
private static int maxTry = 1;
ScreenshotListstener picCapture = new ScreenshotListstener();
public boolean retry(ITestResult arg0) {
if(count < maxTry) {
count++;
return true; //Retry for flaky tests with unexpected variables
} else {
picCapture.onTestFailure(driver, arg0);
return false; //Don't retry the failed test Capture Screenshot.
}
}
Screenshot Class:
public class ScreenshotListstener extends TestListenerAdapter {
public void onTestFailure(Driver driver, ITestResult result) {
Calendar calendar = Calendar.getInstance();
String methodName = result.getMethod().getMethodName();
SimpleDateFormat formater = new SimpleDateFormat("dd_MM_yyyy_hh_mm_ss");
File bugPic = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
try {
Files.copy(bugPic, new File("./target/surefire-reports/error-pics/"+ methodName + "_" +formater.format(calendar.getTime()) + ".png"));
} catch(IOException e) {
e.printStackTrace();
}
}
}
Currently with this setup, I'm getting hit with a NullPointerException occurring in ScreenshotListener class when initializing on the bugPic File instance. Is there something that I'm missing? Would this current setup be following towards a good practice for test frameworks?
Any advice is helpful.
Thanks.
(Update):
Figured out my issue thanks to Grasshopper's advice.
Driver Class:
public class Driver implements WebDriver {
protected WebDriver driver;
String browserName;
public static JavascriptExecutor js;
public TakesScreenshot bugCapture;
public Driver (String browserName) {
this.browserName = browserName;
if(browserName.equalsIgnoreCase("chrome")) {
System.setProperty("webdriver.chrome.driver", "./resources/webdrivers/chromedriver_win32/chromedriver.exe");
this.driver = new ChromeDriver();
js = (JavascriptExecutor) this.driver;
bugCapture = (TakesScreenshot)driver;
}
// Other methods for different browsers
BaseTest (Start Portion):
#Listeners({TestMethodListener.class, ScreenshotListstener.class})
public class BaseTest {
protected Driver driver;
public static final ThreadLocal<Driver> driverThread = new ThreadLocal<Driver>();
DummyTestAccount dummyAccount = new DummyTestAccount();
String browserName = getParamater("browser");
#BeforeClass(alwaysRun = true)
public void setUp() {
driver = new Driver(browserName);
driver.manage().window().maximize();
driverThread.set(driver);
}
private String getParamater(String name) {
String value = System.getProperty(name);
if(value == null) {
throw new RuntimeException(name + "missing parameter");
}
return value;
}
//Further setup for my extended test classes
RetryAnalyzer:
public class RetryAnalyzer extends BaseTest implements IRetryAnalyzer {
int count = 0;
private static int maxTry = 1;
ScreenshotListstener picCapture = new ScreenshotListstener();
public boolean retry(ITestResult arg0) {
if(count < maxTry) {
count++;
return true; //Retry for flaky tests with unexpected variables
} else {
picCapture.onTestFailure(driverThread.get(), arg0);
return false; //Don't retry the failed test Capture Screenshot.
}
}
Screenshot Class:
public class ScreenshotListstener extends TestListenerAdapter {
public void onTestFailure(Driver driver, ITestResult result) {
Calendar calendar = Calendar.getInstance();
String methodName = result.getMethod().getMethodName();
SimpleDateFormat formater = new SimpleDateFormat("dd_MM_yyyy_hh_mm_ss");
File bugPic = driver.bugCapture.getScreenshotAs(OutputType.FILE);
try {
Files.copy(bugPic, new File("./target/surefire-reports/error-pics/"+ methodName + "_" +formater.format(calendar.getTime()) + ".png"));
} catch(IOException e) {
e.printStackTrace();
}
}
}

Related

Test results getting ignored and fail

While doing testing i got error as one of test ignored and second failed. have posted both classes of test class and driver manger and also error showing after executing test.
This is my test class:
#BeforeSuite
public void setUp(){
driver = DriverManager.getWebDriver();
driver.get(Configuration.getInstance().getProperty("appUrl"));
}
#Test
public void testrequestDemo(){
topNavigation = new TopNavigation(driver);
page = topNavigation.clickRequestDemoLink();
page.KeyInEmail("Azhar#gmail.com")
.and().KeyinCompany("ABC");
}
DriverManager Class :
public class DriverManager {
private WebDriver driver;
private static DriverManager manager;
private DriverManager(){
init();
driver = createDriver();
}
private void init(){
System.setProperty("webdriver.chrome.driver",
Configuration.getInstance().getProperty("chrome.executable"));
}
/**
* This method will create webdriver instance based on the
* property provided.
* #return
*/
private WebDriver createDriver(){
if(Configuration.getInstance().getProperty("browser").equals(Constants.CHROME)){
return new ChromeDriver();
}
else if(Configuration.getInstance().getProperty("browser").equals(Constants.FIREFOX)){
return new FirefoxDriver();
}else {
return new ChromeDriver();
}
}
private WebDriver getDriver() {
return driver;
}
public static WebDriver getWebDriver(){
if(manager==null){
manager = new DriverManager();
return manager.getDriver();
}
return manager.getDriver();
}
}
This is error I am getting
For setUP test fail
For testrequestDemo test ignored>
co.pragra.testingframework.drivermanager.DriverManager.createDriver(DriverManager.java:99)
co.pragra.testingframework.drivermanager.DriverManager.(DriverManager.java:73)
co.pragra.testingframework.drivermanager.DriverManager.getWebDriver(DriverManager.java:113)
co.pragra.testingframework.testcases.HomePageTest.setUp(HomePageTest.java:37)

Getting Exception In Initializer error from maven- Selenium test

i am getting an Exception In Initializer error in my code and don't know what i am doing wrong, please help !i am happy to answer if you have any questions. Below is the error i am getting from maven:
[ERROR] Failures:
[ERROR] Homepage_Test.startURL:13->BaseClass.globalDriver:26 ยป ExceptionInInitializer.
//My Homepage_Test which includes all tests. startURL() invokes webdriver and runs to get website:
public class Homepage_Test extends BaseClass{
#BeforeTest
public void startURL() {
globalDriver();
}
#Test
public void search_product() {
Homepage_Methods.search_and_add();
}
#Test
public void verify_product_text() {
Homepage_Methods.verify_search();
}
#Test
public void scroll_down_page() {
Homepage_Methods.scroll_down_verify_text();
}
#AfterClass
public void clear_field() {
Homepage_Methods.clear_inputfield();
}
}
// My BaseClass:
public class BaseClass {
public static void globalDriver() {
System.setProperty("webdriver.gecko.driver" , System.getProperty("user.dir")+"/resources/geckodriver.exe");
WebDriver driver = new FirefoxDriver();
#SuppressWarnings("unused")
Shop_Page_Objects cpo = new Shop_Page_Objects(driver);
#SuppressWarnings("unused")
Homepage_Objects hpom = new Homepage_Objects(driver);
#SuppressWarnings("unused")
Shop_Page_Methods spm = new Shop_Page_Methods(driver);
#SuppressWarnings("unused")
Homepage_Methods hpm = new Homepage_Methods(driver);
DesiredCapabilities dc = new DesiredCapabilities();
FirefoxOptions options = new FirefoxOptions();
options.setHeadless(true);
dc.setCapability(FirefoxOptions.FIREFOX_OPTIONS,options);
driver.manage().timeouts().implicitlyWait(6000, TimeUnit.SECONDS);
driver.get("blabla website");
}
}
//Line 26 of BaseClass contains Shop_Page_Methods which is very long but here is the snippet:
public class Shop_Page_Methods extends BaseClass{
private static WebDriver driver;
public Shop_Page_Methods(WebDriver driver) {
Shop_Page_Methods.driver = driver ;
}
public static WebElement product_1_to_add_to_cart = Shop_Page_Objects.get_cart_product_1();
public static List<WebElement> all_products = Shop_Page_Objects.get_All_Products();
public static int products_count = Shop_Page_Objects.get_All_Products().size();
public static List<WebElement> all_price = Shop_Page_Objects.get_price();
public static String remove_number = Shop_Page_Objects.get_number_to_remove().getText();
public static String[] remover_1 = remove_number.split("");
public static int remove_number_1 = Integer.parseInt(remover_1[0]);
i figured out the issue was that i was doing the following before actually clicking/switching to their container:
public static String remove_number = Shop_Page_Objects.get_number_to_remove().getText();
public static String[] remover_1 = remove_number.split("");
public static int remove_number_1 = Integer.parseInt(remover_1[0]);

How to initialize String originalHandle = driver.getWindowHandle(); once?

I am using TestNG with Selenium.
I am trying to use driver.getWindowHandle(); to switch between pop-ups, iframes and such.
The thing is, if I declare it like this in the TNGDriver class
public String originalHandle = driver.getWindowHandle();
I get a java.lang.NullPointerException (obviously, because this is initialized before the driver).
How can I declare it once and start using it in other classes? Keep in mind my classes are extended between them and I need to use this originalHandle variable inside methods in other classes, e.g.:
public void clickOnFacebookIcon() {
Assert.assertTrue(true, driver.findElement(By.id(FACEBOOK_ICON)).getText());
driver.findElement(By.id(FACEBOOK_ICON)).click();
for(String handle : driver.getWindowHandles()) {
if (!handle.equals(originalHandle)) {
driver.switchTo().window(handle);
driver.close();
}
}
driver.switchTo().window(originalHandle);
}
Here are my other classes:
TNGDriver class
public class TNGDriver {
public static WebDriver driver;
public static final String CHROME_DRIVER_PATH = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe";
private WebDriverWait wait;
#SuppressWarnings("deprecation")
public void init() {
DesiredCapabilities capabilities = DesiredCapabilities.chrome();
ChromeOptions options = new ChromeOptions();
options.addArguments("--incognito");
capabilities.setCapability(ChromeOptions.CAPABILITY, options);
System.setProperty("webdriver.chrome.driver", CHROME_DRIVER_PATH);
driver = new ChromeDriver(capabilities);
driver.manage().window().maximize();
}
public WebDriverWait getWait() {
wait = new WebDriverWait(driver, 60);
return wait;
}
Testcase1 class
public class Testcase1 extends Registration {
TNGDriver tngDriver = new TNGDriver();
#BeforeTest
public void setup() {
tngDriver.init();
}
#Test(priority = 1)
public void step1_clickOnSignIn() {
clickOnSignIn();
}
#Test(priority = 2)
public void step2_clickOnFacebookIcon() {
clickOnFacebookIcon();
}
You can use a desing pattern to do that
https://en.wikipedia.org/wiki/Singleton_pattern
With this pattern you will only have one instance of the object.
class Singleton
{
// static variable single_instance of type Singleton
private static Singleton single_instance = null;
// variable of type String
public String originalHandle = driver.getWindowHandle();
// private constructor restricted to this class itself
private Singleton()
{
//Do something on constructor
}
// static method to create instance of Singleton class
public static Singleton getInstance()
{
if (single_instance == null)
single_instance = new Singleton();
return single_instance;
}
}
To access it you may do something like this
Singleton x = Singleton.getInstance();
//To access the String variable
x.s

JUnit passing parameters between class

I have a scenario as follows that I'm not sure from where to start,
File name should be passed as an argument param when running the jar file
say for example I want to test a set of data from external file and I have a super class (Test Suite) that have number one and number two
and there are two test classes that should extend this class and perform the tests.
I'm currently new to JUnit so I'm lacking many concepts and need someone's help.
I have class CoreManager which executes the main
public static void main(String[] args)
{
if (Arrays.asList(args).contains("Import"))
{
accountInfo = new ArrayList<>();
int ImportIndex = Arrays.asList(args).indexOf("Import");
String fileName = args[ImportIndex+1];
if (fileName.contains("xml"))
{
ParseXML parseXML = new ParseXML();
accountInfo = parseXML.ParseAccounts(fileName);
Result result = JUnitCore.runClasses(LoginTestSuite.class);
for (Failure failure : result.getFailures()) {
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
}
}
And Suite Class
#RunWith(MockitoJUnitRunner.class)
#Suite.SuiteClasses({
Login.class,
SignUp.class
})
public class LoginTestSuite {
public static WebDriver driver;
public static ArrayList<AccountInfo> Account;
public static int SecondsToWait;
public LoginTestSuite(WebDriver driver,ArrayList<AccountInfo> Account,int
secondsToWait)
{
this.Account = Account;
this.SecondsToWait = secondsToWait;
this.driver = driver;
}
}
And Test Class
public class Login {
private static WebDriver driver;
private static ArrayList<AccountInfo> Account;
private static int SecondsToWait;
private static final Logger logger = Logger.getLogger(Login.class.getName());
#BeforeClass
public void init(){
this.driver = LoginTestSuite.driver;
this.Account = LoginTestSuite.Account;
this.SecondsToWait = LoginTestSuite.SecondsToWait;
}
#Before
public void Setup(){
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(SecondsToWait,
TimeUnit.SECONDS);
driver.manage().timeouts().pageLoadTimeout(SecondsToWait,
TimeUnit.SECONDS);
}
#After
public void TearDown(){
driver.quit();
}
#Test
public void TestUserLogin() throws Exception
{
// Logic
}
Your code looks muddled and contains several poor quality constructs. Most importantly, I don't see a distinction between test code and production code. Which is which?
This could be production code:
public class App {
public static void main(String[] args) {
AccountReader accountReader = new AccountReader();
List<AccountInfo> accounts = accountReader.read(args);
// maybe do something with those accounts?
}
}
public class AccountReader {
private ParseXML parseXML;
public AccountReader() {
this.parseXML = new ParseXML();
}
// extra constructor to allow dependency injection from test
protected AccountReader(ParseXML parseXML) {
this.parseXML = parseXML;
}
public List<AccountInfo> read(String[] args) {
return parseXML.ParseAccounts(getFileName(args));
}
private String getFileName(String[] args) {
List<String> arguments = Arrays.asList(args);
int importIndex = arguments.indexOf("Import");
if (importIndex < 0) {
throw new RuntimeException("Missing Import argument");
}
int fileNameIndex = importIndex + 1;
if (fileNameIndex >= arguments.size()) {
throw new RuntimeException("Missing fileName argument");
}
String fileName = args[fileNameIndex];
if (!fileName.endsWith(".xml")) {
throw new RuntimeException("Can only import XML files");
}
return fileName;
}
}
And this could be a test for it:
public AccountReaderTest {
private AccountReader instance;
#Mock // creates a mock instance which we can give desired behavior
private ParseXML parseXML;
#Mock
List<AccountInfo> accounts;
#Before
public void setUp() {
instance = new AccountReader(parseXML);
}
#Test
public void testHappy() {
// SETUP
String fileName = "test.xml";
// specify desired behavior of mock ParseXML instance
when(parseXML.ParseAccounts(fileName).thenReturn(accounts);
// CALL
List<AccountInfo> result = instance.read(new String[] { "Import", fileName });
// VERIFY
assertEquals(accounts, result);
}
#Test(expected = RuntimeException.class)
public void testMissingImport() {
instance.read(new String[] { "notImport" });
}
#Test(expected = RuntimeException.class)
public void testMissingFileName() {
instance.read(new String[] { "Import" });
}
#Test(expected = RuntimeException.class)
public void testNotXml() {
instance.read(new String[] { "Import", "test.properties"});
}
}

How make webdriver not to close browser window after each test?

I'm new in both Selenium WebDriver and Java. I have some webservices on my site on page /someservice.php. I've wrote few tests on Selenuim and they work fine. Code example (Main Class):
public class SiteClass {
static WebDriver driver;
private static boolean findElements(String xpath,int timeOut ) {
public static void open(String url){
//Here we initialize the firefox webdriver
driver=new FirefoxDriver();
driver.get(url);
}
public static void close(){
driver.close();
}
WebDriverWait wait = new WebDriverWait( driver, timeOut );
try {
if( wait.until( ExpectedConditions.visibilityOfElementLocated( By.xpath( xpath ) ) ) != null ) {
return true;
} else {
return false;
}
} catch( TimeoutException e ) {
return false;
}}
public static Boolean CheckDiameter(String search,String result){
driver.findElement(By.xpath("//input[#id='search_diam']")).sendKeys(search);
WebDriverWait wait = new WebDriverWait(driver, 5);
WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//div[#class='ac_results'][last()]/ul/li")));
WebElement searchVariant=driver.findElement(By.xpath("//div[#class='ac_results'][last()]/ul/li"));
Actions action = new Actions(driver);
action.moveToElement(searchVariant).perform();
driver.findElement(By.xpath("//li[#class='ac_over']")).click();
Boolean iselementpresent = findElements(result,5);
return iselementpresent;
}
}
Code Example (Test Class)
#RunWith(Parameterized.class)
public class DiamTest {#Parameters
public static Collection<Object[]> diams() {
return Arrays.asList(new Object[][] {
{ "111", "//div[#class='jGrowl-message']",true},
{ "222", "//div[#class='jGrowl-message']",false},
{ "333", "//div[#class='jGrowl-message']",true},
});
}
private String inputMark;
private String expectedResult;
private Boolean assertResult;
public DiamTest(String mark, String result, boolean aResult) {
inputMark=mark;
expectedResult=result;
assertResult=aResult;
}
#BeforeClass
public static void setUpClass() {
}
#AfterClass
public static void tearDownClass() {
}
/**
* Test of CheckDiameter method, of class CableRu.
*/
#Test
public void testCheckDiameter() {
SiteClass obj=new SiteClass();
obj.open("http://example.com/services.php");
assertEquals(assertResult, obj.CheckDiameter(inputMark, expectedResult));
obj.close();
}
}
Now I have 2 tests like that with 3 parameters each (total 6 variants). As you can see in every variant I create new browser window and when I run all 6 variants that take too much time (up to 80 seconds).
How can I run all variants in one browser window to speed up my tests?
Just move contents of public static void close() method from your SiteClass to tearDownClass() method in DiamTest class. In this way the browser window will be closed when the class execution finished (because of #AfterClass annotation). Your code then should look like this:
//DiamTest class
#AfterClass
public static void tearDownClass() {
driver.close();
}
It's also a good practice to move browser window initialization to setUpClass() method which will be executed before each test class (according to #BeforeClass annotation)
//DiamTest class
#BeforeClass
public static void setUpClass() {
//Here we initialize the firefox webdriver
driver=new FirefoxDriver();
driver.get(url);
}
What you need to do is share your help class with all your tests, this mean, you should create an instance of SiteClass inside your setUpClass method.
This method are annotated with #BeforeClass assuring your test class will create this method will be executed before all the test be executed.
You can read more about #BeforeClass in jUnit doc: or have a simple overview in this response.
You will also need do some rewrite some code to allow share the driver with the another test, something like this:
#RunWith(Parameterized.class)
public class DiamTest {
#Parameters
public static Collection<Object[]> diams() {
return Arrays.asList(new Object[][] {
{ "111", "//div[#class='jGrowl-message']",true},
{ "222", "//div[#class='jGrowl-message']",false},
{ "333", "//div[#class='jGrowl-message']",true},
});
}
private String inputMark;
private String expectedResult;
private Boolean assertResult;
private static SiteUtil siteUtil;
public DiamTest(String mark, String result, boolean aResult) {
inputMark=mark;
expectedResult=result;
assertResult=aResult;
}
#BeforeClass
public static void setUpClass() {
siteUtil = new SiteUtil();
}
#AfterClass
public static void tearDownClass() {
siteUtil.close();
}
#Test
public void testCheckDiameter() {
siteUtil.open("http://example.com/services.php");
assertEquals(assertResult, obj.CheckDiameter(inputMark, expectedResult));
}
}
and:
public class SiteClass {
static WebDriver driver;
public SiteClass() {
driver = new FirefoxDriver();
}
public void open(String url){
driver.get(url);
}
...
Tip:
You should read about the TestPyramid.
Since functional tests are expensive, you should care about what is really necessary test. This article is about this.

Categories

Resources