I've been attempting to learn selenium in c# and in the past day or so it's developed into using the testinitialize. Before I ran everything from my test method and it worked great the long term goal is for me to be able to load a page on the initialize then login in one test add posts on another test etc. I don't want to be loading up and logging in every time I'd like it to be free flowing from the point of logging in. At the moment I'm getting something in the wrong place as now it's just firing up a blank firefox page and doing nothing. I've kept it simple at the moment so I can get to grips with it. So the code below should load up wikipedia and check for some text in the heading.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
namespace SeleniumPractice
{
[TestClass]
public class Setup
{
IWebDriver driver;
[TestInitialize]
public void GoToWiki()
{
//Create an instance of the firefox driver.
IWebDriver driver = new FirefoxDriver();
}
[TestMethod]
public void VerifyHelloWorld()
{
driver.Navigate().GoToUrl("https://en.wikipedia.org/wiki/%22Hello,_World!%22_program");
driver.Manage().Window.Maximize();
string actualvalue = driver.FindElement(By.Id("firstHeading")).Text;
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));
Assert.AreEqual(actualvalue, "\"Hello, World!\" program");
}
[TestCleanup]
public void Teardown()
{
driver.Close();
}
}
}
Also I'm getting a green line under IwebDriver driver; in my class. With this error.
field 'seleniumPractice.Setup.driver' is never assigned to, and always have its default value null
I put this here because i noticed the test method didn't recognise the driver anymore when I moved it out of the test method.
The reason for the compiler warning and the blank Firefox window is that you are not actually assigning the driver field with a reference to a new FirefoxDriver object in GoToWiki(), you are actually declaring a new variable that is scoped only to that method and assigning a reference to a new FirefoxDriver object to that variable. The field is null when you call GoToUrl on it in VerifyHelloWorld(). Try this edit:
[TestInitialize]
public void GoToWiki()
{
//Create an instance of the firefox driver.
driver = new FirefoxDriver();
}
Related
I have a selenium-webdriver-di.cs file like this:
using TechTalk.SpecFlow;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium;
using BoDi;
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
public class WebDriverHooks
{
private readonly IObjectContainer container;
private static Dictionary<string, ChromeDriver> drivers = new Dictionary<string, ChromeDriver>();
private ScenarioContext _scenarioContext;
private FeatureContext _featureContext;
public WebDriverHooks(IObjectContainer container, ScenarioContext scenarioContext, FeatureContext featureContext)
{
this.container = container;
_scenarioContext = scenarioContext;
_featureContext = featureContext;
}
[BeforeFeature]
public static void CreateWebDriver(FeatureContext featureContext)
{
Console.WriteLine("BeforeFeature");
var chromeOptions = new ChromeOptions();
chromeOptions.AddArguments("--window-size=1920,1080");
drivers[featureContext.FeatureInfo.Title] = new ChromeDriver(chromeOptions);
}
[BeforeScenario]
public void InjectWebDriver(FeatureContext featureContext)
{
if (!featureContext.ContainsKey("driver"))
{
featureContext.Add("driver", drivers[featureContext.FeatureInfo.Title]);
}
}
[AfterFeature]
public static void DeleteWebDriver(FeatureContext featureContext)
{
((IWebDriver)featureContext["driver"]).Close();
((IWebDriver)featureContext["driver"]).Quit();
}
And then in each of my .cs files that contains the step definitions, I have constructors like this:
using System;
using TechTalk.SpecFlow;
using NUnit.Framework;
using OpenQA.Selenium;
using System.Collections.Generic;
using PfizerWorld2019.CommunityCreationTestAutomation.SeleniumUtils;
using System.Threading;
using System.IO;
namespace PfizerWorld2019
{
[Binding]
public class SharePointListAssets
{
private readonly IWebDriver driver;
public SharePointListAssets(FeatureContext featureContext)
{
this.driver = (IWebDriver)featureContext["driver"];
}
}
}
And then I'm using the driver variable in all the functions of the class. Lastly I have a file that I called Assembly.cs, where I put this for NUnit fixture level parallelization:
using NUnit.Framework;
[assembly: Parallelizable(ParallelScope.Fixtures)]
Which in SpecFlow's terms means parallelization on the Feature level(1 .feature file = 1 Nunit Test = 1 Nunit Fixture)
If I run my tests serially, they work fine.
But if I run 2 tests in parallel, any two tests, always something funny happens. For example: the first Chromedriver window tries to click an element and it clicks it if and only when the second Chromedriver window (that is running a different test) renders the exact same part of the website. But it sends the click to the correct window (the first one).
I have tried:
To use the IObjectContainer interface and then do containers[featureContext.FeatureInfo.Title].RegisterInstanceAs<IWebDriver>(drivers[featureContext.FeatureInfo.Title]) in the InjectWebDriver function
To use Thread.CurrentThread.ToString() instead of featureContext.FeatureInfo.Title for indexing
To do featureContext.Add(featureContext.FeatureInfo.Title + "driver", new ChromeDriver(chromeOptions) instead of drivers[featureContext.FeatureInfo.Title] = new ChromeDriver(chromeOptions); in the CreateWebDriver function.
I just do not understand what allows this "sharing". Since FeatureContext is used for everything related to driver spawning and destruction, how can two chromedrivers talk with each other?
Update: I tried the driver initialization & sharing like this:
[BeforeFeature]
public static void CreateWebDriver(FeatureContext featureContext)
{
var chromeOptions = new ChromeOptions();
chromeOptions.AddArguments("--window-size=1920,1080");
chromeOptions.AddArguments("--user-data-dir=C:/ChromeProfiles/Profile" + uniqueIndex);
WebdriverSafeSharing.setWebDriver(TestContext.CurrentContext.WorkerId, new ChromeDriver(chromeOptions));
}
and I made a webdriver-safe-sharing.cs file like this:
class WebdriverSafeSharing
{
private static Dictionary<string, IWebDriver> webdrivers = new Dictionary<string, IWebDriver>();
public static void setWebDriver(string driver_identification, IWebDriver driver)
{
webdrivers[driver_identification] = driver;
}
public static IWebDriver getWebDriver(string driver_identification)
{
return webdrivers[driver_identification];
}
}
and then in each step definition function, I'm just calling WebdriverSafeSharing.getWebDriver(TestContext.CurrentContext.WorkerId) without any involvement of the FeatureContext. And I'm still getting the same issue. Notice how I'm doing chromeOptions.AddArguments("--user-data-dir=C:/ChromeProfiles/Profile" + uniqueIndex); because I'm also starting to not trust that chromedriver itself is thread safe. But even that did not help.
Update 2: It tried an even more paranoid webdriver-safe-sharing.cs class:
class WebdriverSafeSharing
{
private static readonly Dictionary<string, ThreadLocal<IWebDriver>> webdrivers = new Dictionary<string, ThreadLocal<IWebDriver>>();
private static int port = 7000;
public static void setWebDriver(string driver_identification)
{
lock (webdrivers)
{
ChromeDriverService service = ChromeDriverService.CreateDefaultService();
service.Port = port;
var chromeOptions = new ChromeOptions();
chromeOptions.AddArguments("--window-size=1920,1080");
ThreadLocal<IWebDriver> driver =
new ThreadLocal<IWebDriver>(() =>
{
return new ChromeDriver(service, chromeOptions);
});
webdrivers[driver_identification] = driver;
port += 1;
Thread.Sleep(1000);
}
}
public static IWebDriver getWebDriver(string driver_identification)
{
return webdrivers[driver_identification].Value;
}
It has a lock, a Threadlocal and unique port. It still does not work. Exact same issues.
Update 3: If I run two separate Visual Studio instances and I run 1 test for each, it works. Or by having two identical projects run side by side
and then selecting to run the tests in parallel:
The problem appears to be that you are stashing the IWebDriver object in the FeatureContext. The FeatureContext is created and reused for each scenario in a feature. While on the surface it appears safe for running tests in parallel using NUnit (which does not run scenarios in the same feature in parallel), my hunch is that this is not as safe as you think.
Instead, initialize and destroy the IWebDriver object with each scenario, rather than feature. The ScenarioContext should be thread safe, since it is created once for each scenario, and is only used for one scenario. I would recommend using dependency injection instead:
[Binding]
public class WebDriverHooks
{
private readonly IObjectContainer container;
public WebDriverHooks(IObjectContainer container)
{
this.container = container;
}
[BeforeScenario]
public void CreateWebDriver()
{
var driver = // Initialize your web driver here
container.RegisterInstanceAs<IWebDriver>(driver);
}
[AfterScenario]
public void DestroyWebDriver()
{
var driver = container.Resolve<IWebDriver>();
// Take screenshot if you want...
// Dispose of the web driver
driver.Dispose();
}
}
Then add a constructor argument to your step definition classes to pass the IWebDriver:
[Binding]
public class FooSteps
{
private readonly IWebDriver driver;
public FooSteps(IWebDriver driver)
{
this.driver = driver;
}
// step definitions...
}
The reason was that all the selenium API actions were wrapped in static methods written by me. It was a class in a file that I wrote many weeks ago for code re-usability. However not being used into working with parallel programming in C#, I was honestly not aware anymore that these methods were declared static. I am now running 20 parallel workers on Selenium Grid.
However I'm placing here some important notes to be aware of, if one faces parallelization issues with NUnit, SpecFlow and Selenium
The initialization of the WebDriver must be done in the [BeforeFeauture]-bound method if the goal is feature-level and not scenario-level parallelization.
The initialization of the WebDriver must be thread safe. What I did is that I used a static Dictionary that is indexed by the FeatureContext.FeatureInfo.Title that contains the WebDrivers
chromedriver is thread safe. There is no need unique data-dir folders or unique ports or unique chromedriver file names. The --headless and --no-sandbox arguments that one might be interested in have not caused me any issues with parallelization (either with Selenium Grid or simple single multi-core machine parallelization). Basically don't blame the chromedriver.
For injecting the webdriver, do use the IObjectContainer interface in the [BeforeScenario]-bound method. It is great and safe.
Dispose the driver in the [AfterFeature]-bound method with driver.Dispose() so that you don't have zombie processes. This helped me with Selenium Grid, because when I was using driver.Close() and driver.Quit(), the nodes would not kill the processes after the latter were done.
For NUnit with [assembly: Parallelizable(ParallelScope.Fixtures)] enabled, all the scenarios within a .feature file run within the same FeatureContext and therefore the scenarios are FeatureContext-safe. This means that you can trust the FeatureContext for sharing data between scenarios within the same .feature file.
The [BeforeFeature] hook is being called only once per .feature file (as one would logically assume). So if you have x .feature files, the hook will be called x times during a test run.
As the Specflow Documentation says, for feature-level parallelization either NUnit or xUnit should be used as the test framework. However the NUnit has the benefit that it provides out of the box ordering for the tests, by alphabetical sort order. This is particularly useful if you want to have two scenarios run in sequence within the same feature file. i.e. putting a number in front of each scenario title will ensure their order during execution. xUnit does not support this natively and it looked like a difficult goal to achieve from searching around.
Specflow is more "friendly" in terms of parallelization, for scenario-level parallelization. This is why the Specflow+ Runner test framework by SpecFlow runs scenario levels in parallel. it looks like the whole philosophy of SpecFlow (I wouldn't say BDD yet) is to have independent scenarios. This doesn't mean of course that you cannot have very nice feature-level parallelization by using the other test frameworks. Just putting it out there as a heads up for someone reading this while drafting a strategy for writing feature files.
I'm trying to run the same nunit Test method with different values in parallel. However the second test seems to fail (i think it's trying to use the first instance of the browser;
This is the test;
namespace AutomationProject.Login_Test_Cases
{
[TestFixture]
[Parallelizable(ParallelScope.Children)]
class Login_Test_Cases: BaseTest
{
[Test]
public void LoginPar([Values("skynet" ,"skynet2")] string username)
{
lg.Log_In(username, "password");
}
}
}
This is the baseTest where the browser is set up;
namespace AutomationProject.BaseClasses
{
public class BaseTest
{
public Log_In_Methods lg;
public IWebDriver driver;
[SetUp]
public void StartBrowser()
{
System.Diagnostics.Trace.AutoFlush = true;
ChromeOptions options = new ChromeOptions();
options.AddAdditionalCapability("useAutomationExtension", false);
driver = new ChromeDriver(//path to chrome driver);
lg = new Log_In_Methods(driver);
driver.Manage().Window.Maximize();
driver.Url = "http://login-test.com";
}
I've also added [assembly: Parallelizable(ParallelScope.Children)]
[assembly: LevelOfParallelism(2)] to AssemblyInfo
The second test always seems to fail (the browser does not even get the url)
I can run different classes and tests in parallel with no issues.
Does anyone know if it's possible to run the same test method in parallel with different values?
Does anyone know if it's possible to run the same test method in parallel with different values?
This is absolutely possible. The issue here is that both tests run in parallel on a single instance of the BaseTest class, and thus you only have a lg field which both tests are trying to create/use simultaneously.
Being able to run the two separate tests with two separate BaseTest objects is an open feature request, see here: https://github.com/nunit/nunit/issues/2574
In the meantime, if you were to include your [SetUp] logic within your test method and use local variables, what you're trying to do should work.
How can i run all test methods in single browser session instead of opening and closing browser for each test method using selenium c#.
E.g.
[TestInitialize]
public void Startup()
{
driver = new FirefoxDriver();
driver.Navigate().GoToUrl("http://testurl:2022");
driver.FindElement(By.XPath("//a[contains(#id, 'tabDrugDimention')]")).Click();
System.Console.WriteLine("Dimension page loaded");
driver.FindElement(By.XPath("//a[contains(text(), 'Testcontent')]")).Click();
System.Console.WriteLine("Drug Item clicked");
}
[TestMethod]
public void DrugAnalysisclick()
{
...
}
[TestMethod]
public void DrugAnalysisclick()
{
...
}
[TestCleanup]
public void TearDown()
{
driver.Quit();
}
For all test method, new browser window is opening and getting closed for each test method.
Can anyone help, how to run all test method in single browser?
Thanks in advance.
You just have to reuse the same driver for every test instead of calling driver.Quit() after each test. As pointed out in the comments you have to be careful with this as you break up the test isolation by reusing the driver.
I am new to automation testing and selenium and have been watching alot of selenium tutorials. I realized that selenium test cases are written in 2 formats and im not sure which one to go with.
1)
namespace SeleniumTests
{
[TestFixture]
public class Login
{
private IWebDriver driver;
private StringBuilder verificationErrors;
private string baseURL;
private bool acceptNextAlert = true;
[SetUp]
public void SetupTest()
{
driver = new FirefoxDriver();
baseURL = "http://chapters.com";
verificationErrors = new StringBuilder();
}
[TearDown]
public void TeardownTest()
{
try
{
driver.Quit();
}
catch (Exception)
{
// Ignore errors if unable to close the browser
}
Assert.AreEqual("", verificationErrors.ToString());
}
[Test]
public void TheLoginTest()
{
driver.Navigate().GoToUrl(baseURL");
driver.FindElement(By.Id("loginCtrl_UserName")).Clear();
driver.FindElement(By.Id("loginCtrl_UserName")).SendKeys("operations");
driver.FindElement(By.Id("loginCtrl_Password")).Clear();
driver.FindElement(By.Id("loginCtrl_Password")).SendKeys("welcome");
driver.FindElement(By.Id("loginCtrl_LoginButton")).Click();
driver.FindElement(By.Id("btnInitialLoad")).Click();
Assert.AreEqual("Chapters", driver.Title);
}
}
}
2)
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
namespace TestProject1
{
public class UnitTest1
{
public void main()
{
FirefoxDriver driver = new FirefoxDriver();
string baseURL = "http://seleniumhq.org/";
driver.Navigate().GoToUrl(baseURL);
driver.FindElement(By.LinkText("Projects")).Click();
driver.FindElement(By.LinkText("Selenium IDE")).Click();
Assert.AreEqual(driver.FindElement(By.XPath("//div[#id='mainContent']/table/tbody/tr/td/p/b")).Text, "Selenium IDE");
driver.Close();
}
}
}
Which one do I go with?
Thanks
The second scenario is simple sequential flow of statements without added advantage of any frameworks like Junit,TestNG,Nunit. It is good for people who have just started with Selenium 2.0 and want to practice with different methods provided by webdriver.
The first scenario is logical division of the code into different annotations provided by Nunit, with these annotations one can get tremendous power of the underlining framework being used, in your case Nunit, these annotation are automatically called by the Nunit framework in a defined order. Apart from this there are multliple other functionalities provided by these frameworks like Reporting,Assertions,Support for Mock Objects,etc
Always use the first scenario for writing Selenium code because along with understanding of the webdriver code, one also gets hang of the underlining framework.
i created a little script with selenium recorder and i exported it to a c# file,
selenium created so many code lines on it but some attributes like:
[TestFixture]
public class TestinGSelenium
{
private IWebDriver driver;
private StringBuilder verificationErrors;
private string baseURL;
private bool acceptNextAlert = true;
[SetUp]
public void SetupTest()
{
driver = new FirefoxDriver();
baseURL = "http://www.google.it/";
verificationErrors = new StringBuilder();
}
[TearDown]
public void TeardownTest()
{
try
{
driver.Quit();
}
catch (Exception)
{
// Ignore errors if unable to close the browser
}
NUnit.Core.NUnitFramework.Assert.AreEqual("", verificationErrors.ToString());
}
visual studio cannot verify attributes
about :[TestFixture] [TearDown] [SetUp]
i added a reference to: "NUnit" and "selenium" but continues to return error .
what else can i do? to run this c# script?
thanks for any suggestion
Adding a reference is not enough.
You will also need to add a using for NUnit.Framework and OpenQA.Selenium and OpenQA.Selenium.Firefox:
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using NUnit.Framework;
As you are using Visual Studio, it also has a handy tip - right-click on one of the attributes it is complaining about, and it will give you a quick handy option to add the reference & using on your behalf.