Run Selenium tests in multiple browsers with C# - c#

I have a method that creates 2 remote web drivers. one with chrome and another with firefox:
Driver.cs
public class Driver
{
public static IWebDriver Instance { get; set; }
public static void Initialize()
{
DesiredCapabilities[] browsers = {DesiredCapabilities.Firefox(),DesiredCapabilities.Chrome()};
foreach (DesiredCapabilities browser in browsers)
{
if (browser == DesiredCapabilities.Chrome())
{
var browser = DesiredCapabilities.Chrome();
System.Environment.SetEnvironmentVariable("webdriver.chrome.driver", "C:/Users/jm/Documents/Visual Studio 2013/Projects/VNextAutomation - Copy - Copy (3)/packages/WebDriverChromeDriver.2.10/tools/chromedriver.exe");
ChromeOptions options = new ChromeOptions() { BinaryLocation = "C:/Users/jm/AppData/Local/Google/Chrome/Application/chrome.exe" };
browser.SetCapability(ChromeOptions.Capability, options);
Console.Write("Testing in Browser: " + browser.BrowserName);
Instance = new RemoteWebDriver(new Uri("http://127.0.0.1:4444/wd/hub"), browser);
} else {
Console.Write("Testing in Browser: "+ browser.BrowserName);
Instance = new RemoteWebDriver(new Uri("http://127.0.0.1:4444/wd/hub"), browser);
}
}
Instance.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(15));
}
Then I have a Test class:
[TestClass]
public class LoginTests
{
[TestInitialize]
public void Init()
{
Driver.Initialize();
}
[TestMethod]
public void Failed_login()
{
LoginPage.GoTo();
LoginPage.LoginAs("user").WithPassword("pass").WithDatasource("db").Login();
Assert.IsTrue(LoginFail.IsAt, "Login failure is incorrect");
}
[TestMethod]
public void Admin_User_Can_Login()
{
LoginPage.GoTo();
LoginPage.LoginAs("user").WithPassword("pass").WithDatasource("db").Login();
Assert.IsTrue(HomePage.IsAt, "Failed to login.");
}
[TestCleanup]
public void Cleanup()
{
Driver.Close();
}
}
}
The problem is when Driver.Intialize gets called it doesn't run both chrome and firefox. What I want to happen is that when the Init method gets called it starts both browsers and runs the Test Methods in each browser.

The way I am currently doing this is with NUnit.
I had the same problem and could not find a good way to do it with MSTest.
What I am doing would be:
As you can see I just create a new TestFixture for each browser.
[TestFixture(typeof(ChromeDriver))]
[TestFixture(typeof(InternetExplorerDriver))]
[TestFixture(typeof(FirefoxDriver))]
public class LoginTests<TWebDriver> where TWebDriver : IWebDriver, new()
{
[SetUp]
public void Init()
{
Driver.Initialize<TWebDriver>();
}
[Test]
public void Failed_login()
{
LoginPage.GoTo();
LoginPage.LoginAs("user").WithPassword("pass").WithDatasource("db").Login();
Assert.IsTrue(LoginFail.IsAt, "Login failure is incorrect");
}
[Test]
public void Admin_User_Can_Login()
{
LoginPage.GoTo();
LoginPage.LoginAs("user").WithPassword("pass").WithDatasource("db").Login();
Assert.IsTrue(HomePage.IsAt, "Failed to login.");
}
[TearDown]
public void Cleanup()
{
Driver.Close();
}
}
}
Driver Class
public class Driver<TWebDriver> where TWebDriver : IWebDriver, new()
{
public static IWebDriver Instance { get; set; }
public static void Initialize()
{
if (typeof(TWebDriver) == typeof(ChromeDriver))
{
var browser = DesiredCapabilities.Chrome();
System.Environment.SetEnvironmentVariable("webdriver.chrome.driver", "C:/Users/jm/Documents/Visual Studio 2013/Projects/VNextAutomation - Copy - Copy (3)/packages/WebDriverChromeDriver.2.10/tools/chromedriver.exe");
ChromeOptions options = new ChromeOptions() { BinaryLocation = "C:/Users/jm/AppData/Local/Google/Chrome/Application/chrome.exe" };
browser.SetCapability(ChromeOptions.Capability, options);
Console.Write("Testing in Browser: " + browser.BrowserName);
Instance = new RemoteWebDriver(new Uri("http://127.0.0.1:4444/wd/hub"), browser);
} else {
Console.Write("Testing in Browser: "+ browser.BrowserName);
Instance = new RemoteWebDriver(new Uri("http://127.0.0.1:4444/wd/hub"), browser);
}
}
Instance.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(15));
}
}
I have tried to fit it around your code.

