How do I pass the values between two steps in cucumber JVM?
In the following scenario, I want to access username provided When step in Then step.
How do I pass the values between two steps in cucumber JVM? Currently I'm accessing those by saving that value into a public variable. Is the approach correct (or) any other way I can access those between the steps?
Scenario:
Given user is on login page
When user enters username as user1 and password as pass1
And clicked on login button
Then post login page is displayed
#When("^user enters username as ([^\"]*) and password as ([^\"]*)$")
public void enterLoginDetails(String username,String password){
driver.findElement(By.id("username")).sendKeys(username);
driver.findElement(By.id("password")).sendKeys(password);
}
In the following step definition, I want to access username from the previous step definition
#Then("^post login page is displayed$")
public void postLoginValidation(){
// i would like access username and verify username is displayed
}
Thanks in Advance
Best Solution :
If you are using scenario outline:
When user enters username as <username> and password as <password>
Then post login page is displayed with <username>
Examples:
| username|password|
|Kiran|1234|
Step Defination Code:
#When("user enters username as (.*) and password as (.*)")
public void enterLoginDetails(String userName,String password)
{
//You will get userName=Kiran and Password=1234
}
#Then("post login page is displayed with (.*)")
public void postLoginValidation(String userName)
{
//You will be access the same username which is you are passing while login
////You will get userName=Kiran
}
public your class {
private String usr;
#When("^user enters username as ([^\"]*) and password as ([^\"]*)$")
public void enterLoginDetails(String username,String password){
usr = username;
...
}
#Then("^post login page is displayed$")
public void postLoginValidation(){
//do something with usr
}
}
You could share state between steps using variables, as suggested by Bala.
Another solution in Java is to use dependency injection. Cucumber-JVM support many different dependency injection frameworks.
One of them is Spring. Dependencies can be made available where they are needed using annotations. Using Spring is a good option if your project is already using Spring. Otherwise it might be too big and cumbersome.
An easy to use alternative to Spring, is to use PicoContainer.
For more information on either, please have a look at:
http://www.thinkcode.se/blog/2017/06/24/sharing-state-between-steps-in-cucumberjvm-using-spring
http://www.thinkcode.se/blog/2017/04/01/sharing-state-between-steps-in-cucumberjvm-using-picocontainer
Related
I am trying to add new field to the user profile (student number) and allow users to login using either email or the new field (student number) with the same password for both.
I have overridden login.jsp to allow both Email and Student Number.
My idea is to override the login action command with something similar to the code below:
#Component(
property = {
"javax.portlet.name=com_liferay_login_web_portlet_LoginPortlet",
"mvc.command.name=/login/login"
},
service = MVCActionCommand.class
)
public class CustomLoginActionCommand extends BaseMVCActionCommand {
#Override
protected void doProcessAction(ActionRequest actionRequest,
ActionResponse actionResponse) throws Exception {
ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
WebKeys.THEME_DISPLAY);
HttpServletRequest request = PortalUtil.getOriginalServletRequest(
PortalUtil.getHttpServletRequest(actionRequest));
HttpServletResponse response = PortalUtil.getHttpServletResponse(
actionResponse);
String login = ParamUtil.getString(actionRequest, "login");
String password = actionRequest.getParameter("password");
boolean rememberMe = ParamUtil.getBoolean(actionRequest, "rememberMe");
String authType = CompanyConstants.AUTH_TYPE_EA;
String email = "";
if(isValidEmail(login)){ //if the user trying to login with his email
email = login ;
}
else if(isNumeric(login)){ //check if the user trying to login with his student number
//fetch User by Student Number (login)
//e.g. fetchUserByStudentNumber(login)
//get the Email Adress for the retrieved user object and use it to login
email = user.getEmailAddress();
}
else{
// Exception
}
AuthenticatedSessionManagerUtil.login(request, response, email, password, rememberMe, authType);
actionResponse.sendRedirect(themeDisplay.getPathMain());
}
}
is this the right way to achive similar requierment?
in Liferay 7.4 U46+, we can extend supported system services with Liferay Objects. so I have two options to extend the User Profile, 1- by adding a new field to the User object. or 2- by creating a new "custom field". which option is better?
in both options, how to force unique values in the added field (student number)?
how to retrieve user object by using added field (fetchUserByStudentNumber)?
Appreciate your feedback!
Thanks
Overwriting the portal login command is possible, but I would rather use a custom Authenticator to not overwrite other logic implemented in the MVC action component. As you want booth (mail and student number), you could implement authenticateByEmailAddress like in Password-Based-Authentication-Pipelines and check both authentication results with a boolean OR approach.
Extending portal model objects should rather be implemented via Custom Fields. Fetching a user like in fetchUserByStudentNumber you will probably need the ExpandoValue service and a dynamic query. Maybe there are better approached, but this is what comes into my mind first.
Im looking for a clean way to test all features on the webpage with 2 different users.
One user is the admin, the second one a normal user.
Here is the overview of my selenium tests:
As you can see, we have 3 different features on the webpage:
UnlockInstruction
Tac
UploadCodes
Each of these features has its own Test class with its own webDriver so im able to run the tests in parallel.
Each of these test files, is calling the Login Class inside the SetUp.
What the Login Class is doing is:
Open Website with goToUrl
Gets the username and password which is stored in a Password Manager tool
Use selenium to enter username, password and click on login
Wait until page after login is loaded and go back to test methods
Everything works perfectly when i test it for one user. All the test run in parallel.
Now i want to test all the same features with the admin user.
The only way which comes into my mind is, to just create another Login class, which gets the other users credentials and copy also the 3 test classes, so all the 6 tests run in parallel.
But in my opinion its not clean, because i would copy 4 files which would have nearly 1:1 the same code.
I'd make the user id and password arguments to the fixtures.
Use a parameterized TestFixture (one with arguments)
Give the same argument types to the constructor so that NUnit can pass them to you.
Your four files will then result in six instances being constructed.
[TestFixture("normalUser", "normalUserPassword")]
[TestFixture("adminUser", "adminUserPassword")]
public class SomeFixture
{
private string User { get; }
private string Password { get; }
public SomeFixture(string user, string password)
{
User = user;
Password = password;
}
[OneTimeSetUp]
public void SetUpTheFixture()
{
// Create the driver
}
...
}
If you prefer to lookup the password, then just make the user id the only argument and look up the password when you need it.
[TestFixture("normalUser")]
[TestFixture("adminUser")]
public class SomeFixture
{
private string User { get; }
public SomeFixture(string user)
{
User = user;
}
[OneTimeSetUp]
public void SetUpTheFixture()
{
// Look up password
// Create driver
}
...
}
I am trying to get the vales as DataTable in cucumber. How can this be done in QAF ?
#test
Scenario: DataTable Examples
Given I am on github login page
And I enter usernames and passwords "${args[0]}"
| testuser_1 | Test#153 |
StepDefinition
#And("^I enter usernames and passwords \"([^\"]*)\"$")
public void iEnterUsernamesAndPasswords(Map<Object, Object> data) {
System.out.println("------------->" +data.get(0));
}
Error Msg:
[WINDOWS 91.0.4472.77]: ▀ ▀ ▀ END STEP: Given I am on github login page ▀ ▀ ▀
17:28:22.608 [[WINDOWS 91.0.4472.77]] ERROR com.qmetry.qaf.automation.step.StepNotFoundException -
/**
* Auto-generated code snippet by QMetry Automation Framework.
*/
#QAFTestStep(description="I enter usernames and passwords {0}{1}")
public void iEnterUsernamesAndPasswords(String str0,Object[] objArray1){
//TODO: remove NotYetImplementedException and call test steps
throw new NotYetImplementedException();
}
/**
* Auto-generated code snippet by QMetry Automation Framework.
*/
#QAFTestStep(description="I enter usernames and passwords {0}{1}")
public void iEnterUsernamesAndPasswords(String str0,Object[] objArray1){
//TODO: remove NotYetImplementedException and call test steps
throw new NotYetImplementedException();
}
args[0] is used when you have data-driven scenario (scenario with examples) and you want to refer entire record from data provider. In this case as you want to pass map you can try following way:
#test
Scenario: DataTable Examples
Given I am on github login page
And I enter usernames and passwords "{'username':'testuser_1', 'password':'Test#153'}"
Or
#test
Scenario: DataTable Examples
Given I am on github login page
And I enter usernames and passwords
| username | password |
| testuser_1 | Test#153 |
You can refer feature files and step implementation.
I am creating a UI test automation framework using POM design pattern. After reading SeleniumHQ page for Page Objects, I am contemplating what all methods should I create inside a Page Object.
Let's take a simple example of login page object consisting of username, password textboxes and submit button. SeleniumHQ link has created the below methods :
1. typeUsername(String username)
2. typePassword(String password)
3. submitLogin()
4. submitLoginExceptionFailure()
5. loginAs(String username, String password)
I am a bit perplexed while looking at these methods. Why should I create first 3 methods(typeUsername, typePassword,submitLogin), when I already am creating a loginAs method. Any thoughts around it ?
SeleniumHQ Link - https://github.com/SeleniumHQ/selenium/wiki/PageObjects
Pasting the code PageObject code for LoginPage :
public class LoginPage {
private final WebDriver driver;
public LoginPage(WebDriver driver) {
this.driver = driver;
// Check that we're on the right page.
if (!"Login".equals(driver.getTitle())) {
// Alternatively, we could navigate to the login page, perhaps logging out first
throw new IllegalStateException("This is not the login page");
}
}
// The login page contains several HTML elements that will be represented as WebElements.
// The locators for these elements should only be defined once.
By usernameLocator = By.id("username");
By passwordLocator = By.id("passwd");
By loginButtonLocator = By.id("login");
// The login page allows the user to type their username into the username field
public LoginPage typeUsername(String username) {
// This is the only place that "knows" how to enter a username
driver.findElement(usernameLocator).sendKeys(username);
// Return the current page object as this action doesn't navigate to a page represented by another PageObject
return this;
}
// The login page allows the user to type their password into the password field
public LoginPage typePassword(String password) {
// This is the only place that "knows" how to enter a password
driver.findElement(passwordLocator).sendKeys(password);
// Return the current page object as this action doesn't navigate to a page represented by another PageObject
return this;
}
// The login page allows the user to submit the login form
public HomePage submitLogin() {
// This is the only place that submits the login form and expects the destination to be the home page.
// A seperate method should be created for the instance of clicking login whilst expecting a login failure.
driver.findElement(loginButtonLocator).submit();
// Return a new page object representing the destination. Should the login page ever
// go somewhere else (for example, a legal disclaimer) then changing the method signature
// for this method will mean that all tests that rely on this behaviour won't compile.
return new HomePage(driver);
}
// The login page allows the user to submit the login form knowing that an invalid username and / or password were entered
public LoginPage submitLoginExpectingFailure() {
// This is the only place that submits the login form and expects the destination to be the login page due to login failure.
driver.findElement(loginButtonLocator).submit();
// Return a new page object representing the destination. Should the user ever be navigated to the home page after submiting a login with credentials
// expected to fail login, the script will fail when it attempts to instantiate the LoginPage PageObject.
return new LoginPage(driver);
}
// Conceptually, the login page offers the user the service of being able to "log into"
// the application using a user name and password.
public HomePage loginAs(String username, String password) {
// The PageObject methods that enter username, password & submit login have already defined and should not be repeated here.
typeUsername(username);
typePassword(password);
return submitLogin();
}
}
You might want to check if typing the username only and then clicking the submit button shows a correct error message, or only the password, etc.
I usually look at the page and try to summarize what "actions" the user can do on that page, every action becomes a method. The different actions might be on different "levels". e.g. on a blog website, the user can enter the blog-title and the blog content, that are two actions the user can do, but looking from an other abstraction layer the user wants to "create" a post. So that function might again call other functions.
Basically its like any other programming, you have multiple abstraction layers, that is why you have Page Objects in the first place.
And just use iterative development, create a function that does what you want to test and if you find yourself reusing the same code (or identifier) in other functions, separate those out in a new function
Finely chopping your code into more methods allows for greater atomization. Nothing stops you from grouping the typeUsername(), typePassword() and submitLogin() into login() method.
The upside of doing so is knowing more precisely that your login failed at, say, typing password as opposed to "somewhere on the login page".
Login page, there would be 3 valid scenarios -
1) valid Login
2) Invalid Login
3) Negative Testing scenario
system is expected to prompt the user with an error message.
But a negative testing scenario would be where you are trying to break the application.
For Instance,
1) leaving Password blank,
2) trying to navigate using the URL,
3) using the forward button of the IE bypassing the Login page etc.....
Using the getting started with selenium [Java] framework, your test would look something like this.
#Config(url="http://systemunder.test", browser=Browser.FIREFOX)
public class TestLogin extends AutomationTest {
#Test
public void testLoginWorks() {
setText(By.id("username"), "valid_username")
.setText(By.id("password"), "valid_password")
.click(By.id("btnLogin"))
.validatePresent(By.id("logout_link"));
}
#Test
public void testPasswordBlank() {
setText(By.id("username"), "invalid_username")
.setText(By.id("password"), "")
.validateText(By.id("error_message"), "Password is blank.");
}
#Test
public void testUrl() {
navigateTo("/profile")
.validateUrl("/login"); // make sure they get redirected to login page.
}
}