We have JUnit Serenity project in kotlin for testing of mobile applications.
We are trying to create custom widget element, but we are half-way successful.
Implementation in java - working
#ImplementedBy(MyWidgetJImpl.class)
public interface MyWidgetJ extends WidgetObject {
public void aaa();
}
public class MyWidgetJImpl extends WidgetObjectImpl implements MyWidgetJ {
public MyWidgetJImpl(final PageObject page, final ElementLocator locator, final WebElement webElement, final long timeoutInMilliseconds) {
super(page, locator, webElement, timeoutInMilliseconds);
}
#FindBy(xpath = "//*[#id='button_text_title']")
private WebElementFacade title;
#Override
public void aaa() {
System.out.println("*********************************" + title.getText());
}
}
This element is correctly initialized in PageObject and title is printed.
Implementation in kotlin - not working
#ImplementedBy(MyWidgetKImpl::class)
interface MyWidgetK : WidgetObject {
fun aaa()
}
class MyWidgetKImpl(page: PageObject, locator: ElementLocator, webElement: WebElement, timeOut: Long) : WidgetObjectImpl(page, locator, webElement, timeOut), MyWidgetK {
#FindBy(xpath = "//*[#id='button_text_title']")
private lateinit var title: WebElementFacade
override fun aaa() {
println("*********************************" + title.text)
}
}
In this case we are not able to build the project. Build errors are:
Error:(10, 1) Kotlin: MyWidgetKImpl inherits conflicting members:
public open fun waitUntilEnabled(): WebElementFacade! defined in
net.serenitybdd.core.pages.WidgetObjectImpl, public abstract fun waitUntilEnabled(): T! defined in
xx.xx.mobile.framework.xxx.MyWidgetK
28 errors of functions/members from WebElementFacade/WebElementFacadeImpl.
Do you know how to properly implement custom serenity Widget in kotlin?
Or by any chance is there different way to create WebElements with custom functionality?
Related
I've spent a few days trying to find out a solution, so I know all the basic answers
I've read the documentation, and I know, that #BeforeClass/#AfterClass is replaced with #BeforeAll/#AfterAll, #RunWith no longer exists; superseded by #ExtendWith
I've read all the topics here on the stackoverflow (removed links, since stackoverflow pretends it's a spam:(
I used to run some JUnit4 tests in the suites. Each Suite class Suite1.class, Suite2.class had several #Test methods and #BeforeClass/#AfterClass were running exactly before/after all the testing methods.
#RunWith(StopOnFailureSuite.class)
#Suite.SuiteClasses({
Test1.class,
Test2.class
})
public class TSuite_20 {
private static final byte SUITE_NUMBER = 20;
#BeforeClass
public static void setUpClass() {
//some logic for suite setup
}
#AfterClass
public static void tearDownClass() {
//some logic for teardown
}
}
As I wanted to use #ParameterizedTests I need to migrate to JUnit5.
And suddenly I realised, that exact the same behaviour, that used to be in JUnit4 is no more achievable.
[run some custom setup code; run several test classes, which may contain several #Test methods; run some custom tear down code];
Does anybody know (better with examples) an approach to make it with JUnit 5?
Option 1
This code will never execute BeforeAfterSuite#beforeAll and BeforeAfterSuite#afterAll
#ExtendWith(BeforeAfterSuite.class)
#Suite
#SelectClasses({
Test1.class,
Test2.class
})
public class TSuite_20 {
public static final byte SUITE_NUMBER = 20;
}
public class BeforeAfterSuite implements BeforeAllCallback, AfterAllCallback
/*,TestInstancePreConstructCallback, BeforeTestExecutionCallback,
AfterTestExecutionCallback, ExtensionContext.Store.CloseableResource*/ {
private static boolean started = false;
#Override
public void beforeAll(ExtensionContext context) {
if (!started) {
started = true;
//before stuff
}
}
#Override
public void afterAll(ExtensionContext context) throws Exception {
//after all;
}
}
Option 2
I was just curious, how will JUnit treat suite class if I put a test method into it...
This code will execute BeforeAfterSuite#beforeAll and BeforeAfterSuite#afterAll once, before and after TSuite_20#test
#ExtendWith(BeforeAfterSuite.class)
#Suite
#SelectClasses({
Test1.class,
Test2.class
})
public class TSuite_20 {
public static final byte SUITE_NUMBER = 20;
#Test
public void test() {
}
}
Option 3
We also can apply #ExtendWith(BeforeAfterSuite.class) per Test class which will results in a BeforeAfterSuite#beforeAll and BeforeAfterSuite#afterAll per Test class. (in this example - 2 times).
#ExtendWith(BeforeAfterSuite.class)
public class Test1 {
#Test
public void test11() {
}
#Test
public void test12() {
}
}
#ExtendWith(BeforeAfterSuite.class)
public class Test2 {
#Test
public void test21() {
}
#Test
public void test22() {
}
}
Option 4
I also give a shot for
a Suite class without #ExtendWith() and #BeforeAll + #AfterAll; (as expected nothing happened)`
a Suite class without #ExtendWith() and #BeforeAll + #Test + #AfterAll; (as expected single execution of BeforeAll/AfterAll for the specific Suite class)`
Option 5
Listeners were my last hope to achieve the desired behaviour.
I've created my own impl for LauncherSessionListener, just because I've thought it will allow me to execute smth exactly before tests start.
public class BeforeAfterSuiteLauncher implements LauncherSessionListener {
private static boolean started = false;
#Override
public void launcherSessionOpened(LauncherSession session) {
if (!started) {
started = true;
//before all
}
}
#Override
public void launcherSessionClosed(LauncherSession session) {
//after all
}
}
And I've added also some default impl CompositeLauncherSessionListener
Packages structure screenshot to show Java SPI configuration: LauncherSessionListener
For TestExecutionListener I've added two default impls, just to catch at least one Listener:
org.junit.platform.launcher.listeners.LoggingListener
org.junit.platform.launcher.listeners.SummaryGeneratingListener
and one custom
public class BeforeAfterExecutionListener implements TestExecutionListener {
#Override
public void testPlanExecutionStarted(TestPlan testPlan) {
//before all
}
#Override
public void testPlanExecutionFinished(TestPlan testPlan) {
//after all
}
}
Packages structure screenshot to show Java SPI configuration:TestExecutionListener
And only SummaryGeneratingListener was triggered!
What am I doing wrong? Why my BeforeAfterExecutionListener impl was not loaded and triggered?
P.S. All of the above code was executed under Intellij Idea 2021.1.3 Ultimate Edition
java version "1.8.0_341"
Java(TM) SE Runtime Environment (build 1.8.0_341-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.341-b10, mixed mode)
here is intelliJs command:
C:\Tools\jdk\bin\java.exe -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:61280,suspend=y,server=n -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:C:\Users\userName\AppData\Local\JetBrains\IntelliJIdea2021.1\groovyHotSwap\gragent.jar -javaagent:C:\Users\userName\AppData\Local\JetBrains\IntelliJIdea2021.1\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath C:\Users\userName\AppData\Local\Temp\classpath1705687115.jar com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit5 com.testdirectly.application.testcase.TSuite_20
Which results in JUnit5IdeaTestRunner
My gradle dependencies
dependencies {
//JUnit platform
// to group tests by package, by class name, by class name pattern, etc (use #Suite, #SelectClasses) :junit-platform-suite-api:1.9.2
// and to filter/discover and run them (SuiteLauncher, SuiteTestEngine, SuiteTestDescriptor) :junit-suite-engine:1.9.2
testImplementation "org.junit.platform:junit-platform-suite:1.9.2"
//Launcher, engine discovery
testImplementation "org.junit.platform:junit-platform-launcher:1.9.2"//to run tests
//JUnit Jupiter
//to use assertions and so on
testImplementation "org.junit.jupiter:junit-jupiter-api:5.9.2"
//to use #ParameterizedTest
testImplementation "org.junit.jupiter:junit-jupiter-params:5.9.2"
//Jupiter engine to run junit5 tests (JupiterTestEngine, Extensions, etc)
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.9.2"
}
Short answer
Option 5 works if you tweak it slightly (see below).
Long answer
Options 1 and 2 do not work because #ExtendWith is a Jupiter extension mechanism whereas #Suite triggers an engine of its own.
Test engines do not combine on the JUnit platform.
The same goes for option 4 since #BeforeAll and #AfterAll are Jupiter annotations.
Making Option 5 Work
First of all I'd suggest to use TestExecutionListener instead of LauncherSessionListener because the latter is still experimental.
Thus we have
package my.project.suites;
import org.junit.platform.launcher.*;
public class BeforeAfterSuiteListener implements TestExecutionListener {
#Override
public void testPlanExecutionStarted(TestPlan testPlan) {
System.out.println("before all");
}
#Override
public void testPlanExecutionFinished(TestPlan testPlan) {
System.out.println("after all");
}
}
The missing thing is now that you'll have to register BeforeAfterSuiteListener globally.
In classpath-based Java you do that through a resource file
META-INF/services/org.junit.platform.launcher.TestExecutionListener:
my.project.suites.BeforeAfterSuiteListener
Now before all and after all should show up in your output exactly once per test run.
Sample Code:
public class RediffLoginPage
{
Webdriver driver;
public RediffLoginPage(Webdriver driver){
this.driver=driver;
}
By username=By.xpath(".//*(#id='login1']");
By Password=By.name("passwd");
}
public Webelement Emailid()
{
return driver.findElement(username);
}
public Webelement Password(){return driver.findElement(Password);}
In this line,
By username=By.xpath(".//*(#id='login1']");
what's the purpose of first By keyword here?
It's object repository code for a testcase.
Class By
Class By extends java.lang.Object and is defined as:
public abstract class By
extends java.lang.Object
Mechanism used to locate elements within a document. In order to create your own locating mechanisms, it is possible to subclass this class and override the protected methods as required, though it is expected that all subclasses rely on the basic finding mechanisms provided through static methods of this class:
public WebElement findElement(WebDriver driver) {
WebElement element = driver.findElement(By.id(getSelector()));
if (element == null)
element = driver.findElement(By.name(getSelector());
return element;
}
By have the following direct known Subclasses:
By.ByClassName
By.ByCssSelector
By.ById
By.ByLinkText
By.ByName
By.ByPartialLinkText
By.ByTagName
By.ByXPath
By.Remotable
I need to refresh the page using the Serenity screenplay pattern. I tried to apply
user.attemptsTo(Hit.the(Keys.F5).into(element));
But it didn't work of course.
Serenity Screenplay does not have a convenience function for refresh, but it should be trivial to roll your own.
public class Refresh implements Task {
public Refresh() { }
public static Refresh thePage() {
return Tasks.instrumented(Refresh.class);
}
#Override
#Step("{0} refreshes the browser")
public <T extends Actor> void performAs(T actor) {
WebDriver driver = BrowseTheWeb.as(actor).getDriver();
driver.navigate().refresh();
}
}
Currently I have the following classes which use PageFactory to initialize the elements that I use.
Base Class:
public class BaseClass {
public static WebDriver driver;
public static boolean bResult;
public BaseClass(WebDriver driver){
BaseClass.driver = driver;
BaseClass.bResult = true;
}}
Login Page Classs that holds the elements:
public class LoginPage extends BaseClass{
public LoginPage(WebDriver driver)
{
super(driver);
}
#FindBy(how= How.XPATH, using="//input[#placeholder='Username']")
public static WebElement username;
Then I use a separate Login class for my actions:
public class Login {
//Login
public static void enterUsernamePassword(String username, String password) throws Exception{
LoginPage.username.sendKeys(username);
LoginPage.password.sendKeys(password);
}
Then my steps class:
#When("^I enter a valid username (.*) and password (.*)")
public void I_enter_a_valid_username_and_password(String username, String password) throws Throwable
{
PageFactory.initElements(driver, LoginPage.class);
Login.enterUsernamePassword(username, password);
}
As you can see I am using PageFactory within the steps class. I hate doing this and would like to put the PageFactory somewhere else, just not in the steps class.
Without adding or deleting any classes, where could I place the PageFactory class? Any help would be appreciated.
I found that the best to do it is by using an Inversion Of Control / Dependency Injection framework. I use Spring but there are other options as well, e.g. Guice, Weld, Picocontainer.
Using Spring you can annotate all pages with #Component and add a PageObject Bean postprocessor to initialize all page elements when the pages are being created.
This approach is also recommended by the Cucumber for Java Book by Aslak Hellesoy (Cucumber Ltd founder). Cucumber also provides
I want to integrate the code to take screenshot using remoteWebdriver inside, testng's onTestFailure(ITestResult). I am unable to get the current webdriver instance inside onTestFailure().
Okay, I had a similar problem.
And since there's no clear answer here, I would post my ( working) solution
On your BaseTest / TestBase class, make your RemoteWebdriver instance accessible ( either by having it as a public property or with a getter)
In the Listener class (In my case its a public property):
public class BaseTest {
public RemoteWebDriver remoteDriver;
//... initalize driver
}
public class TestListener implements ITestListener {
#Override
public void onTestFailure(ITestResult result) {
BaseTest test = (BaseTest) result.getInstance();
if (test == null) {
return;
}
RemoteWebDriver driver = test.remoteDriver;
}
}