If you wanted to be able to specify a browser to run a test on an adhoc basis rather than all of them every time using TestFixtures, Richard Bradshaw has an excellent tutorial here.
The idea is to use an app config file (and Factory Pattern) that houses values such as the browser, version, platform, selenium hub and port information (as well as any other data you might require in your Hub/Node implementation on Grid) and pull it in at the time of testing to create a WebDriver instance. You can then modify this file in between tests to spin up a WebDriver of a different type if necessary.
We use this to run tests sequentially with NUnit and it has proven quite effective.

an easier and a little bit more straight forward solution using NUnit:
namespace Test
{
//add all browser you want to test here
[TestFixture(typeof(FirefoxDriver))]
[TestFixture(typeof(ChromeDriver))]
public class SkillTest<TWebDriver> where TWebDriver : IWebDriver, new()
{
private IWebDriver _driver;
private string driverPath;
[SetUp]
public void Init()
{
_driver = new TWebDriver();
_driver.Navigate().GoToUrl("https://localhost:5001/");
}
[Test]
public void your_test_case()
{
//some test logic
} 
[TearDown]
public void CleanUp()
{
_driver.Quit();
_driver.Dispose();
}
}
}

Related

How can I run some test cases with chrome and come with Firefox using C# mstest?

There are total 10 test cases. I need to run some testcases with chrome lets say 1-3 and 6-8 test cases with chrome and 4-5 and 9-10 test cases with firefox.
The code in helper file is
static public IWebDriver GetWebDriverC(browserType brt)
{
ChromeOptions cOption = new ChromeOptions();
cOption.AddArgument("no-sandbox");
driver = new ChromeDriver(cOption);
driver.Navigate().GoToUrl(url);
return driver;
}
static public IWebDriver GetWebDriverFF(browserType brt, string url, string username, string password)
{
FirefoxDriverService service = FirefoxDriverService.CreateDefaultService();
service.FirefoxBinaryPath = #"path";
driver = new FirefoxDriver(service);
driver.Navigate().GoToUrl(url);
bool ele1 = AutoItX.WinExists("[CLASS:MozillaDialogClass]") == 1;
if (ele1)
{
AutoItX.WinActivate("[CLASS:MozillaDialogClass]");
AutoItX.Send(username);
AutoItX.Send("{TAB}");
AutoItX.Send(password);
AutoItX.Send("{ENTER}");
}
return driver;
}
Username and password is different for different users.
There are multiple testcases in main file
[TestInitialize]
public void initilize()
{
Webd= Helper.GetWebDriverC(Chrome);
}
[Priority(0)]
[TestMethod]
public void 1()
{
}
[Priority(2)]
[TestMethod]
public void 2()
{
}
.
.
.
[Priority(10)]
[TestMethod]
public void 10()
{
}
There is only one driver in Test initialize that is chrome and chrome gets open before each testcase run. I want that when testcases run on the basis of some condition as mentioned earlier testcases run on the desired Browsers.
How can I achieve that using C# mstest?
The problem here is that you're setting the WebDriver in the TestInitialize step, so any tests within the same class will be using the same driver, in this case Chrome.
One approach would be to have separate test classes for FireFox and Chrome.
Chrome Tests:
public class ChromeTests
{
[TestInitialize]
public void initilize()
{
Webd = Helper.GetWebDriverC(Chrome);
}
[TestMethod]
public void 1()
{
}
}
FireFox tests
public class FireFoxTests
{
[TestInitialize]
public void initilize()
{
Webd = Helper.GetWebDriverFF(Chrome);
}
[TestMethod]
public void 1()
{
}
}
Alternative solutions would include:
Having two WebDrivers in your test class, one for Chrome and one for FireFox, and then you can decide in you tests which driver you wish to use.
Creating a new WebDriver instance in each test

when using chromedriver, with specflow, a new browser instance open per step

