Specflow - Sharing Steps Between Features Issue - c#

I am breaking down the testing on this so that I have feature files for areas like Login, ResetPassword, ForgotPassword etc. Let's say I have the below example. I have an automation step creating a brand new user in CreateAccount.feature. That step is used multiple times within that Feature/Step Class without issue. But now I want the user to change their password so I create a new Feature File MyAccount.feature. When I copy the Given Statement in, it is found immediately. Then I add the code to click the reset password and continue on with the rest of the steps.
When I run the ResetPassword test, the automation creates the new user but when it get's to step 2, "When I Click Reset Password" it fails because it can't find the element. Since bindings are global, this strikes me odd. So what I did was take step "Given I have created my account" and renamed it and added to the other feature file/steps class and ran it again. It worked fine.
I am not sure why I can't share between steps. Any ideas?
Some updates showing more code...
CreateAccount.feature
scenario: Feature Create Account
Given I have created my account
-----------
CreateAccountsteps.cs
namespace Project
{
[Binding]
public class CreateAccount: BaseTestObject
{
[Given]
public void Given_I_have_created_my_account()
{
ConfigProperties.Environment = "Test";
TestDriver.goToUrl(ConfigProperties.StartUrl);
TestDriver.goToUrl(ConfigProperties.StartUrl + "Create/Account");
[followed by input for creating a user acct]
-------------------------------------------------
MyAccount.feature
scenario: Feature Change Password
Given I have created my account
When I Click Reset Password
...........
MyAccountSteps.cs
namespace Project
{
[Binding]
public class MyAccountSteps: BaseTestObject
{
[When]
public void When_I_click_Reset_Password()
{
On.MyHeaderPage.BtnResetPassword.Click();
}
[followed by rest of steps to change password]
BaseTestObject.cs
namespace Project
{
public class BaseTestObject
{
private IWebDriver seleniumDriver;
private IDriver testDriver;
[TestInitialize]
public virtual void Setup()
{
TestDriver.goToUrl(ConfigProperties.StartUrl);
}
[AfterScenario]
public void CleanUp()
{
if (seleniumDriver != null)
{
SeleniumDriver.Dispose();
seleniumDriver = null;
}
}
public IWebDriver SeleniumDriver
{
get
{
if (seleniumDriver == null)
{
seleniumDriver = GetDriver();
}
return seleniumDriver;
}
}
public IDriver TestDriver
{
get
{
if (testDriver == null)
{
testDriver = new UiDriver(SeleniumDriver);
}
return testDriver;
}
}
public CurrentPageObjectScope On
{
get
{
return new CurrentPageObjectScope(TestDriver);
}
}
public static String GetTimestamp()
{
return DateTime.Now.ToString("yyyyMMddhhmmssfff");
}
public static String GetTimestamp2()
{
return DateTime.Now.ToString("M/d/yyyy");
}
private IWebDriver GetDriver()
{
switch (ConfigProperties.Browser.ToLower())
{
case "firefox":
return new FirefoxDriver();
case "chrome":
ChromeOptions options = new ChromeOptions();
ChromeDriverService service = ChromeDriverService.CreateDefaultService(#"../Chrome/");
service.SuppressInitialDiagnosticInformation = true;
service.HideCommandPromptWindow = true;
options.AddArguments("test-type");
options.AddArgument("--start-maximized");
return new ChromeDriver(service, options);
case "ie":
case "internetexplorer":
return new InternetExplorerDriver(#"../IE/");
default:
throw new NotImplementedException("Unknown browser string in Config properties " + ConfigProperties.Browser);
}
}

Based on your updates it looks like you have named your when step incorrectly. You feature says:
scenario: Feature Change Password
Given I have created my account
When I Click Reset Password
but you step has the name When_I_click_My_Account
This seems wrong to me.
Really though we need more details (like the actual exception message) and perhaps some indication of what BaseTestObject looks like.

Resolved - In the BaseTestObject I changed the methods to static.

Related

Not sure how to handle exceptions using try catch if element was not found

I am trying to handle the error message on login page by clearing the Username field from the entered input.
Here is my code:
using OpenQA.Selenium;
using System;
using System.Linq.Expressions;
using TestProject.Common.Extensions;
using TestProject.SDK;
using TestProject.SDK.Tests;
using TestProject.SDK.Tests.Helpers;
namespace SecondTest
{
public class LoginTest : IWebTest
{
public object Assert { get; private set; }
public object TimeUnit { get; private set; }
public object InvalidInputErrorTxt { get; private set; }
public bool IWebelement { get; private set; }
public bool ErrorMsg { get; private set; }
public ExecutionResult Execute(WebTestHelper helper)
{
var driver = helper.Driver;
var URL = "https://www.office1.bg/login";
driver.Navigate().GoToUrl(URL);
driver.Manage().Window.Maximize();
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
driver.FindElementById("CybotCookiebotDialogBodyLevelButtonAccept").Click();
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
//Login
Pages.LoginPage LoginPage = new Pages.LoginPage(driver);
LoginPage.PerformLogin("atanas.grudev1#gmail.com", "123456789");
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
try
{
IWebElement Level = driver.FindElementByClassName("corporal-page-list-item");
if (Level.Displayed)
{
Level.Click();
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
driver.FindElementByClassName("corporate-login-button-wrapper").Click();
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
return ExecutionResult.Passed;
}
else
{
IWebElement ErrorMsg = driver.FindElementByXPath("//*[#id='loginForm']/div/div[1]");
if (ErrorMsg.Displayed)
{
LoginPage.TxtUserName.Clear();
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
return ExecutionResult.Failed;
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return ExecutionResult.Passed;
}
}
}
Seems ExecutionResult at the beginning requires return ExecutionResult.Passed/Failed; at the end of the code, but since I have try - catch I am not sure how to get the correct result. End goal is:
If valid credentials are entered code from the first if statement should be executed and to get Passed result.
If the entered credentials are not valid the website will throw an error and if the error is present on the the screen the input from the username field should be cleared and get the Failed Result.
Thank you in advance!
A few suggestions:
In general, you want to avoid use of try-catch to control code flow whenever possible. In this case, you can use driver.FindElements() (note the plural ElementS) and check for an empty list to avoid exceptions being thrown when no element is found. This is a best practice according to the docs.
findElement should not be used to look for non-present elements, use findElements(By) and assert zero length response instead.
Setting the timeout for ImplicitWait only needs to be done once. Once set, that timeout is applied automatically every time an element is searched for. It doesn't actually wait when called... I think maybe you are confusing it with WebDriverWait?
Using ImplicitWait is not recommended by the Selenium contributors. You should instead use WebDriverWait each time you need to wait.
I would suggest you investigate and use the Page Object Model. If done right, it will save you a lot of time with code reuse, make your project much more organized, and make your test code a lot cleaner. It looks like you are trying to use something like Page Objects with your LoginPage.PerformLogin() call but all your driver.FindElement() calls should be in the appropriate page object and you should have one page object per "page".
Here's the code after implementing the first two suggestions. I'll let you do #3 and #4, if you choose to. I removed the try-catch, added new ifs, and removed all but the first ImplicitWait set.
using OpenQA.Selenium;
using System;
using System.Collections.Generic;
using System.Linq;
using TestProject.Common.Extensions;
using TestProject.SDK;
using TestProject.SDK.Tests;
using TestProject.SDK.Tests.Helpers;
namespace SecondTest
{
public class LoginTest : IWebTest
{
public object Assert { get; private set; }
public object TimeUnit { get; private set; }
public object InvalidInputErrorTxt { get; private set; }
public bool IWebelement { get; private set; }
public bool ErrorMsg { get; private set; }
public ExecutionResult Execute(WebTestHelper helper)
{
var driver = helper.Driver;
var URL = "https://www.office1.bg/login";
driver.Navigate().GoToUrl(URL);
driver.Manage().Window.Maximize();
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
driver.FindElementById("CybotCookiebotDialogBodyLevelButtonAccept").Click();
//Login
Pages.LoginPage LoginPage = new Pages.LoginPage(driver);
LoginPage.PerformLogin("atanas.grudev1#gmail.com", "123456789");
IReadOnlyCollection<IWebElement> level = driver.FindElementsByClassName("corporal-page-list-item");
if (level.Any() && level.ElementAt(0).Displayed)
{
level.ElementAt(0).Click();
driver.FindElementByClassName("corporate-login-button-wrapper").Click();
return ExecutionResult.Passed;
}
IReadOnlyCollection<IWebElement> errorMsg = driver.FindElementsByXPath("//*[#id='loginForm']/div/div[1]");
if (errorMsg.Any() && errorMsg.ElementAt(0).Displayed)
{
LoginPage.TxtUserName.Clear();
return ExecutionResult.Failed;
}
return ExecutionResult.Passed;
}
}
}
A few notes on the new code that you may not have seen before.
.Any() is an equivalent in LINQ to .Count > 0. It returns true if the list is not empty, and false otherwise.
.ElementAt() is a LINQ method that allows you to access a member of the collection by index. NOTE: Even though it has "Element" in the name, it has nothing to do with WebElement or Selenium.
Let me know if you have any questions.

Selenium WebDriver C# NUnit Tests Failing in Parallel

Please see the update at the bottom!
I am setting up my framework and currently have 4 Tests. Individually they all run like a charm. However when I try to run all 4 in parallel (I have set up the Parallelizable attribute up correctly and am calling tests from different classes not within the same method) I am consistently getting several errors that seem to jump around each test. These are the messages that I am getting each run:
Again These objects are found when the tests are run individually. I am not sure what code I need to show in order to help. Please advise.
UPDATE** #Chris my suspicions are the same. I think my tests are confusing the same driver when looking for objects. If that is the case can someone please advise how to better handle this, my browser class is what is calling the driver.
public static class Browser
{
private static IWebDriver driver;
private static string baseURL = "someURL";
public static ISearchContext Driver { get { return driver; } }
internal static bool WaitUntilElementIsDisplayed(By element, int timeout)
{
for (int i = 0; i < timeout; i++)
{
if (ElementIsDisplayed(element))
{
return true;
}
Thread.Sleep(1000);
}
return false;
}
internal static IWebElement FindElement(By by)
{
return driver.FindElement(by);
}
public static bool ElementIsDisplayed(By element)
{
var present = false;
driver.Manage().Timeouts().ImplicitlyWait(System.TimeSpan.FromSeconds(0));
try
{
present = driver.FindElement(element).Displayed;
}
catch (NoSuchElementException)
{ }
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));
return present;
}
public static void Initialize()
{
var options = new InternetExplorerOptions();
options.IntroduceInstabilityByIgnoringProtectedModeSettings = true;
options.EnsureCleanSession = true;
options.IgnoreZoomLevel = true;
driver =
new InternetExplorerDriver(
#"C:Myfilepath",
options, TimeSpan.FromMinutes(10));
Goto("");
}
public static void CleanUp()
{
driver.Close();
driver.Quit();
}
public static void Goto(string URL, bool userBaseURL = true)
{
if (userBaseURL)
driver.Navigate().GoToUrl(string.Format("{0}/{1}", baseURL, URL));
else
driver.Navigate().GoToUrl(URL);
}
}
Newest Update: per the recommendation I have removed the static references but could someone help me with the syntax on creating an instance of the driver within my current code
public class Pages
{
private T GetPage<T>() where T : new()
{
var page = new T();
PageFactory.InitElements(Browser.Driver, page);
return page;
}
public LoginPage Login
{
get { return GetPage<LoginPage>(); }
}
public RegisterPage Register
{ get { return GetPage<RegisterPage>(); } }
public SearchPage Search
{ get { return GetPage<SearchPage>(); } }
}
I am not sure how to create an instance of Browser.Driver Please help!
Remove all references to "static" in your class and create an instance of the class in each test to fix your issue.
...Now change your Page class to accept the driver in the constructor
public class Pages
{
private readonly ISearchContext _context;
public Pages(ISearchContext context)
{
_context = context;
}
private T GetPage<T>() where T : new()
{
var page = new T();
PageFactory.InitElements(_context, page);
return page;
}
public LoginPage Login
{
get { return GetPage<LoginPage>(); }
}
public RegisterPage Register
{ get { return GetPage<RegisterPage>(); } }
public SearchPage Search
{ get { return GetPage<SearchPage>(); } }
}
... then in your test method
var browser = new Browser();
var page = new Page(browser.Driver);
Sorry. Been away and noticed your updates.
I have a separate class helper that I use to return my web driver. I’m using chrome driver and (headless) unit driver, which on my machines polices requires several params to get it running, so a class in its own right makes senses to me. E.g. WebDriverHelper.java. This has several static methods that returns a new instance of the driver of interest.
E.g.
WebDriver myDriver = WebDriverHelper.ChromeDriver();
My ChromeDriver method returns a new driver.
E.g.
return new ChromeDriver;
If you need more detail, let me know and I’ll copy some of my classes when I get in work tomorrow.

Selenium C# parallel testing using single driver\browser

I have a project which uses the page object model and I've edited it to try and use parallel testing with Nunit. However when I run one single test it will launch a second unwanted browser. I think this is where I'm initiating my page at the beginning of the test.
The files I have are a Base class for the driver:
namespace ParallelTests
{
public class Base
{
public static IWebDriver Driver { get; set; }
}
}
A hooks file to setup the driver:
namespace ParallelTests
{
public class Hooks : Base
{
public Hooks()
{
Driver = new ChromeDriver(#"D:\Data\user\Documents\Visual Studio 2012\Projects\ParallelTests\ParallelTests\bin");
}
}
}
The page file:
namespace ParallelTests
{
class PageObject_LoggedIn : Hooks
{
public PageObject_LoggedIn()
{
PageFactory.InitElements(Driver, this);
}
[FindsBy(How = How.Id, Using = "lst-ib")]
public IWebElement SearchBox = null;
public void Search()
{
SearchBox.SendKeys("Deep Purple");
SearchBox.SendKeys(Keys.Enter);
}
}
}
And the test itself:
[TestFixture]
[Parallelizable]
public class ChromeTesting : Hooks
{
[Test]
public void ChromegGoogleTest()
{
PageObject_LoggedIn loggedIn = new PageObject_LoggedIn();
Driver.Navigate().GoToUrl("https://www.google.co.uk");
loggedIn.Search();
}
}
I think it's PageObject_LoggedIn loggedIn = new PageObject_LoggedIn(); in the test which is launching the second browser but I'm not sure how to rectify it.
This is an extension to an original issue, but is treated as a separate issue

I created a class in a console application, now I want to create a windows form applocation that uses that class

I have created a button called "authenticate", and two text boxes so the program can get some user input. When the authenticate button is clicked, I want the program to either return "Authenticated" or "NOT Authenticated" depending on whether or not username and password matches. Anybody know a way of doing this? New to C#, thank you. I have added the class below for reference.
class Authenticator
{
static void Main(string[] args)
{
Authenticator au = new Authenticator();
au.InitialValues();
if (au.Authenticate())
Console.WriteLine("Authenticated");
else
Console.WriteLine("NOT Authenticated");
Console.WriteLine("Press Enter to end");
Console.ReadLine();
}
private Dictionary<string, string> dictionary = new Dictionary<string, string>();
public void InitialValues()
{
dictionary.Add("username1", "password1");
dictionary.Add("username2", "password2");
dictionary.Add("username3", "password3");
dictionary.Add("username4", "password4");
dictionary.Add("username5", "password5");
}
public bool Authenticate()
{
bool authenticated = false;
Console.WriteLine("Please enter a username");
string inputUsername = Console.ReadLine();
Console.WriteLine("Please enter your password");
string inputPassword = Console.ReadLine();
if (dictionary.ContainsKey(inputUsername) && dictionary[inputUsername] == inputPassword)
{
authenticated = true;
}
else
{
authenticated = false;
}
return authenticated;
}
}
You can just reference the Console project in the windows forms project (exe-File). This is not a good practice. It would be better to put your reused code in a Library project which will be referenced by both the Console and the Forms project.
Oh and note that you code will not work in a Forms project without console.
Add the Class Library project howto:
In your solution create a new project (right click Add->New Project) and choose Class Library. After this you right click on your console or forms project and choose Add->Reference. In the dialog go to Project Reference and check the newly created Class Library. Now copy your class except the Main function into the Class Library project and compile. Just pay attention that you rename your projects.
To make the authentication work you should use parameters instead of Console.ReadLine.
In class library
public class Authentication
{
private Dictionary<string, string> dictionary = new Dictionary<string, string>();
public Authentication()
{
dictionary.Add("username1", "password1");
dictionary.Add("username2", "password2");
dictionary.Add("username3", "password3");
dictionary.Add("username4", "password4");
dictionary.Add("username5", "password5");
}
public bool Authenticate(string user, string password)
{
// note i just replaced the variable with return
return dictionary.ContainsKey(user) && dictionary[user] == password;
}
}
In your console application
Main(...)
{
// ...
Console.WriteLine("Please enter a username");
var user = Console.ReadLine();
Console.WriteLine("Please enter your password");
var password = Console.ReadLine();
var auth = new Authentication();
if(auth.Authenticate(user, password))
{ // do what you need to do ;) }
}
Note that your authentication mechanism is not secure. So don't use it where it is really important to have security.
Well, you could make a solution with three diferent projects:
Logic (Class Library Project): it will have a class with your validation logic methods;
Console Application: it will have a reference to Logic, and make use of its methods to validate and display results in a console;
WinForms Application: the same as Console, but showing the results in a form
I suggest:
Logic
public class MyLogic()
{
public void InitializeData()
{
...
}
public boolean Authenticate(string userName, string password)
{
if(....)
{
return true;
}
else
{
return false;
}
}
}
Console Application
public static void main(string[] args)
{
Console.WriteLine('Enter your username: ');
string username = Console.ReadLine();
Console.WriteLine('Enter your password: ');
string password= Console.ReadLine();
var logic = new MyLogic();
logic.InitializeData();
if(logic.Authenticate(username, password))
{
Console.WriteLine("Success")
}
else
{
Console.WriteLine("Fail");
}
}
WinForms Application
Here, you can make the same as in Console, but showing the results in a Popup, in a label or wherever you want. I think you can catch the idea.

Moq: Unit testing IAuthorisationLoader(GetUsers() method)

I have become slightly stumped. I'm Unit Testing my ServicesController where I call different methods such as:
GetRoles()
GetDomains()
GetUsers()
In my Unit Test I have checked that the call is being made and is being run properly and that is all fine. However I want to make an Assert/call where I can check that this method is returning X amount of users from my mocked _userList where they are enabled.
The following code is my GetUsers() method:
[CustomAuthorize]
public ActionResult GetUsers(bool enabledOnly = true)
{
return new JsonNetResult { Data = _authorizationLoader.GetUsers(enabledOnly) };
}
I would like to know how to get this to return users from my _userList that are enabled and to be able to Assert the correct amount are called at the end of the test.
This part of my code is the unit test for the method:
private User _user1;
private User _user2;
private User _user3;
private User _user4;
private User _user5;
private bool isEnabled;
private List<User> _userList;
private Mock<IAuthorizationLoader> iAuthLoaderMock;
public override void SetUp()
{
_user1 = new User
{
stuff needed is here
};
_user2 = new User
{
stuff needed is here
};
_user3 = new User
{
stuff needed is here
};
_user4 = new User
{
stuff needed is here
};
_user5 = new User
{
stuff needed is here
};
_userList = new List<User>();
_userList.Add(_user1);
_userList.Add(_user2);
_userList.Add(_user3);
_userList.Add(_user4);
_userList.Add(_user5);
isEnabled = true;
}
public override void Initialize()
{
iAuthLoaderMock = MockContainer.GetMock<IAuthorizationLoader>();
iAuthLoaderMock.Setup(i => i.GetUsers(isEnabled)).Verifiable();
base.Initialize(); // dont worry about this call it is needed though
}
public override void Because()
{
TestSubject.GetUsers(true);
// this subject is created in code above but is unecessary to include
}
[TestMethod]
public void The_controller_returns_only_enabled_users()
{
iAuthLoaderMock.Verify(i => i.GetUsers(isEnabled));
//---------------- Need to find a way of returning and checking this -------------------------//
//Assert.AreEqual(X (number of enabled users), new _userList.result(my result from test));
}
any help would be great as this has me stumped. Thanks.

Categories

Resources