I have basic understanding of Selenium architecture, where Search Context is main interface implemented by Web Driver interface which is extended by various browser driver class. Normally, we follow POM for our selenium projects and define each object by their By class -
By addButton=By.Id("asdf");
But just realized that we can even do-
addButton.FindElement(By.XPath("ABC").
But this does not return element like driver.FindElement(addButton)
When to use above statement?
The following is using terms from the Java-binding, but it holds true for the C#-Binding as well:
First
#spcial is right there is no "By.findElement(By)" defined in Selenium. Nevertheless there is a "By.findElement(SearchContext)" defined which I will explain below:
SearchContext vs. By
In Selenium you have an interface called a SearchContext and then you have the By class.
A SearchContext can be a WebElement or a WebDriver
Now you have two options to find an element (using pseudo-code):
1) SearchContext.findElement(By...)
or
2) By.findElement(SearchContext...)
Both do the same thing!
Say in your case you have a driver and the By variable like this:
WebDriver driver = new FirefoxDriver();
By addButtonLocator = By.id("asdf");
now you can find your element in two ways:
1) driver.findElement(addButtonLocator);
or
2) addButtonLocator.findElement(driver);
Again! Both do the same thing, it's just another way to "read" these expressions like this:
1) "take the driver and search for an element using this By-statement"
or
2) "take the By-statement and search for an element that fits this statement within driver"
As said before, instead of a driver you can have a smaller scope if you use an already identified element.
Related
I work with Specflow (if there is such a function there)
Programming language C#.
I want a screenshot to be taken after each selenium action.
Why? -> I want that after the first automation, a series of pictures of the successful test run is created (so that business analysts in the future can quickly see via pictures, what is tested with which test caseid).
It should be possible to specify via a flag whether the screenshot should be created or not. So my main problem is:
How do I ensure that my screenshot function is automatically called after each "driver.FindElement.... "and then "variable.click" or similar?
I don't want to manually insert "makeScreenshot()" everywhere.
It would be wonderful if a different method could be called after each method call. That looks up what the "command is". If the syntax then matches something like "Click()", the screenshot is triggered, otherwise not.
If there is no such thing. Something like "Screenshot(variable.Click())" would also be conceivable. That would still be clean and easily implemented everywhere via copy paste.
Does anyone have an idea or a link? (I'm a bit of a noob and need code examples to get started)
Potential solution 1:
You could add a extension method to your project for IWebElement, which would allow you to create your own custom click and find methods that screenshot after clicking. For example:
public static class IWebElementExtensions
{
public static void ClickAndScreenshot(this IWebElement element,
IWebDriver driver,
ScenarioContext scenarioContext)
{
element.Click();
Screenshot ss = ((ITakesScreenshot)driver).GetScreenshot();
ss.SaveAsFile($"{scenarioContext.ScenarioInfo.Title}_{scenarioContext.StepContext.StepInfo.Text.Replace("\"", "")}.png",
ScreenshotImageFormat.Png);
}
}
If you add the above class to your project and reference it in your steps, it would click the element, and then take a screenshot and add the scenario and step info to the filename to identify it later (consider how you plan to manage these screenshots, might be worth adding a subfolder per scenario, or datetimes to the filename for diagnostics).
You can then make use of this method in your Specflow steps:
[When(#"I click a login button")]
public void WhenIClickALoginButton()
{
By someLoginButton = By.Id("login");
_driver.FindElement(someLoginButton).ClickAndScreenshot(_driver, _scenarioContext);
}
You could do something similar for finding the element. You could create an extension method that finds the element, takes a screenshot and then returns the element as a return type to be used by your WebDriver.
You would have to change your current click() methods to take advantage of this new method (find and replace should help make this trivial), but your screenshot code would all be in one place if you need to change it. Doing it this way means you can pick and choose when to call that method as well depending on your steps, as you could stick to the original Click() method if not interested in a screenshot in a particular step.
Potential Solution 2:
I feel like taking a screenshot after every Specflow step rather then each Selenium action is worth mentioning as a potential solution, focusing on chunks of user actions rather then every Selenium action.
For example, you could configure a hooks file to take a screenshot after every specflow step:
[Binding]
public class Hooks
{
IWebDriver _driver;
public Hooks(IWebDriver driver)
{
_driver = driver;
}
[Scope(Tag = "screenshot")]
[AfterStep]
public void TakeScreenshotAfterStep(ScenarioContext scenarioContext)
{
Screenshot screenshot = ((ITakesScreenshot)_driver).GetScreenshot();
screenshot.SaveAsFile($"{scenarioContext.ScenarioInfo.Title}_{scenarioContext.StepContext.StepInfo.Text}.png",
ScreenshotImageFormat.Png);
}
[AfterScenario]
public void AfterScenario()
{
_driver.Dispose();
}
}
For a example Feature:
Feature: Login
#screenshot
Scenario: Successful Login
GIVEN I navigate to stack overflow
WHEN I login
THEN I am logged in
#someOtherTag
Scenario: API test with no UI
GIVEN I have a cool API
WHEN I do a GET
THEN I GET some cool stuff
This would generate 3 screenshots after each step for the Successful login scenario, but it wouldn't take any for the API test due to the tag scope on the after step, which should tick the box around being able to specify when to do this.
The above relies on your Specflow steps not being too large though... if you're doing multiple large chunks of user actions within one step, it may skip a lot, but if that's only a rare occurrence you could do some extra screenshot taking within those steps using the extension from potential solution 1.
I hope this helps, I apprieciate it's not a pure answer but from experience of when these sorts of screenshots can be useful, I'd recommend solution 2 and use it as an opportunity to make sure your Specflow steps aren't doing too much.
When I declare my IWebElement like this:
[FindsBy(How = How.CssSelector, Using = "input#raffle_submit")]
private IWebElement _buyNowButton;
And initialize it in the class constructor using PageFactory:
PageFactory.InitElements(Driver, this);
Then when I make a break point in any method in order to look at the element's properties I see no properties in this element:
public CartObj ClickBuyNowButton()
{
_buyNowButton.Click(); //here is my break point
}
Only property I see is: - Non-Public members : http://prntscr.com/8k90r4
So where are all properties that must be in IWebElement like "Enabled", "Displayed" and so forth?
More to say: I don't have any problems with using this element, I can click on it, I can send keys to it but when I use the next JavaScript code it tells me that argument is wrong (but it worked well before):
((IJavaScriptExecutor)Driver).ExecuteScript("arguments[0].scrollIntoView();", webElement);
Error that I appears after executing JS code above:
Additional information: Argument is of an illegal
typeOpenQA.Selenium.Support.Events.EventFiringWebDriver+EventFiringWebElement
I'm sure it's connected with version of WebDriver.
So my current WebDriver version is 2.47.0
ChromDriver version is 2.19
To your first question: What you see in the debugger is actually just a proxy object. The PageFactory initializes your _buyNowButton with a proxy which will only be resolved once you use it somewhere in your script. So unless you actually use it, you won't see any properties like Displayed or Enabled in the debugger.
As to your second question: I can't recreate that now with Selenium 3 and the latest ChromeDriver, but it may have been a bug back in the day.
I am new to C# and am trying to use SpecFlow as I used to use Gherkin by giving a unique name to an item and then passing in the name in the Step Definition. My question is about how to add in the identifier when I create an object so I can call the object without having to pass in the actual name of the object every time that I create a step.
So, for instance the code would look something like this:
[When(#"I click the (.*) button")]
public void ClickTheButton(string ButtonName)
{
driver.Click(ButtonName)
//ButtonName would be a string that would call to the ID for the ADD button
}
I want to be able to put in something like "Add" (so the line would read "When I click the ADD button") and then have the code search for the "ADD" identifier.
I know that this is possible in Ruby/Cucumber by using a DOM and then passing in XML with gherkin names. In Ruby/Cucumber the object would look something like this:
<button gherkin_name="ADD" key="id" value="add_button_12685"/>
However, I am finding absolutely no way of doing that in C# with SpecFlow and this is something that I really need to be able to do.
Is there a way to do this at all? All I'm really trying to do is link a handle/parameter name that business users could actually use to a Page Object like you can in Ruby/Cucumber without making the user know the code in the background. And, incidentally, the names of the objects are almost exactly like the gherkin line that I added in, thus they are very weird to have a user write. This is the reason that I'd like to have just an identifier for the user.
Thanks in advance for your help.
EDIT: I realise now I was not clear enough in my original post so perhaps some background will help. I am using Selenium-Webdriver to test a website that has hundreds of items on it. Writing a different step for every single item on every single page would be exceedingly tedious and time consuming. Because there are many of the exact same items with the exact same characteristics (for instance there are something like 50 buttons that all behave similarly on a single page and the site is dozens of pages) on the pages, writing a single method for testing them seems the most logical idea. Identifying these items with an identifier that the business could use would cut down on bulk inside of the Steps, the number of steps written, and the likelihood that the business users would feel comfortable using the code which is the end goal.
You can do what you want if you are using the PageObject pattern and have a property Buttons (probably on a base PageObject class) which exposes the available buttons as a collection (which can be done via reflection) and then you can just do something like:
[When(#"I click the (.*) button")]
public void ClickTheButton(string ButtonName)
{
myPage.Buttons.First(button=>button.Name==ButtonName).Click;
}
but I would take what AutomatedChaos said into consideration and not use this in a step in the gerkin but just have this as a helper method something like this
[When(#"I add a widget")]
public void AddAWidget(string ButtonName)
{
ClickTheButton("Add")
}
private void ClickTheButton(string ButtonName)
{
myPage.Buttons.First(button=>button.Name==ButtonName).Click;
}
your Buttons property doesn't have to be done with reflection, the simplest implementation is something like this:
public IEnumerable<IWebElement> Buttons
{
yield return AddButton;
yield return RemoveButton;
yield return SomeOtherButton;
//etc etc
}
but using reflection will mean that as you add buttons to the page object you don't need to remember to add them to this method, they will be found automatically.
SpecFlow is only the BDD framework. It will not drive browsers itself, you need to install additional packages that drives the browser.
With C#, you have a few options:
Selenium, the best known and works with the Page Object you are accustomed with.
Fluent Automation, an upcoming library that works as a wrapper on top of Selenium, and makes the interfacing easier (more natural language)
CodedUI, Microsofts web and UI test solution that comes natively with Visual Studio Test edition.
On a personal note, I consider Selenium (with or without Fluent Automation) the best fitted to work with SpecFlow (comparisson)
If you want to install Selenium or other packages, you can install the NuGet package manager to easily search, install and update packages for you.
Lastly, have you considered to use more domain specific Gherkin phrases like When I add a Wabberjock instead of When I press the Add button? This is where the power of BDD lies: Exposing the intention while hiding the implementation details.
I am building a Page Object Model in Selenium WebDriver for C#, using the PageFactory.
Unfortunately, I have discovered that the FindsByAttribute will not initialize a SelectElement (HTML <select> tag / dropdown menu). I've happened upon or come up with a few ideas to work around it so far, but none of them is ideal:
PageFactory and FindsByAttribute are sealed, so I can't force it to by just inheriting those.
Manually instantiating a SelectElement from an IWebElement in each method is rather messy and duplicative. It also ignores the apparent built-in wait in PageFactory and throws NoSuchElementExceptions unless I add a wait every time I do this -- which would require repeating the locator all over the place, defeating (part of) the purpose of the POM.
Wrapping each IWebElement property with a SelectElement property is less messy, but still has the same waiting problem as above.
The best option so far is #3, and writing a wrapper for SelectElement that just adds a wait to every method. While this solution will work, it will bulk up the code of each page a lot, as instead of this (hypothetical) pretty code:
[FindsBy(How = How.Id, Using = "MonthDropdown")]
public SelectElement MonthDropdown;
I'm stuck with a wrapper wrapper (something I'd rather avoid), and:
[FindsBy(How = How.Id, Using = "MonthDropdown")]
private IWebElement _monthDropdown;
public Selector MonthDropdown
{
get { return new Selector(MonthDropdown, Wait); }
}
With Selector being the SelectElement wrapper, that also has to take in the IWait<IWebDriver> so it can wait, and instantiating a new Selector every time I access it.
Is there a better way of doing this?
EDIT: Sleepily put in wrong access modifiers. Fixed. Thanks, #JimEvans.
First, there's no "built-in wait" in the .NET PageFactory implementation. You can easily specify one in the call to InitElements (more on that in a bit). At present, the best option for you would be your option 3, though I wouldn't expose the IWebElement member; I'd make it private, since the PageFactory can enumerate over private members just as easily as public ones. So your page object would look like this:
[FindsBy(How = How.Id, Using = "MonthDropdown")]
private IWebElement dropDown;
public SelectElement MonthDropdownElement
{
get { return new SelectElement(dropdown); }
}
How do you get the actual IWebElement when you need it? Since SelectElement implements IWrappedElement, you can simply call the WrappedElement property if you need access to the methods and properties of the element provided by the IWebElement interface.
Recent versions of the .NET bindings have restructured the PageFactory to be more extensible. To add the "built-in wait" you desire, you could do the following:
// Assumes you have a page object of type MyPage.
// Note the default timeout for RetryingElementLocator is
// 5 seconds, if unspecified.
// The generic version of this code looks like this:
// MyPage page = PageFactory.InitElements<MyPage>(new RetryingElementLocator(driver), TimeSpan.FromSeconds(10));
MyPage page = new MyPage();
PageFactory.InitElements(page, new RetryingElementLocator(driver, TimeSpan.FromSeconds(10)));
Additionally, if you really need to customize how things work, you're always welcome to implement IPageObjectMemberDecorator, which allows you to fully customize how attributes are enumerated and values set to the properties or fields decorated with those attributes. One of the (non-generic) overloads of PageFactory.InitElements takes an instance of an object implementing IPageObjectMemberDecorator.
I'll leave aside that proper implementations of the Page Object Pattern as strictly defined shouldn't expose any WebDriver objects outside of each page object. Otherwise, all you're implementing is a "page wrapper," which is a perfectly valid approach, just not what one would call a "page object."
I am very new to Selenium, so my apologies if it's a silly question.
I have successfully wired up IntelliJ (Play! framework) with Selenium, and created some tests using firefoxDrivers.
I'm trying to check if the page had been validated properly.
long story short, I'm selecting an element like this:
WebElement smallDecel = firefoxDriver.findElement(By.cssSelector("#configTable tr:nth-child(2) td .playerDecelInput"));
I do some further operations (clear and change the value, submit the 'form'), and then I want to check if the TD the input sits in was given another class.
So, the question is - is there a simple technique I can use to find out if a WebElement / DOM has a class specified?
To expand on Sam Woods' answer, I use a simple extension method (this is for C#) to test whether or not an element has a specified class:
public static bool HasClass( this IWebElement el, string className ) {
return el.GetAttribute( "class" ).Split( ' ' ).Contains( className );
}
Once you find the element, you can just call myElement.GetAttribute("class"). Then you can parse the string that is returned and see if it contains or does not contain the class name you care about.
You can use FindElement(By.ClassName(//name of your class)); I would recommend that you either loop through and search the DOM for a set period of time or set a Thread.sleep(xxxx) and then look for the appended class.