Selenium C# parallel testing using single driver\browser - c#

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

Related

How to skip a Unit Test at runtime?

Thanks in advance!
We have some Automation tests using the selenium web driver which are great and provide a really good regression pack.
The problem is now we have feature toggles in our code. So I need to say ignore these tests unless that feature toggle is turned On/ Off. I can't find anything really searching Google.
Ideally I don't want a 'if' statement at the top of the Feature tests but it looks like it's going to be the main way. My initial thoughts where to create a custom attribute
public class IsFeatureFlagTurnedOn : Attribute
{
public IsFeatureFlagTurnedOn(string featureToggleName)
{
FeatureToggleName = featureToggleName;
}
public string FeatureToggleName {get;}
}
public class MyTests
{
[TestMethod]
[IsFeatureFlagTurnedOn("MyFeature1")]
public void ItShould()
{
// only run if MyFeature1 is turned on
}
}
I some how need to hook into the MSTest pipeline and say if this attribute is present and the logic for MyFeature1 is turned off then don't run this test - Looked at dynamically adding the [Ignore] but with no luck.
This is running through VSTS and I could use [TestCategories] but I'd have to keep updating the pipeline to which feature is turned on/off which I don't want to do.
Any help or suggestions would be great!
MSTest v2 now has a lot of extensibility points, and you can achieve this by extending the TestMethodAttribute. First we add two attribute arguments, a string for a property name and a Type that has the property. Then we override the Execute method and invoke the property via reflection. If the result is true, we'll execute the test as normal, otherwise we return an 'inconclusive` test result.
public class TestMethodWithConditionAttribute : TestMethodAttribute
{
public Type ConditionParentType { get; set; }
public string ConditionPropertyName { get; set; }
public TestMethodWithConditionAttribute(string conditionPropertyName, Type conditionParentType)
{
ConditionPropertyName = conditionPropertyName;
ConditionParentType = conditionParentType;
}
public override TestResult[] Execute(ITestMethod testMethod)
{
if (ConditionParentType.GetProperty(ConditionPropertyName, BindingFlags.Static | BindingFlags.Public)?.GetValue(null) is bool condiiton && condiiton)
{
return base.Execute(testMethod);
}
else
{
return new TestResult[] { new TestResult { Outcome = UnitTestOutcome.Inconclusive } };
}
}
}
Now we can use our new attribute like this:
[TestClass]
public class MyTests
{
[TestMethodWithCondition(nameof(Configuration.IsMyFeature1Enabled), typeof(Configuration))]
public void MyTest()
{
//...
}
}
public static class Configuration
{
public static bool IsMyFeature1Enabled => false;
}
The above is a very generic solution. You could also customize it a little more to your particular use case to perhaps avoid quite so much verbosity in the attribute declaration:
public class TestMethodForConfigAttribute : TestMethodAttribute
{
public string Name { get; set; }
public TestMethodForConfigAttribute(string name)
{
Name = name;
}
public override TestResult[] Execute(ITestMethod testMethod)
{
if (IsConfigEnabled(Name))
{
return base.Execute(testMethod);
}
else
{
return new TestResult[] { new TestResult { Outcome = UnitTestOutcome.Inconclusive } };
}
}
public static bool IsConfigEnabled(string name)
{
//...
return false;
}
}
And use it like:
[TestClass]
public class MyTests
{
[TestMethodForConfig("MyFeature1")]
public void MyTest()
{
//...
}
}
Based on my reading of this, you may need to use Assert.Inconclusive

Error CS0103: The name 'TimeSpan' does not exist in the current context (CS0103) (testingProgram)?

I am trying to create tests in C# with selenium driver in visual studio. I get the following error. Error CS0103: The name 'TimeSpan' does not exist in the current context (CS0103) (testingProgram) ?? I also have a second error displayed in the images provided. The code uses the PageObjectPattern >> https://www.automatetheplanet.com/page-object-pattern/
Btw I am using a mac. I have added some images to help better describe the situation.The following images show both files. Can someone please try to run in on their end to see if it works.
How do I fix this? Can someone try and run the program to see if it is running on their end?? How do I get this program to run successfully?
here is the following code-
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support;
using OpenQA.Selenium.Support.UI;
[TestClass]
public class SearchEngineTests
{
public IWebDriver Driver { get; set; }
public WebDriverWait Wait { get; set; }
[TestInitialize]
public void SetupTest()
{
this.Driver = new FirefoxDriver();
this.Wait = new WebDriverWait(this.Driver, TimeSpan.FromSeconds(30));
}
[TestCleanup]
public void TeardownTest()
{
this.Driver.Quit();
}
[TestMethod]
public void SearchTextInSearchEngine_First()
{
SearchEngineMainPage searchEngineMainPage = new SearchEngineMainPage(this.Driver);
searchEngineMainPage.Navigate();
searchEngineMainPage.Search("Automate The Planet");
searchEngineMainPage.ValidateResultsCount("264,000 RESULTS");
}
}
here is the second file-
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.PageObjects;
public class SearchEngineMainPage
{
private readonly IWebDriver driver;
private readonly string url = #"searchEngineUrl";
[FindsBy(How = How.Id, Using = "sb_form_q")]
public IWebElement SearchBox { get; set; }
[FindsBy(How = How.Id, Using = "sb_form_go")]
public IWebElement GoButton { get; set; }
[FindsBy(How = How.Id, Using = "b_tween")]
public IWebElement ResultsCountDiv { get; set; }
public void Navigate()
{
this.driver.Navigate().GoToUrl(this.url);
}
public void Search(string textToType)
{
this.SearchBox.Clear();
this.SearchBox.SendKeys(textToType);
this.GoButton.Click();
}
public void ValidateResultsCount(string expectedCount)
{
Assert.IsTrue(this.ResultsCountDiv.Text.Contains(expectedCount), "The results DIV doesn't contains the specified text.");
}
}
Okay, so, in both cases, the error message tells you exactly what's missing.
First, TimeSpan is in the System namespace, and there is no using System; in there, so the compiler can't find it.
Second, SearchEngineMainPage doesn't have a constructor that takes a single parameter (in fact it doesn't have a constructor at all, so the compiler generates one for you, but that one takes no parameters, so it's still not good).

Running one tests on multiple browsertypes in parallel using Selenium, NUnit and C#

What I'm using:
Selenium WebDriver (v3.2.0)
NUnit (v3.6.0)
C#
I've found online how to run a single test using multiple browser types in parallel, my code is as follows and this works:
namespace MultipleBrowserTest
{
[TestFixture(typeof(FirefoxDriver))]
[TestFixture(typeof(ChromeDriver))]
[TestFixture(typeof(InternetExplorerDriver))]
[TestFixture(typeof(EdgeDriver))]
public class SiteLoadsTest<TWebDriver> where TWebDriver : IWebDriver, new()
{
private IWebDriver _driver;
[Test]
public void MultipleBrowserTests()
{
_driver = new TWebDriver();
_driver.Navigate().GoToUrl("https://google.com/");
Assert.AreEqual("https://google.com/", _driver.Url);
}
[TearDown]
public void FixtureTearDown()
{
_driver?.Quit();
if (_driver != null) _driver.Dispose();
}
}
}
However, I want to make this more maintainable so that every test class the QA doesn't have to format the class with the 'where...' part (public class SomeUITestClass<TWebDriver> where TWebDriver : IWebDriver, new()). I was looking at making the test inheriting a Browser class like this:
public class Browsers<TWebDriver> where TWebDriver : IWebDriver, new()
{
private IWebDriver Browser { get; set; }
public IWebDriver Driver
{
get
{
if (Browser == null)
{
throw new NullReferenceException(
"The WebDriver browser instance was not initialized.");
}
return null;
}
set { Browser = value; }
}
public void LaunchDriver()
{
Browser = new TWebDriver();
}
}
And editing my test to be like this:
namespace MultipleBrowserTest
{
[TestFixture(typeof(FirefoxDriver))]
[TestFixture(typeof(ChromeDriver))]
[TestFixture(typeof(InternetExplorerDriver))]
[TestFixture(typeof(EdgeDriver))]
public class SiteLoadsTest_InheritedBrowser : Browsers<>
{
[SetUp]
public void Setup()
{
LaunchDriver();
}
[Test]
public void MultipleBrowserTests()
{
Driver.Navigate().GoToUrl("https://google.com/");
Assert.AreEqual("https://google.com/", Driver.Url);
}
[TearDown]
public void FixtureTearDown()
{
Driver?.Quit();
if (Driver != null) Driver.Dispose();
}
}
}
However I don't know what to pass into Browsers<>. If I don't pass anything I get "Unexpected use of an unbound generic name". If I pass in IWebDriver I am getting 'IWebDriver' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TWebDriver' in the generic type or method 'Browsers'.
I've got to the extent of my C# skills (as a QA) and I'm not sure how to resolve this or even whether it's resolvable! Any help would be much appriciated.
So with the amazing help of a fellow QA, we've worked out how to do this using reflection. We do have to add the constructor for our test (see below the Test Class):
namespace MultipleBrowserTest
{
[TestFixture(typeof(FirefoxDriver))]
[TestFixture(typeof(ChromeDriver))]
[TestFixture(typeof(InternetExplorerDriver))]
[TestFixture(typeof(EdgeDriver))]
public class SiteLoadsTest_InheritedBrowser : Browsers_Reflection
{
public SiteLoadsTest_InheritedBrowser(Type type) : base(type)
{
}
[Test]
public void MultipleBrowserTests()
{
Driver.Navigate().GoToUrl("https://google.com/");
Driver.Url.ShouldContain("google");
}
}
}
And this is the Browser class that we'll keep in our framework:
namespace MultipleBrowserTest
{
public class Browsers_Reflection
{
public Browsers_Reflection(Type type)
{
Driver = (IWebDriver)Activator.CreateInstance(type);
}
private IWebDriver Browser { get; set; }
public IWebDriver Driver
{
get {
if (Browser == null)
throw new NullReferenceException(
"The WebDriver browser instance was not initialized.");
return Browser;
}
set { Browser = value; }
}
}
}
Edit: We are also looking at using UnityContainer which we were having issues with but have worked it out, here is the code:
namespace MultipleBrowserTest
{
public class Browsers_UnityContainer
{
public Browsers_UnityContainer(Type type)
{
_unityContainer.RegisterType(typeof(IWebDriver), type, new InjectionConstructor());
Browser = _unityContainer.Resolve<IWebDriver>();
}
private IWebDriver Browser { get; set; }
private readonly UnityContainer _unityContainer = new UnityContainer();
public IWebDriver Driver
{
get { return Browser; }
set { Browser = value; }
}
}
}

AutoComplete for string parameter

I have set up a testproject using NUnit and Selenium Webdriver of which you can find a shortened version below.
class ByHolder
{
public readonly string name, path;
public readonly Func<string, By> call;
public ByHolder(string name, string path, Func<string, By> call)
{
this.name = name;
this.path = path;
this.call = call;
}
}
class Page
{
private readonly List<ByHolder> LocatorList = new List<ByHolder>();
public Page()
{
SetUpList();
}
private void SetUpList()
{
AddLocator("Button0", "//button0", By.XPath);
AddLocator("Button1", "button1", By.Id);
...
}
public By Get(string locatorName)
{
var holder = LocatorList.FirstOrDefault(p => p.name.Equals(locatorName));
return holder?.call(holder.path);
}
public void AddLocator(string name, string path, Func<string, By> call)
{
LocatorList.Add(new ByHolder(name, path,call ));
}
}
class PersonelDriver : IWebDriver
{
IWebDriver driver = new FirefoxDriver();
Page page = new Page();
public void Click(string locatorName)
{
driver.FindElement(page.Get(locatorName)).Click();
}
...
}
[TestFixture]
class PageTest
{
private readonly PersonelDriver d = new PersonelDriver();
[Test]
public void ClickTest0()
{
d.Click("Button0");
}
[Test]
public void ClickTest1()
{
d.Click("Button1");
}
...
}
As you can hopefully see I tried implementing a shortened method with a minimum of variables to make longer testcases easier to read mainly for outsiders but also for me, for example.
d.Click("that");
d.EnterText("thisLocator","text");
d.WaitFor("somethingElse");
d.Click("this");
(After using Selenium for some time I find that things can become chaotic quite fast when repeatedly using the driver.FindElement... in the tests themselves.)
Even tough I'm happy with the shortened versions and readability, there is of course no autocomplete or check since i'm handling strings and not IWebElement objects or By references that have been named or put in a specific getter.
What I used to do was the following, but it just felt wrong:
class Locators
{
public By GetButton()
{
return By.Id("button");
}
...
}
I was wondering if there is a way to implement an autocomplete or some other check for the string values when adding for example d.Click("stringvalue");
Thank you in advance.

Unable to Import parts with Metadata

I'm trying to import parts and include a custom MetadataAttribute, following the imperative model, using .NET 4.5
Below, I've included the simplest of example I can, which illustrates the problem.
When this code is executed, the Engine class constructor is called, and passed an empty Enumerator, rather than the two plugins which are clearly part of the project.
At the moment I'm suspecting the PluginMetadata attribute, but I don't see how to get Metadata into the catalog without it.
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Registration;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var builder = new RegistrationBuilder();
builder.ForTypesDerivedFrom<IPlugIn>().Export<Lazy<IPlugIn, IPlugInMetadata>>();
builder.ForType<Engine>().Export();
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly(), builder);
var container = new CompositionContainer(catalog);
var engine = container.GetExport<Engine>();
engine.Value.Run();
}
}
internal class Engine
{
private IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> PlugIns { get; set; }
public Engine(IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> plugins)
{
PlugIns = plugins;
}
public void Run()
{
foreach (var plugIn in PlugIns)
{
Console.WriteLine("Starting {0}", plugIn.Metadata.Name);
plugIn.Value.Work();
}
}
}
interface IPlugIn
{
void Work();
}
interface IPlugInMetadata
{
string Name { get; }
}
[MetadataAttribute]
class PlugInMetadataAttribute : ExportAttribute, IPlugInMetadata
{
public PlugInMetadataAttribute(string name)
{
this.name = name;
}
private readonly string name;
public string Name { get { return name; } }
}
[PlugInMetadata("PlugIn1")]
class PlugIn1 : IPlugIn
{
public void Work()
{
Console.WriteLine("PlugIn 1 working");
}
}
[PlugInMetadata("PlugIn2")]
class PlugIn2 : IPlugIn
{
public void Work()
{
Console.WriteLine("PlugIn 2 working");
}
}
}
Metadata interfaces must not have any properties with setters. You should modify the IPlugInMetadata interface so its properties won't have any setters, otherwise the composition will fail:
interface IPlugInMetadata
{
string Name { get; }
}
Also, you should consider making your PlugInMetadataAttribute class inherit from ExportAttribute rather than Attribute. That will allow using this attribute as an export attribute and you won't have to use a RegistrationBuilder.
EDIT: I think I found your problem
When trying to use ImportMany in the constructor, you must specify so explicitly, so your constructor should look like this:
[ImportingConstructor]
public Engine([ImportMany] IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> plugins)
{
PlugIns = plugins;
}
Alternatively, you can choose to import it as a property:
[ImportMany]
private IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> PlugIns { get; set; }
As a side note, when deriving from ExportAttribute, you'd like to include constructors that automatically export your part as IPlugIn:
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
class PlugInMetadataAttribute : ExportAttribute, IPlugInMetadata
{
public PlugInMetadataAttribute()
: base(typeof(IPlugIn))
{
}
public PlugInMetadataAttribute(string contractName)
: base(contractName, typeof(IPlugIn))
{
}
public string Name { get; set; }
}

Categories

Resources