I'm completely new to this instrumentation concept. I have a custom jar file which has lot of methods. Lets assume for now i have start and stop method. Inorder to collect the start and stop metrics i need to call those methods after every click . Instead of doing that is there a way to instrument this. I want this methods to be called for all clickable elements dynamically before and after during runtime. Any advise on this would be great. Thanks in advance. Please find the sample code.
Custom Methods:
Public void start (){
long start = System.currentTimeMillis();
}
public void stop{
long finish= System.currentTimeMillis();
long totalTime = finish - start;
}
Sample Code:
start();
driver.findElement(By.name("username")).sendkeys("###");
stop();
start();
driver.findElement(By.name("password")).sendkeys("###");
stop();
start();
driver.findElement(By.name("login")).click();
stop();
Here's an example instrumentation solution using ByteBuddy, although as I mentioned in comments under this question it's probably not a best approach to solve this.
For this simple example the code only covers a case where invocations on WebDriver and WebElement are chained e.g.:
driver.findElement(By.name("login")).click();
driver.findElement(By.name("logout")).click();
Something like below fragment will not work without additional coding:
WebElement element1 = findElement(By.name("login"));
WebElement element2 = findElement(By.name("logout"));
element2.click();
element1.click();
Instrumentation code:
public class ByteBuddyTest {
public static void main(String[] args) throws Exception {
ByteBuddyAgent.install();
new ByteBuddy()
.redefine(RemoteWebDriver.class)
.visit(Advice.to(WebDriverAdvice.class).on(named("findElement").and(takesArguments(1))))
.make()
.load(ByteBuddyTest2.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent());
new ByteBuddy()
.redefine(RemoteWebElement.class)
.visit(Advice.to(WebElementAdvice.class).on(named("click")))
.make()
.load(ByteBuddyTest2.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent());
InternetExplorerDriver driver = new InternetExplorerDriver();
driver.get("<some webpage>");
driver.findElement(By.id("<some_id>")).click();
}
public static class WebDriverAdvice {
#Advice.OnMethodEnter
public static void enter(#Advice.Origin String method) {
System.out.printf("Driver Method Enter: %s\n", method);
Times.start = System.currentTimeMillis();
}
}
public static class WebElementAdvice {
#Advice.OnMethodExit
public static void exit(#Advice.Origin String method, #Advice.This Object target) {
System.out.printf("Element Method Exit: %s\n", method);
System.out.println("Time: " + (System.currentTimeMillis() - Times.start));
}
}
public static class Times {
public static long start = 0L;
}
}
Example using WebDriverEventListener
public class WebDriverEventListenerTest {
public static void main(String[] args) throws Exception {
InternetExplorerDriver driver = new InternetExplorerDriver();
EventFiringWebDriver eventDriver = new EventFiringWebDriver(driver);
eventDriver.register(new EventHandler());
eventDriver.get("<some webpage>");
eventDriver.findElement(By.id("<some id>")).click();
eventDriver.findElement(By.id("<some id>")).click();
}
public static class EventHandler extends AbstractWebDriverEventListener {
#Override public void beforeFindBy(By by, WebElement element, WebDriver driver) {
System.out.printf("Driver Find By: %s\n", by);
Times.start = System.currentTimeMillis();
}
#Override public void afterClickOn(WebElement element, WebDriver driver) {
System.out.printf("Element Method Exit: %s\n", element);
System.out.println("Time: " + (System.currentTimeMillis() - Times.start));
}
}
public static class Times {
public static long start = 0L;
}
}
It appears you are trying to benchmark code. If so, I suggest using a benchmarking framework, such as Google Caliper, which helps instrument code while nominally impacting the actual performance of the code, and it also helps account for JIT compiling, etc., which may alter the execution time of your method as code is executed repeatedly.
Related
I use ThreadLocal for thread safety and run the tests in parallel using Maven failsafe and JUnit. I am running two tests from two feature files to test parallel running.
But I always have the first browser blank. Then the subsequent ones are fine and the tests pass. If I run sequentially, there isn’t any issue.
HookStep class:
public class HookStep {
#Before()
public void beginTest() {
WebDriverFactory.setDriver(Props.getValue("browser.name"));
}
#After()
public void stopTest(Scenario scenario) {
switch (environment) {
case "local":
case "aws": {
if (scenario.isFailed()) {
Screenshots.Shot shot = new Screenshots(Screenshots.CONTEXT_TEST_FAIL)
.takeShot(scenario.getName() + formCounter.getAndIncrement() + "");
scenario.embed(shot.getContent(), "image/png", "Error - ");
}
WebDriverFactory.closeBrowser();
}
}
}
WebDriverFactory class:
public class WebDriverFactory {
private static ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static synchronized void setDriver(String browser) {
switch (browser) {
case "chrome":
driver = ThreadLocal.withInitial(() -> {
WebDriverManager.chromedriver().setup();
return new ChromeDriver(BrowserOptions.getChromeOptions());
});
prepareBrowser();
break;
case "fireFox":
driver = ThreadLocal.withInitial(() -> {
WebDriverManager.firefoxdriver().setup();
return new FirefoxDriver(BrowserOptions.getFirefoxOptions());
});
break;
default:
throw new IllegalStateException("Unexpected value: " + browser);
}
}
private static void prepareBrowser() {
getDriver().manage().window().maximize();
getDriver().manage().deleteAllCookies();
getDriver().manage().timeouts().pageLoadTimeout(15, TimeUnit.SECONDS);
getDriver().manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
}
public static synchronized WebDriver getDriver() {
return driver.get();
}
public static void closeBrowser() {
getDriver().quit();
}
}
The StepDef class:
public class SampleStepDef {
private final WorldHelper helper;
public SampleStepDef(WorldHelper helper) {
this.helper = helper;
}
#Given("I click on the URL")
public void iClickOnTheURL() {
helper.getSamplePage().navigateToSite();
}
}
public class WorldHelper {
WebDriverFactory webDriverFactory = new WebDriverFactory();
protected WebDriver webDriver = webDriverFactory.getDriver();
private BasePage basePage;
private SamplePage samplePage;
public SamplePage getSamplePage() {
if(samplePage != null)
return samplePage;
samplePage = PageFactory.initElements(webDriver, SamplePage.class);
return samplePage;
}
}
public class SamplePage extends BasePage {
public SamplePage(WebDriver webDriver) {
super(webDriver);
}
public void navigateToSite() {
webDriver.get("https://www.bbc.co.uk");
webDriver.findElement(By.xpath("//a[contains(text(),\'News\')]")).click();
}
}
public class BasePage extends WorldHelper {
public BasePage(WebDriver driver) {
this.webDriver = driver;
}
}
How can I fix this problem?
I noticed multiple problems associated with your code.
You are making use of ThreadLocal.withInitial(). Ideally speaking this should have been defined when you are instantiating the driver thread local static variable.
So instead of
private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
it should have been
private static final ThreadLocal<WebDriver> driver = ThreadLocal.withInitial(() -> {
return null; //Your supplier goes here.
});
There's a clear mess up in your inheritance hierarchy (there's a very good chance that you were trying to create a simple example and perhaps have omitted out the details behind the layers of inheritance), but it wasn't clear as to why does all your page object classes extend WorldHelper
You are having multiple statements at the class level such as this. The problem with these field level initialisations is that they get invoked when the object is constructed. So if the object is being constructed in a different thread, then you run into the problem of the WebDriver initialisation being triggered for that thread. End result: You have a lot of ghost browser instances that keep getting opened up, but no selenium actions are directed to them.
private final WebDriver driver = WebDriverFactory.getDriver();
When working with ThreadLocal variants of WebDriver management, you need to make sure that your calls are always from within your step definitions and never from the constructor or from class level field initialisations such as above.
Here are the list of fixes that you need to do.
Remove all occurrences of private final WebDriver driver = WebDriverFactory.getDriver(); in your code. They are not needed.
Refactor your WebDriverFactory class to look like below (For brevity I have removed off all the commented out code)
public class WebDriverFactory {
private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static void setDriver(String browser) {
RemoteWebDriver rwd;
switch (browser) {
case "chrome":
WebDriverManager.chromedriver().setup();
rwd = new ChromeDriver(BrowserOptions.getChromeOptions());
break;
case "fireFox":
WebDriverManager.firefoxdriver().setup();
rwd = new FirefoxDriver(BrowserOptions.getFirefoxOptions());
break;
default:
throw new IllegalStateException("Unexpected value: " + browser);
}
driver.set(Objects.requireNonNull(rwd));
prepareBrowser();
}
private static void prepareBrowser(){
getDriver().manage().window().maximize();
getDriver().manage().deleteAllCookies();
getDriver().manage().timeouts().pageLoadTimeout(15, TimeUnit.SECONDS);
getDriver().manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
}
public static WebDriver getDriver(){
return Objects.requireNonNull(driver.get());
}
public static void closeBrowser() {
getDriver().manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
getDriver().close();
getDriver().quit();
}
}
Since all your page classes seem to be extending from WorldHelper, add a getter method such as below in it (or) ensure that no where in any of your page classes you have a WebDriver field. Whenever you need to get hold of the WebDriver instance, you should do it directly via WebDriverFactory.getDriver() (or) via the getter method such as below in your WorldHelper or whatever base class you are creating.
protected WebDriver getDriver() {
return WebDriverFactory.getDriver();
}
Once you have fixed the afore-mentioned problems, you should be good and shouldn't see any blank browser windows open up.
Note: Please clean up your project on GitHub. I noticed some cloud service provider credentials in it (it could be real credentials or could be fake. I wouldn't know.)
I haven't used webDriverFactory, but I'd try calling driver.set() in the factory class, as in this tutorial:
http://makeseleniumeasy.com/2020/05/27/threadlocal-static-webdriver-for-parallel-execution/
I am learning and trying to separate locators from actual code in selenium. I have already separated them but I need guidance on more optimization, how can I optimize the code more? Is the Page Object design model used to store only locators? Or can we store their methods too. Can someone please explain with reference to below code?
Link: https://www.goibibo.com/
Actual code with Logic(TC_01Test.java) and Base.java class initializes driver
public class TC_01Test extends Base {
WebDriver driver;
#BeforeTest
public void initialize() throws IOException {
driver = initializeDriver();
}
// Sign In functionality
#Test
public void SignIn() throws InterruptedException {
TC_01 tc02 = new TC_01(driver);
tc02.siginLink().click();
System.out.println(driver.getWindowHandle());
driver.switchTo().frame("authiframe");
System.out.println(driver.getWindowHandle());
tc02.mobileNumber().sendKeys(prop.getProperty("phoneNumber"));
System.out.println("number entered");
tc02.submitButton().click();
System.out.println("button clicked");
driver.switchTo().defaultContent();
System.out.println(driver.getWindowHandle());
tc02.closePopup().click();
}
// SignUp functionality
#Test
public void SignOut() {
TC_01 tc01 = new TC_01(driver);
tc01.sigupLink().click();
driver.switchTo().frame("authiframe");
tc01.mobileNumber().sendKeys(prop.getProperty("phoneNumber"));
tc01.submitButton().click();
driver.switchTo().defaultContent();
tc01.closePopup().click();
}
#AfterTest
public void closeBrowser() {
driver = tearDown();
}
}
Below is the code for Page Object(TC_01.java) created for above test case.
public class TC_01 {
WebDriver driver;
public TC_01(WebDriver driver) {
this.driver = driver;
}
// driver.findElement(By.xpath("//a[#id='get_sign_in']"))
// mobileNumber= driver.findElement(By.xpath("//input[#id='authMobile']")
// driver.findElement(By.id("mobileSubmitBtn"))
// driver.findElement(By.xpath("//div[#class='popContent']/a"))
By signinLink = By.xpath("//a[#id='get_sign_in']");
By signupLink = By.xpath("//a[#id='get_sign_up']");
By mobileNumber = By.xpath("//input[#id='authMobile']");
By submitButton = By.id("mobileSubmitBtn");
By closePopup = By.xpath("//div[#class='popContent']/a");
public WebElement siginLink() {
return driver.findElement(signinLink);
}
public WebElement sigupLink() {
return driver.findElement(signupLink);
}
public WebElement mobileNumber() {
return driver.findElement(mobileNumber);
}
public WebElement submitButton() {
return driver.findElement(submitButton);
}
public WebElement closePopup() {
return driver.findElement(closePopup);
}
}
Answering on your question - yes, you can store methods in PO classes as well. Furthermore, it's a good practice.
Regarding your code optimization - it's better to express business behavior instead of granular technical actions. Also, instead of returning WebElement methods and then perform actions (click, sendKeys etc) in the Test class you can simply perform such actions in PO class.
Check the code below.
public void enterFirstName() {
driver.findElement(firstName).sendKeys("abc");
}
public void enterLastName() {
driver.findElement(lastName).sendKeys("qwerty");
}
public void pressSubmitButton() {
driver.findElement(submitButton).click();
}
// instead of invocation all of these methods above in test class you can simply do this:
public void loginWithValidCredentials(String firstNameValue, String lastNameValue) {
driver.findElement(firstName).sendKeys(firstNameValue);
driver.findElement(lastName).sendKeys(lastNameValue);
driver.findElement(submitButton).click();
}
// Thus your test will look like:
#Test
public void loginTest() {
POclass po = new POclass();
po.loginWithValidCredentials("yourName", "yourNameABC");
// some assert() methods...
}
This is much simplier.
BTW, it's useful to know and use PageFactory concept - https://www.guru99.com/page-object-model-pom-page-factory-in-selenium-ultimate-guide.html
P.S. - read about "Chain of responsibilities" pattern, but in case you are strong in Java, because this is a quite advanced topic.
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
I read a lot about this problem but i can not find answer for my case.
I have class with Selenium method
public class PrzesylkiPrzygotowane implements Tools{
private WebDriver driver;
private StringBuffer verificationErrors = new StringBuffer();
//Override
public Narzedzia getNarzedzia() {
return new Narzedzia();
}
public void setUp() throws Exception {
StartEN start = new StartEN(GetParams.getUser(),
GetParams.getPassword());
this.driver = start.getDriver();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Test
public void testPrzesylkiPrzygotowane() throws Exception {
setUp();
driver.findElement(By.cssSelector("a[href*='?action=GetZbior&arg1=220170'")).click();
driver.findElement(By.cssSelector("button.widgetButton")).click();
driver.findElement(By.id("nazwa")).clear();
Thread.sleep(1000);
driver.findElement(By.id("nazwa")).sendKeys("Mar");
driver.findElement(By.xpath("//body[#id='Body']/div[4]/ul/li/strong[3]")).click();
driver.findElement(By.id("submit_button")).click();
Thread.sleep(1000);
//NPE throw here
getNarzedzia().logout();
}
... rest code.
I made interface for this class
public interface Tools {
public Narzedzia getNarzedzia();
}
"Narzedzia" is a class with group of methods which i use like tools for aplication.
public class Narzedzia{
public WebDriver driver;
boolean acceptNextAlert = true;
public void logout() throws InterruptedException{
//Ustawienia driv = new Ustawienia();
driver.findElement(By.linkText("Wyloguj")).click();
Thread.sleep(1000);
assertTrue(closeAlertAndGetItsText(driver).matches("^Czy na pewno chcesz wyjść z Elektronicznego Nadawcy[\\s\\S] Sprawdź czy wszystkie dane zostały przekazane do placówki\\.$"));
driver.close();
}
public String closeAlertAndGetItsText(WebDriver driv) {
try {
Alert alert = driv.switchTo().alert();
String alertText = alert.getText();
if (acceptNextAlert) {
alert.accept();
} else {
alert.dismiss();
}
return alertText;
} finally {
acceptNextAlert = true;
}
}
...rest code
When i run test rest od method in "Narzedzia" works fine but logout throw error:
java.lang.NullPointerException
With Google you should find many hints for implementing a Singleton in Java.
It should be something like this:
public class SingleObject {
//create an object of SingleObject
private static SingleObject instance = new SingleObject();
//make the constructor private so that this class cannot be
//instantiated
private SingleObject(){}
//Get the only object available
public static SingleObject getInstance(){
return instance;
}
}
Source: Design Pattern - Singleton Pattern (my first search result with Google 'singleton pattern java')
Basically the Constructor has to be private and your getter method has to return a field which holds the object. There are some variants around the tutorial sites. Some of them are thread safe. Choose what you need.
I solved problem.
Made constructor with parameter in "Narzedzia" class
public Narzedzia (WebDriver wd){
this.driver = wd;
}
And call constructor like this in PrzesylkiPrzygorowane
public Narzedzia getNarzedzia() {
return new Narzedzia(this.driver);
}
Test is done and logout doesn't throw NPE.
I created 4 classes also after I decided to convert my project into this design pattern. I moved my codes inside the related classes into the methods. While compiling I'm facing failures and I don't know why.
The main class
GidiyorTest.java
public class GidiyorTest {
protected WebDriver driver;
protected String baseUrl;
private boolean acceptNextAlert = true;
private StringBuffer verificationErrors = new StringBuffer();
static GidiyorTest gittiGidiyor = new GidiyorTest();
static String generatedMail = gittiGidiyor.generateString();
static String generatedUsername = gittiGidiyor.generateString();
static RegisterPage registerPage = new RegisterPage();
static LoginPage loginPage = new LoginPage();
static SearchPage searchPage = new SearchPage();
static DiscountsPage discountsPage = new DiscountsPage();
public String generateString(){
char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 7; i++) {
char c = chars[random.nextInt(chars.length)];
sb.append(c);
}
String output = sb.toString();
return output;
}
#Before
public void setUp() throws Exception {
driver = new FirefoxDriver();
baseUrl = "https://www.gittigidiyor.com/";
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Test
public void testGidiyor() throws Exception {
registerPage.Register();
loginPage.Login();
searchPage.Search();
discountsPage.Discount();
}
#After
....
RegisterPage.java (One of the four new classes for instance sharing just one)
public class RegisterPage extends GidiyorTest {
public void Register() throws InterruptedException {
driver.get(baseUrl + "/kayit-ol");
driver.findElement(By.name("name")).clear();
driver.findElement(By.name("name")).sendKeys("murat");
driver.findElement(By.name("surname")).clear();
driver.findElement(By.name("surname")).sendKeys("yilmaz");
Thread.sleep(300);
driver.findElement(By.id("suggestion_email_input_verifier")).clear();
driver.findElement(By.id("suggestion_email_input_verifier")).sendKeys(
generatedMail + "#gmail.com");
driver.findElement(By.id("nickname")).clear();
driver.findElement(By.id("nickname")).sendKeys(generatedUsername);
Thread.sleep(300);
driver.findElement(By.name("passwd")).clear();
driver.findElement(By.name("passwd")).sendKeys("123456abc");
driver.findElement(By.name("passwd2")).clear();
driver.findElement(By.name("passwd2")).sendKeys("123456abc");
Thread.sleep(300);
driver.findElement(By.id("cepgsm")).clear();
driver.findElement(By.id("cepgsm")).sendKeys("531");
driver.findElement(By.id("cep")).clear();
driver.findElement(By.id("cep")).sendKeys("600 29 79");
Thread.sleep(1000);
driver.findElement(By.id("SubmitForm")).click();
}
}
And the error is beginning at registerPage.Register(); line. One another is java.lang.NullPointerException.
Hope you can help.
The way you're creating your PageObject is not correct. You should not extend the test, one of the main points is that the PageObject should not know anything about the test, rather just expose the services offered by the page.
On the other hand your test should hold the assertions and other test related logic.
The second wrong thing is that you should use PageFactory to instantiate your page object, so that you can take advantage of the lazy binding mechanism. So change to something like this
public class RegisterPage {
private WebDriver driver;
public RegisterPage(WebDriver driver) {
this.driver = driver;
}
// The rest of your class
}
and instantiate inside the test using PageFactory
PageFactory.initElements(driver, RegisterPage.class);
also to ease up maintainence and benefit from lazy element binding you can think about adding your elements as fields, and mark them via annotation, so they get populated by PageFactory as well e.g.
public class RegisterPage {
private WebDriver driver;
public RegisterPage(WebDriver driver) {
this.driver = driver;
}
#FindBy(name = "name")
private WebElement name;
...
}
}
I am creating a test suite for my android application and have this setUp method
private static final String TAG_NAME = "TESTING_SUITE";
public TestingMusicDAO musicDAO;
public List<Song> songs;
public Instrumentation instr;
MusicService musicService;
#Override
public void setUp() throws Exception {
instr = this.getInstrumentation();
Log.d(TAG_NAME, "Setting up testing songs");
musicDAO = new TestingMusicDAO(instr.getContext());
musicService = new MusicServiceImpl(musicDAO);
musicDAO.getAllSongsFromFile();
songs = musicDAO.getAllSongs();
for(Song song : songs)
Log.d( TAG_NAME, song.toString() );
}
And then have these tests which are created by a python tool from a text file
public void test1() {
List<Song> testPlaylist;
String testArtist = ("The Beatles");
String actualArtist = ("TheBeatles");
testPlaylist = testingPlaySongsByKeyword(testArtist);
if(testPlaylist.isEmpty()){
fail("No Songs Were Found");
} else {
for( Song loopsongs : testPlaylist){
if (!(loopsongs.getArtist().equals(actualArtist))){
fail("Song Doesnt Contain the artist" + actualArtist + "... Contains ->" + loopsongs.getArtist());
}
}
}
}
and every time one of these gets called the musicDAO is regenerated. How can I stop the setup method from being called
You don't. The design of JUnit is that setUp() and tearDown() are done once per test. If you want it done per class, do it in the constructor. Just make sure that you don't alter anything inside the classes. The reason for doing it once per test is to make sure all tests start with the same data.
You could use #BeforeClass and #AfterClass annotations from JUnit.
#BeforeClass
public static void test_setUp_Once(){
// Code which you want to be executed only once
createDb();
}
#AfterClass
public static void test_tearDown_Once(){
// Code which you want to be executed only once
deleteDb();
}
Note: You need to declare these methods static to work properly.
I had the same basic problem. I want to be able to test the structure of my database, so I create it in the setUp method and delete it in the tearDown. Using the constructor wouldn't solve my need to delete the database once all my tests are executed, so I used some reentrant logic:
static int testsExecutedSoFar = 0;
static boolean isFirstRun = true;
#Override
protected void setUp() throws Exception {
if(isFirstRun){
createDb();
isFirstRun = false;
}
}
#Override
protected void tearDown() throws Exception{
testsExecutedSoFar++;
if (testsExecutedSoFar == totalNumberOfTestCases())
deleteDb();
}
private int totalNumberOfTestCases() {
return countTestCases()+1; //have to add one for testandroidtestcasesetupproperly added by AndroidTestCase
}
The fields have to be static since JUnit creates a new instance of the class for each run. The magic 1 had to be added since AndroidTestCase adds it's own test (testandroidtestcasesetupproperly) to the test suite but it doesn't count towards the number returned by countTestCases().
A bit on the ugly side, but it did the trick.