I'm having an issue with Specflow. For some reason, a new instance of chrome is started per step.
can you help with this?
For follow test, 3 steps, 3 chrome instance are started.
Feature: Home
Background:
Given The QA Department site
Scenario: QA Department title
When QA site open
Then QA site tile must be "UI Testing Site"
The first instance has the content i want but the other two, that i don´t know why are started, are empty.
namespace QAtests.SpecFlow_Steps.Home
{
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow;
[Binding]
class HomeTestSteps
{
private readonly MainPage mainPage = new MainPage();
private readonly PageHelper pageHelper = new PageHelper();
public HomeTestSteps()
{
}
[When(#"QA Department site open")]
public void GivenQADepartmentSiteOpen()
{
var url = this.mainPage.ValiteSiteAddress();
Assert.AreEqual(url, this.mainPage.siteURL);
}
[Then(#"QA Department site tile must be ""(.*)""")]
public void ThenQADepartmentSiteTileMustBe(string pageTile)
{
var title = this.pageHelper.GetElementTitle();
Assert.AreEqual(pageTile, title);
}
}
}
The main page class just sets the url and checks elements
namespace QAtests.Pages.Main
{
public class MainPage
{
private ChromeDriver driver = new ChromeDriver();
private readonly string elementTitle = "//title";
public readonly string siteURL = "http://uitest.duodecadits.com/";
public MainPage()
{
}
public void StartSite()
{
try
{
this.driver.Navigate().GoToUrl(siteURL);
}
catch (Exception e)
{
throw new Exception("Can´t set address.", e);
}
}
public void Close()
{
this.driver.Close();
}
public string ValiteSiteAddress()
{
return this.driver.Url;
}
public IWebElement ElementTitleisVisible()
{
try
{
return this.driver.FindElement(By.XPath(elementTitle));
}
catch(Exception e){
throw new Exception("Element tile not found.", e);
}
}
}
}
I think that is related with the declaration of
private ChromeDriver driver = new ChromeDriver();
You are instantiating a new ChromeDriver instance each time you instantiate a page.
As suggested, you will want to setup your webdriver in a BeforeScenario step.
Also, you could make use of the built in IOC container and register your web driver instance. Then that instance can be injected into any of your page objects, as and when they are needed.
Something like...
[Binding]
public class BaseScenarioHooks
{
protected IObjectContainer Container;
public BaseScenarioHooks(IObjectContainer container)
{
Container = container;
}
[BeforeScenario(Order = 3)]
public void SetupDrivers()
{
var webDriver = new WebDriver(browser, seleniumHub, url, Context.ScenarioInfo.Title,
Context["UniqueTestName"].ToString(), FrameworkConfiguration.VideoRecording);
Container.RegisterInstanceAs<WebDriver>(webDriver);
}
}
Have a look at https://github.com/techtalk/SpecFlow/wiki/Context-Injection.
Also have a look here for ideas on how to organize your driver class
Not seeing your thread get a lot of love here. In all honesty, I would look for a SpecFlow framework online and then start building yours out from there. SpecFlow uses hooks so you are probably going to want to invoke your browser with a BeforeScenario so that it is called once for each test. Put your cleanup in an AfterScenario hook.
If you use the nuget chromedriver and have it set always output to your bin debug folder, you can use the GetBasePath call so it always calls from that directory. You can remove the call and hard code the chromedrive.exe path though. This just makes it easier if it is going into source control.
Hopefully this will help you get started.
ex.
MyFile.cs
[BeforeScenario]
public static void Startup()
{
Browser.StartWebDriver();
}
Browser.cs
public class Browser
{
static IWebDriver _Driver;
static WebDriverWait _wait;
public static void StartWebDriver()
{
ChromeDriverService service = ChromeDriverService.CreateDefaultService(Path.Combine(GetBasePath, #"bin\Debug\"));
ChromeOptions options = new ChromeOptions();
options.AddArguments("--disable-extensions");
options.AddAdditionalCapability("useAutomationExtension", false);
options.AddExcludedArgument("enable-automation");
_Driver = new ChromeDriver(service, options);
_wait = new WebDriverWait(_Driver, TimeSpan.FromSeconds(10));
}
public static string GetBasePath
{
get
{
var basePath =
System.IO.Path.GetDirectoryName((System.Reflection.Assembly.GetExecutingAssembly().Location));
basePath = basePath.Substring(0, basePath.Length - 10);
return basePath;
}
}
}
Yes. This line
private ChromeDriver driver = new ChromeDriver();
is definitely your problem. Every time you access this property you are creating a new driver.
Here's a web hook class i use in my tests.
[Binding]
internal sealed class WebHooks
{
private readonly IObjectContainer _objectContainer;
public WebHooks(IObjectContainer objectContainer)
{
_objectContainer = objectContainer;
}
[BeforeScenario("web")]
public void BeforeWebScenario()
{
var options = new ChromeOptions();
options.AddArgument("--start-maximized");
options.AddAdditionalCapability("useAutomationExtension", false);
options.AddArgument("no-sandbox");
//HACK: this fixes issue with not being able to find chromedriver.exe
//https://stackoverflow.com/questions/47910244/selenium-cant-find-chromedriver-exe
var webDriver = new ChromeDriver(".", options);
webDriver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
_objectContainer.RegisterInstanceAs<IWebDriver>(webDriver);
}
[AfterScenario("web")]
public void AfterWebScenario()
{
var webDriver = _objectContainer.Resolve<IWebDriver>();
if (webDriver == null) return;
webDriver.Close();
webDriver.Dispose();
}
}
So this registers the webdriver with the internal SpecFlow DI container so that it can now be called in you step definitions.
Then modify your step definition class to access the webdriver as a constructor parameter
[Binding]
public class YourSteps
{
private readonly IWebDriver _webDriver;
public CanvasSteps(IWebDriver webDriver)
{
_webDriver = webDriver;
}
}
Specflow's internal DI automagically wires everything up and you should now have 1 webdriver instance per scenario. It also creates a better design because your cross-cutting concern of a webdriver instance has been extracted from your steps and placed in a Hook where is belongs. Just tag your scenario with the #web hook

PhantomJS & Selenium - Test calling a method from another file (NullReferenceException)

I have got the following method in one file
public class Login
{
static IWebDriver driver;
public static void LogIn(string Env, string User)
{
driver.Navigate().GoToUrl(Env);
driver.Manage().Timeouts().SetPageLoadTimeout(TimeSpan.FromSeconds(3000));
driver.FindElement(By.XPath("//*[#ng-model='email']")).SendKeys(User);
driver.FindElement(By.XPath("//*[#ng-model='password']")).SendKeys("1234");
driver.FindElement(By.XPath("//*[#id=\"ui-view\"]/div/div/div[1]/form/div[2]/button")).Click();
System.Threading.Thread.Sleep(2000);
NUnit.Framework.Assert.IsTrue(driver.FindElement(By.Name("some element")).Displayed,
"Login failed, home page did not display");
}
}
}
And the test I want to call the above method from, in another:
[TestFixture]
public class SmokeTest
{
private IWebDriver driver;
[SetUp]
public void SetUp()
{
driver = new PhantomJSDriver();
}
[Test]
public void Test1()
{
**Login.LogIn("myEnv", "user");**
}
}
I can compile with no issues but once I run Test1 I get the following message:
Automation.SmokeTest.SearchForProduct:
System.NullReferenceException : Object reference not set to an instance of an object.
Nunit points to the Login.LogIn("myEnv", "user"); as the source or error.
Any ideas ?
Ta.
Short answer
static IWebDriver driver; << NULL here
Long answer
You should pass driver through your Login, at your [Test]. It should be something like
[Test]
public void Test1()
{
var loginPage = new Login(driver);
loginpage.LogIn("myEnv", "user");**
}
For your Login, it could be change like following
public class Login
{
/// <summary>
/// Get or set driver
/// </summary>
public IWebDriver Driver { get; set; }
public Login(IWebDriver driver)
{
this.Driver = driver;
}
public static void LogIn(string Env, string User)
{
Driver.Navigate().GoToUrl(Env);
Driver.Manage().Timeouts().SetPageLoadTimeout(TimeSpan.FromSeconds(3000));
Driver.FindElement(By.XPath("//*[#ng-model='email']")).SendKeys(User);
Driver.FindElement(By.XPath("//*[#ng-model='password']")).SendKeys("1234");
Driver.FindElement(By.XPath("//*[#id=\"ui-view\"]/div/div/div[1]/form/div[2]/button")).Click();
System.Threading.Thread.Sleep(2000);
NUnit.Framework.Assert.IsTrue(Driver.FindElement(By.Name("some element")).Displayed,
"Login failed, home page did not display");
}
}
One way would be to change the login method to looks like this:
public static void LogIn(string Env, string User, IWebDriver driver)
by adding passing driver as a var. This way the test stays as single liner
Login.LogIn("myEnv", "user");

Test method from base class not executed after a test method from derived class is executed

I have a test class Navigation and I have derived class LeadExTest that is derived from Navigation class. Both class are of [TestClass] type
Now i have a ordered test file, that has a few tests i need to be executed in the order i have shown below.
AdminLogin
do some action
quit browser
AdminLogin, Navigate to page and quit browser test methods belong to Navigation class and do some action belongs to leadextest class
When i execute this ordered test file, admin login and do some action test cases execute just fine, but quit browser method is not getting hit.
//Base class
public class Navigation
{
protected static IWebDriver driver;
protected WebDriverWait wait;
protected StringBuilder verificationErrors;
private string baseURL;
//private bool acceptNextAlert = true;
protected static string advertiserId = "6570";
protected static Actions builder;
[TestInitialize]
public void SetupTest()
{
if (driver == null)
{
driver = new FirefoxDriver();
driver.Manage().Window.Maximize();
baseURL = ConfigurationManager.AppSettings["base_url"].ToString();
verificationErrors = new StringBuilder();
//string url = #"https://stage.madisonlogic.com/login.aspx";
//driver.Navigate().GoToUrl(url.ToString());
driver.Navigate().GoToUrl(ConfigurationManager.AppSettings["test_url"].ToString());
builder = new Actions(driver);
}
wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
}
[TestMethod]
public void adminLogin()
{
CommonFunctions.Login(driver, "rlodha", "a");
if (IsElementPresent(By.ClassName("TopTitle")))
Assert.AreEqual("Admin Center | Dashboard", driver.FindElement(By.ClassName("TopTitle")).Text.Trim().ToString());
else
Assert.Fail("Timed Out");
}
[TestMethod]
public void browserQuit()
{
CommonFunctions.BrowserQuit(driver);
Assert.IsNull(driver);
}
//derived class
[TestMethod]
public void Nav_Lead_Delivery()
{
builder.MoveToElement(driver.FindElement(By.Id("Content_AdvertiserMenu1_LeadsBtn"))).Perform();
driver.FindElement(By.Id("Content_AdvertiserMenu1_LeadExportBtn")).Click();
Console.Write("Hi");
}
Well you are just declaring it as another test method, so of course this isn't going to work.
You need to give MSTest much more of a clue about when to call your method, so replace it with a ClassCleanup to tell MSTest to call this method when all the tests are done.

Second testMethod failing Selenium C#

Here's my code :
[TestInitialize]
public void init()
{
_browser = new DefaultSelenium("localhost", 4444, #"*iehta", "http://localhost:4444");
}
[TestMethod]
public void TestLogin()
{
bool hasText;
_browser.Start();
_browser.Open("http://localhost/testSite.asp");
_browser.Type("id=NomUtilisateur", "admin");
_browser.Type("id=UserPassword", "password");
_browser.Click("name=Submit");
_browser.WaitForPageToLoad("30000");
hasText = _browser.IsTextPresent("test");
Assert.IsTrue(hasText, #"The search result does not contain text ""test"".");
}
[TestMethod]
public void TestRequisitionPhotocopie()
{
_browser.Start();
_browser.Open("http://localhost/testSite.asp");
_browser.Type("id=NomUtilisateur", "admin");
_browser.Type("id=UserPassword", "password");
_browser.Click("name=Submit");
_browser.WaitForPageToLoad("30000");
_browser.Click("link=lnkTest");
_browser.WaitForPageToLoad("30000");
}
[TestCleanup]
public void clean()
{
_browser.Stop();
//_browser.Close();
}
If I run the two test method, the secon test always fail with an error message like this :
Remote server don't exist or is unavaible
If I comment one of the test method, it's working, my two test method are working
Where's my mistake.
Thanks
EDIT :
The error is not happening everytime, but the error is in Selenium Remote Control If i debug the error is in selenium-browserbot.js at line 724 : windowToModify.seleniumAlert = windowToModify.alert;
Selenium log console have nothing in it
Have you tried it like this? I always start selenium in the setup of the test. (i'm also using Nunit
[SetUp]
public void init()
{
_browser = new DefaultSelenium("localhost", 4444, #"*iehta", "http://localhost:4444");
_browser.Start();
_browser.Open("http://localhost/testSite.asp");
}
[TestMethod]
public void TestLogin()
{
bool hasText;
_browser.Type("id=NomUtilisateur", "admin");
_browser.Type("id=UserPassword", "password");
_browser.Click("name=Submit");
_browser.WaitForPageToLoad("30000");
hasText = _browser.IsTextPresent("test");
Assert.IsTrue(hasText, #"The search result does not contain text ""test"".");
}
[TestMethod]
public void TestRequisitionPhotocopie()
{
_browser.Type("id=NomUtilisateur", "admin");
_browser.Type("id=UserPassword", "password");
_browser.Click("name=Submit");
_browser.WaitForPageToLoad("30000");
_browser.Click("link=lnkTest");
_browser.WaitForPageToLoad("30000");
}
[TearDown]
public void clean()
{
_browser.Stop();
//_browser.Close();
}

Categories

Resources