Is it ok to use new to create and initialise a page instead of pagefactory.initelements method - selenium-webdriver

I was developing a pagefactory based framework. I had earlier used pagefactory.initements method to inintialise and move from page to page. Init method basically does the same work as saying Homepage HP = new HomePage(driver);
So it is necessary to use init method in pagefactory?
Will we loose something if we do not use it and instead use new to create a page.

If you are using a Java PageFactory with annotations yes.
The PageFactory.initElements(driver, My.class) command parses the annotations and sets up the Java Proxy classes. If you don't .initElements() none of the WebElements in your class will have locators assigned to them and they will all be null.
You can put the .initElements() in your constructor if you just want to new up a page e.g.:
public class MyPage {
public MyPage(WebDriver driver) throws Exception {
PageFactory.initElements(driver, this);
}
}

Related

I am getting null pointer exception for webelement defined under #FindBy annotation in page factory model

I'm new in Selenium learning. I'm getting null pointer exception when I try to use web element - Milestone_Tile_Text.click; in my code but it works fine when I use
LoginTestScript.fd.findElement(By.linkText("Milestone")).click();
Please see below code I have used PageFactory.initElements as well but not sure how to solve this error.
public class MilestoneTileModel
{
GenerateTestData objtestdata = new GenerateTestData() ;
public MilestoneTileModel() //constructor
{
PageFactory.initElements(LoginTestScript.fd, this);
}
#FindBy(xpath="//a[text()='Milestone']")
WebElement Milestone_Tile_Text;
public void Milestone_Tile_Click()
{
Milestone_Tile_Text.click();
LoginTestScript.fd.findElement(By.linkText("Milestone")).click();
LoginTestScript.fd.findElement(By.xpath("//*#id='CPH_btnAddNewMilestoneTop']")).click();
}
}
Timing issues might occur more often when you use an init method.
The timing issue is when you init an element the driver immediately try to find the elements, on failure you will get no warning but the elements will refer null.
The above can occur for example because the page was not fully rendered or the driver see an older version of the page.
A fix can be to define the elements as a property and on the get of the property use the driver to get the element from the page
Please note that selenium does not promise the driver sees the latest version of the page so even this might break and on some situations a retry will work.
First problem what I see: You didn't set LoginTestScript
Following documentation at first you need to set PageObject variable:
GoogleSearchPage page = PageFactory.initElements(driver, GoogleSearchPage.class);
The best way to rich that point is separate Page Object Model and scenario scipt
You fist file POM should contain:
LoginTestPOM
public class LoginTestPOM {
#FindBy(xpath="//a[text()='Milestone']")
WebElement MilestoneTileText;
public void clickMilestoneTitleText(){
MilestoneTitleText.click();
}
}
TestScript
import LoginTestPOM
public class TestLogin {
public static void main(String[] args) {
// Create a new instance of a driver
WebDriver driver = new HtmlUnitDriver();
// Navigate to the right place
driver.get("http://www.loginPage.com/");
// Create a new instance of the login page class
// and initialise any WebElement fields in it.
LoginTestPOM page = PageFactory.initElements(driver, LoginTestPOM.class);
// And now do the page action.
page.clickMilestoneTitleText();
}
}
This is basis of Page Object Pattern.
NOTE: I'm writing that code only in browser so it could contain some mistakes.
LINK: https://github.com/SeleniumHQ/selenium/wiki/PageFactory
The "ugly" solution without page object pattern is:
UglyTestScript
public class UglyTestLogin {
public static void main(String[] args) {
// Create a new instance of a driver
WebDriver driver = new HtmlUnitDriver();
// Navigate to the right place
driver.get("http://www.loginPage.com/");
// DON'T create a new instance of the login page class
// and DON'T initialise any WebElement fields in it.
// And do the page action.
driver.findElement(By.xpath("//a[text()='Milestone']").click()
}
}

PageFactory class of selenium and its working when used with FindBy Annotation

I have a question on how the PageFactory.initElements method of Selenium webdriver works when I use FindBy annotation
I have a snippet of code like this
Class PageObject {
#FindBy(id = "username")
private WebElement userName;
#FindBy(id = "password")
private WebElement passWord;
private WebDriver driver;
private String url = "http://www.loginpage.com";
public PageObject() {
driver = new FirefoxDriver();
PageFactory.initElements(driver, this);
}
void load() {
this.driver.get(url);
}
}
class TestClass {
public void testMethod() {
PageObject po = new PageObject();
po.load();
}
}
If I read documentation of the PageFactory.initElements method, it says - Instantiate an instance of the given class, and set a lazy proxy for each of the WebElement and List fields that have been declared, assuming that the field name is also the HTML element's "id" or "name".
My question here is when I create the object of the page in the test class, it fires the constructor of the page object and initializes the webelements using PageFactory. But what it would initialize to? I navigate to the page only after I construct the page object, using the load method. What would the PageFactory initialize my WebElements to when the page is not yet available. How does it work. Can somebody please help me understand this
Regards
Gauri
Here's what happens: When you call initElements, the PageObjectFactory scans your PageObject for fields of the type WebElement. For each WebElement field that your page object has, it creates a proxy object. That proxy object stores the locator of the WebElement (the value of the #FindBy annotation). The proxy is then assigned to the field.
Later, when your code actually accesses the field, instead of a WebElement object, you retrieve the proxy. Remember that the proxy object "knows" the locator of the WebElement it represents. Now the proxy actually tries to locate that locator on the current page, and returns the actual WebElement if it was found.
So, in your example, as long as you don't work with the userName or passWord fields, they won't be actually located. That means, that for example you wouldn't get a NoSuchElementException, even if the locators were wrong, unless you actually work with those elements.
So to answer your question: It doesn't matter that at the time when the PageObject is initialized the driver hasn't navigated anywhere yet, as the creation of the proxy objects doesn't actually locate them.

CakePHP Globally override class?

I reconfigured my cakeEmail class to log to a specific type by rewriting the send method. I would like to use this override globally. My current single file setup uses /Lib/CustomCakeEmail.php with
App:uses('CustomCakeEmail', 'Lib');
CakePhp: Cake Email AfterSend event suggests a method to globally override using AppController but I have been unable to even trigger the debugger in
App::uses('CustomCakeEmail', 'Lib');
class AppController extends Controller {
public function getEmailInstance($config = null) {
CakeLog::write('debug', 'appcontroller triggered');
return new CustomCakeEmail($config);
}
What is the correct way to implement this global override?
CakePHP Version 2.8.4

How to load a component in console/shell

In CakePHP, how do I load a component in a shell?
To use a component in a controller, you include an array of components in a property called $components. That doesn't work for my Shell. Neither does the "on-the-fly" loading suggested in the documentation.
class MakePdfsShell extends Shell {
public $components = array('Document'); // <- this doesn't work
public function main()
{
$this->Document = $this->Components->load('Document'); // <- this doesnt work either
$this->Document->generate(); // <- this is what I want to do
}
...
I have some xml utilities that I use across some of my controllers. One of the controller launches a heavy task via the cake console, so that it can quietly run in the background via PHP CLI, while the user's request is immediately completed (once the task done, it will e-mail the results to the user).
The xml utilities are generic enough to be used in controller and shell, are too specific to the application to warrant them vendor status. The offered solution with the Lib folder does not work as in CakePhp v3 there seems to be no Lib folder.
After some quite some time spent, I managed to load my controller component to the shell task. Here is how to:
namespace App\Shell;
use Cake\Console\Shell;
use Cake\Core\App;
use Cake\Controller\Component;
use Cake\Controller\ComponentRegistry;
use App\Controller\Component\XmlUtilitiesComponent; // <- resides in your app's src/Controller/Component folder
class XmlCheckShell extends Shell
{
public function initialize() {
$this->Utilities = new XmlUtilitiesComponent(new ComponentRegistry());
}
...
$this->Utilities can now be used across my entire shell class.
You simply don't.
If you think you have to load a component in shell your application architecture is bad designed and should be refactored.
Technically it is possible but it doesn't make sense and can have pretty nasty side effects. Components are not made to run outside of the scope of a request. A component is thought to run within the scope of a HTTP request and a controller - which is obviously not present in a shell.
Putting things in the right place
Why does XML manipulation stuff have to go into a component? This is simply the wrong place. This should go into a class, maybe App\Utility\XmlUtils for example and have no dependencies at all to the request nor controller.
The logic is properly decoupled then and can be used in other places that need it. Also if you get incoming XML the right place to do this manipulation (by using your utility class) would be inside the model layer, not the controller.
You want to learn about Separation of Concerns and tight coupling
Because you've gone just against both principles.
https://en.wikipedia.org/wiki/Separation_of_concerns
What is the difference between loose coupling and tight coupling in the object oriented paradigm?
Search before asking
You could have tried to search via Google or on SO you would have found one of these:
using components in Cakephp 2+ Shell
CakePHP using Email component from Shell cronjob
Using a plugin component from shell class in cakephp 2.0.2
...
Be aware that some of them might encourage bad practice. I haven't checked them all.
I assume that you have a component named YourComponent:
<?php
App::uses('Component', 'Controller');
class YourComponent extends Component {
public function testMe() {
return 'success';
}
}
- with cake 2., you can load your component like this
App::uses('ComponentCollection', 'Controller');
App::uses('YourComponent', 'Controller/Component');
class YourShell extends AppShell {
public function startup() {
$collection = new ComponentCollection();
$this->yourComponent = $collection->load('Your');
}
public function main() {
$this->yourComponent->testMe();
}
}
- with cake 3. you can load your component like this
<?php
namespace App\Shell;
use App\Controller\Component\YourComponent;
use Cake\Console\Shell;
use Cake\Controller\ComponentRegistry;
class YourShell extends Shell {
public function initialize() {
parent::initialize();
$this->yourComponent = new YourComponent(new ComponentRegistry(), []);
}
public function main() {
$pages = $this->yourComponent->testMe();
}
}
If you are trying to access a custom XyzComponent from a shell, then you probably have commonly-useful functionality there. The right place for commonly-useful functionality (that is also accessible from shells) is in /Lib/.
You can just move your old XyzComponent class from /Controller/Component/XyzComponent.php to /Lib/Xyz/Xyz.php. (You should rename your class to remove the "Component" suffix, e.g., "XyzComponent" becomes "Xyz".)
To access the new location, in your controller, remove 'Xyz' from your class::$components array. At the top of your controller file, add
App::uses('Xyz', 'Xyz'); // that's ('ClassName', 'folder_under_/Lib/')
Now you just need to instantiate the class. In your method you can do $this->Xyz = new Xyz(); Now you're using the same code, but it can also be accessed from your Shell.
//TestShell.php
class TestShell extends AppShell{
public function test(){
//to load a component in dis function
App::import('Component', 'CsvImporter');
$CsvImporter = new CsvImporterComponent();
$data = $CsvImporter->get();
}
}
//CsvImporterComponent.php
App::uses('Component', 'Controller');
class CsvImporterComponent extends Component {
function get(){
//your code
}
}

MEF and loading EntityTypeConfiguration in runtime

You cannot vote on your own post
0
Hi.
I am developing this (http://arg-co.com/SabteNam%20-%20Copy.zip) windows application, and for my DAL I use Entity Framework. But every single extension has its own EntityTypeConfiguration, so I decided to use [Import] and [Export] to add them in OnModelCreating method of my DbContext.The problem here is that, in 'SabteNamDbContext' class which is located on 'SabteNamDataAccess' library, the '_Configs' is not initialized so I cant iterate it and add its items to 'modelBuilder.Configurations'.
In the source code of 'SampleConfiguration' class, I commented out '[Export(typeof(IDbConfiguration))]' but even Uncommenting this part of code, do not cause application to work properly.
Intresting point is that, if I use the following code in 'Main' windows form, the '_Configs' would be initialized :
[ImportMany(typeof(IDbConfiguration))]
public IEnumerable<EntityTypeConfiguration<object>> _Configs { get; set; }
How can this be fixed ?
While I realize this is probably no longer of use to you, we use a variation on this model from OdeToCode, which I advise you read.
In our case, we have created an interface for our extensions in general, not just for the entity configuration like Scott did, which allows us not only to load the configurations, but also factory and test data per extension, add new permission types to the core application, etc.
Our OnModelCreating looks something like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// load core object mappings
modelBuilder.Configurations.Add(new UserConfiguration());
modelBuilder.Configurations.Add(new PermissionConfiguration());
// get plugin assemblies
var catalog = new DirectoryCatalog("bin");
var container = new CompositionContainer(catalog);
container.ComposeParts();
var plugins = container.GetExportedValues<IPlugin>();
// load plugin object mappings
foreach (IPlugin plugin in plugins)
{
plugin.RegisterDomainEntities(modelBuilder.Configurations);
}
base.OnModelCreating(modelBuilder);
}

Resources