I am currently writing unit tests for my selenium project and I am using Mockito to mock up my webelements and drivers.
The problem I am having is that I have a function that is used to change the radio option in a list of radio buttons but I am having a problem with this. the code looks like this:
#Test
public void testChangeRadioState(){
WebElement mockElement = mock(WebElement.class);
List<WebElement> mockElementList = new ArrayList<>();
WebElement selectedMockElement = mock(WebElement.class);
/*The when statements*/
when(selectedMockElement.isSelected()).thenReturn(true);
doReturn(when(mockElement.isSelected()).thenReturn(true)).when(mockElement).click();
doReturn(when(selectedMockElement.isSelected()).thenReturn(false)).when(mockElement).click();
/*Add a selected and a none selected element to the list*/
mockElementList.add(mockElement);
mockElementList.add(selectedMockElement);
/*The method that is beeing tested*/
elementSetter.changeRadioState(mockElementList);
Assert.assertTrue("The radio state was not selected",mockElement.isSelected());
}
What I am trying to do int he doReturn part is to tell the element "mockElement" that when it recieves a click it should allways return true on a isSelected() call. but since Click() is a void function it won't let me do that. Anybody know a way around this?
Ok, it is separate topic - what you are testing and would I mock things so deep.
I would just rewrite test like this:
#Test
public void testChangeRadioState() {
WebElement mockElement = mock(WebElement.class);
WebElement selectedMockElement = mock(WebElement.class);
List<WebElement> mockElementList = new ArrayList<>();
/*The when statements*/
when(selectedMockElement.isSelected()).thenReturn(true);
// By default mockito will return false but maybe I want to highlight
// that this is important
when(mockElement.isSelected()).thenReturn(false);
/*Add a selected and a none selected element to the list*/
mockElementList.add(mockElement);
mockElementList.add(selectedMockElement);
/*The method that is beeing tested*/
elementSetter.changeRadioState(mockElementList);
verify(selectedMockElement).click();
// according to test method name I would add
// one more verification that something was dis-selected
}
Another variant with state which I think has unnecessary mocks:
boolean selected;
#Test
public void testChangeRadioState() {
selected = false;
WebElement mockElement = mock(WebElement.class);
WebElement selectedMockElement = mock(WebElement.class);
List<WebElement> mockElementList = new ArrayList<>();
/*The when statements*/
when(selectedMockElement.isSelected()).thenReturn(true);
doAnswer(new Answer<Object>() {
public Object answer(InvocationOnMock invocation) {
selected = true;
return null;
}
}).when(mockElement).click();
/*Add a selected and a none selected element to the list*/
mockElementList.add(mockElement);
mockElementList.add(selectedMockElement);
/*The method that is beeing tested*/
elementSetter.changeRadioState(mockElementList);
Assert.assertTrue("The radio state was not selected", selected);
// according to test method name I would add
// one more verification that something was dis-selected
}
But again there is misleading in names. For example I would expect that there are elements which don't become selected when they clicked. Question again about what you are testing
Related
I have a list of rows in the table and a moveUpButton, it will move selected rows to one step up. It works fine but I need to write unit test using white box for this method.
Here is my code:
public void registerMoveUpBut(final Button moveUpBut){
this.moveUpBut = moveUpBut;
moveUpBut.addActionListener(new ActionListener){
public void actionPerformed(ActionEvent event){
Transferevent obj = transModel.getElementAt(TransTable.getSelectedRow);
TransferObj transferObj = new TransferObj(obj);
transferObj.setEventType(TransferTypeEnum.UP);
transferObj.setEventNum(obj.getEventNum()-1)
}
}
}
Unit test I have so far is
#Test
public void testMoveUpBut(){
Controller Con = mock(Controller.class)
JButton moveUpBut = Whitebox.getInternalState(Con, "moveUpBut");
//here i want to use something like addListener so that it will pass through my
//method and use ArgumentCaptor to verify whether it's population the data or
//not. Can you pls help with that, Thank you!
moveUpBut.addActionerListener() //something like this
ArgumentCaptor<Controller>argument = ArgumentCaptor.forClass(Controller.class);
verify().doSomething(argumentCapture));
}
I am trying to click on an option (array of images) from an unordered list using Selenium Java.
I have tried doing a click on the className of "attachment" but that doesn't seem to pick up the list item that I need to click on.
Is there anyway to pick up something like the data-id?
The code that I have tried:
public void click(By by) {
waitProvider.waitFor().waitUntilElementIsClickable(by);
WebElement clickableElement = locate(by);
Actions actions = new Actions(webDriverProvider.driver());
actions.moveToElement(clickableElement);
actions.click().perform();
}
and then we do:
public void selectFirstImage() {
click("attachment");
}
locate:
public WebElement locate(By by) {
waitProvider.waitFor().waitUntilVisibilityOfElementLocatedBy(by);
scrollIntoView(by);
return webDriverProvider.driver().findElement(by);
}
I think click method call is incorrect.
Since click expects By not String it should look like this:
click(new ByCssSelector(".attachment-preview"));
firstli i write my scenario:
go http://demo.opencart.com/
search ipod
click add to compare in every found items
here is my code (java webdriver pagefactory)
searchresultspage (my object page)
#FindBy(id = "compare-total")
WebElement numberOfProductToCompare;
public void compareAllItems() {
for (WebElement compareButtons: compareButton) {
compareButtons.click();
}
}
public void areAllItemsClickedCompare() {
String text = numberOfProductToCompare.getText();
System.out.println(text);
}
My main test class
#Test
public void addToCompare() {
searchresultspage.compareAllItems();
searchresultspage.areAllItemsClickedCompare();
}
i click all compare buttons and i want to get number from link Product Compare (4) but when i use searchresultspage.areAllItemsClickedCompare(); then System.out.println(text); print me Product Compare (0), even this method is after adding to compare (should be should be Product Compare (4)) Dont know what to do, some advice?
Think, the problem appears because the compare-total element is captured by Selenium on class instantiating, but when its content is updated on the page, it doesn't affect the captured value.
Try to capture the compare-total element only after all checkboxes were clicked:
public void areAllItemsClickedCompare() {
WebElement numberOfProductToCompare = driver.findElement(By.id("compare-total"));
String text = numberOfProductToCompare.getText();
System.out.println(text);
}
Where driver is your WebDriver instance.
I am testing a web page using Webdriver and Java.
The test page contains List of Records and i click on the "Employee ID" title hyperlink and the records should be sorted in the ascending order of the employee IDs and there should be a small icon beside the "Employee ID" column indicating that the results are now sorted.
Here is the code i have:
public Class ResultsPage extends SlowLoadableComponent<ResultsPage> {
#FindAll({ #FindBy(how = How.XPATH, using = "some xpath"), #FindBy(how = How.XPATH, using = "another xpath") })
public List<WebElement> resultsTableElement;
#FindBy(how = How.XPATH, using = "//a[#title='A system assigned identifier for the Employee record.']")
public WebElement employeeIDColumnTitle;
#FindBy(how = How.XPATH, using = "//a[#title='A system assigned identifier for the Employee record.']/following-sibling::img")
public WebElement ascOrDescIcon;
public ResultsPage(WebDriver driver) {
super(new SystemClock(),20);
this.driver = driver;
wait = new WebDriverWait(driver, Start.TIME_OUT);
PageFactory.initElements(driver,this);
}
#Override
protected void load() {
PageFactory.initElements(driver, this);
LOGGER.info("From the load method");
}
#Override
protected void isLoaded() throws Error {
boolean loaded = false;
if (resultsTableElement.size() > 0) {
loaded = true;
}
LOGGER.warn("isloaded method failed ");
Assert.assertTrue(loaded, "Looks like the Claim Results Search Frame is not loaded yet");
}
public void testThis() {
//some code here
systemIDColumnTitle.click();
PageFactory.initElements(driver, this); //Calling the initElements of the same page again to see that the element
LOGGER.info(ascOrDescIcon.getAttribute("src")); //This line always fails saying that the element is not found.
}
}
From the comment you have specified, why can't you have locator defined in the same page object like:
#FindBy(id="newElement")
private WebElement newElement
This newElement points to the new element that got created after certain operation.
Edit:
As per your comment, you can directly call the get methods rather than instantiating another time. Kindly understand, that all #FindBy WebElements are proxys; Only when you call methods on it, they will be fetched (using the locator you have given) and does the specific operation.
So thats the reason, you can have the elements on your pageobject and calling initElements wont throw error, even the #FindBy aren't found!
I use selenium IDE to initially record the tests and save them as Java WebDriver tests.
When I go into an input field, delete all the text and enter a new value, it records that as 2 commands:
driver.findElement(By.id("username")).clear();
driver.findElement(By.id("username")).sendKeys("johnnyleitrim");
One problem with this for me is that the clear() event fires a Javascript change event for the "username" field. This does not happen when I use the browser itself - it waits until the field loses focus before firing the change javascript event, and that's what I want to emulate in Selenium.
The reason I need this is that I do validation on the change() event, and when change is called with an empty value, it displays an alert telling the user the information is invalid - and this alert stops Selenium
So how do I clear the field without using WebElement.clear()?
You can avoid using the clear() method and use the Actions class to clear and set text in one go, therefore firing the onchange() event only once the text is set.
Call the below method like:
ClearAndSetText(By.id("username"),"johnnyleitrim");
The method clicks the element, selects the existing text using shift+home keys,clears using backspace, and then types in the new text - just like how a user would do.
public void ClearAndSetText(By by, string text)
{
WebElement element = driver.findElement(by);
Actions navigator = new Actions(driver);
navigator.click(element)
.sendKeys(Keys.END)
.keyDown(Keys.SHIFT)
.sendKeys(Keys.HOME)
.keyUp(Keys.SHIFT)
.sendKeys(Keys.BACK_SPACE)
.sendKeys(text)
.perform();
}
You can try it using JavaScriptExecutor (although I haven't tested it).
JavaScriptExecutor js = (JavaScriptExecutor) driver;
js.executeScript("document.querySelector(\"input[id='username']\").value = ''");
Seems like it's a known Selenium bug. There were a few options as workarounds mentioned on the bug page, but they all meant having to "heavily" modify the code returned from Selenium IDE. Instead, I decided to create a Proxy which would do the work for me without too much modification to the IDE generated code:
protected WebElement findElement(By criteria) {
try {
WebElementHandler webElementHander = new WebElementHandler(seleniumWebDriver.findElement(criteria));
return (WebElement) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{WebElement.class}, webElementHander);
} catch (NoSuchElementException e) {
logger.error("Could not find " + criteria + " on page " + seleniumWebDriver.getCurrentUrl());
throw e;
}
}
private class WebElementHandler implements InvocationHandler {
private WebElement proxiedElement;
private WebElementHandler(WebElement proxiedElement) {
this.proxiedElement = proxiedElement;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("clear")) {
Keys[] keys = new Keys[proxiedElement.getAttribute("value").length()];
for (int i = 0; i < keys.length; i++)
keys[i] = Keys.BACK_SPACE;
proxiedElement.sendKeys(Keys.chord(keys));
return null;
}
return method.invoke(proxiedElement, args);
}
}