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.
Related
I'm trying to customize some of nUnits behaviour, however I'm constantly hitting a brick wall
because of nUnits heavy use of code reflection. Test methods (and also setup methods etc) are passed all the way down, deep into the framework, and are converted into a delegate at the latest step possible.
The classes I'm interested in are called TestCommands and only there the framework becomes functional.
For reference here is a snippet I found in nUnits source of the TestMethodCommand class which propably is the bread and butter test execution delegate.
public class TestMethodCommand : TestCommand
{
private readonly TestMethod testMethod;
private readonly object[] arguments;
public TestMethodCommand(TestMethod testMethod) : base(testMethod)
{
this.testMethod = testMethod;
this.arguments = testMethod.Arguments;
}
public override TestResult Execute(TestExecutionContext context)
{
object result = InvokeTestMethod(context); // missing a branch deciding about sync vs. async
// missing some code that checks object against "expected result"
return context.CurrentResult;
}
private object InvokeTestMethod(TestExecutionContext context)
{
return testMethod.Method.Invoke(context.TestObject, arguments);
}
}
I'm puzzled why nUnit couldn't wrap the test method into an Func<object> way way sooner and just pass the context along. As it stands for now if I don't have a MethodInfo nUnit can't run it.
In case you wonder, here is an example of a thing I want to do but I ran into the same problem in other instances as well.
[Scenario(When: "Device Registration reads out PCB Type",
Then: "Device Type might change")]
public void Identifier_Changes_Are_Recognized()
{
var changedType = reference.ChangeType(DeviceType.Terminal);
var changedID = reference.ChangeID(123456);
Assert.Multiple(() =>
{
AssertIsSameDevice(reference, changedType);
AssertIsDifferentDevice(reference, changedID);
});
}
This scenario attribute is supposed to print a small description like so.
public void RunBeforeTest()
{
var text = new MultiLineText
("Scenario:",
"\tGiven:\t" + When,
"\tThen:\t" + Then,
"-------------\n"
);
Console.WriteLine(text);
}
I reaaallly want to tell nUnit "Look, here is an action, please run it" but for the time beeing this seems very hard to achieve. Did anyone else here run in these kinds of problems?
Are there possibly ways to achieve what I'm trying to do? Maybe create my own TestCommand, but as I mentioned, these objects only get created very deep into the framework.
OP here (Writing from my home account)
I looked more into this and actually found a working solution:
public class ArbitraryCodeExecutionWrapper : DelegatingTestCommand
{
public ArbitraryCodeExecutionWrapper(TestCommand innerCommand) : base(innerCommand)
{
}
public Action<TestExecutionContext> BeforeTest { get; init; } = _ => { };
public Action<Test, TestResult> AfterTest { get; init; } = (_,_) => { };
public override TestResult Execute(TestExecutionContext context)
{
BeforeTest(context);
var result = innerCommand.Execute(context);
AfterTest(context.CurrentTest, result);
return result;
}
}
public class NUnitTestCommandWrapperAttribute : Attribute, IWrapTestMethod
{
protected virtual void BeforeTest(TestExecutionContext context)
{
}
protected virtual void AfterTest(Test test, TestResult result)
{
}
public TestCommand Wrap(TestCommand command)
=> new ArbitraryCodeExecutionWrapper(command)
{
BeforeTest = BeforeTest,
AfterTest = AfterTest
};
}
public class ScenarioAttribute : NUnitTestCommandWrapperAttribute
{
public string When { get; init; } = "";
public string Then { get; init; } = "";
protected override void BeforeTest(TestExecutionContext context)
{
var text = new MultiLineText
("Scenario:",
"\tGiven:\t" + When,
"\tThen:\t" + Then
);
Console.WriteLine(text);
}
protected override void AfterTest(Test test, TestResult result)
{
Console.WriteLine("After Test");
}
}
[TestFixture]
public class TestCodeExecution
{
[Test]
[Scenario(When = "nUnit Comes here",
Then = "Print Hello World")]
public void Try_Out_Code_Execution()
{
Console.WriteLine("Hello World");
}
}
public class MultiLineText
{
private List<string> items = new();
public static implicit operator string(MultiLineText text) => text.ToString();
public MultiLineText(params string[] lines)
{
items = lines.ToList();
}
public override string ToString() => string.Join("\n", items);
}
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
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.
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; }
}
}
}
I am trying to figure out the reason why my unit test would fail when run together with other unit tests in the solution but pass when run alone. Can anyone show me what am I missing?
The SUT is a class called CompositeClient that is essentially a wrapper class around two other clients. It's main idea is to give priority to one of those clients to be called.
public class CompositeClient : IReceiverChannel
{
private static readonly List<IReceiverChannel> ReceiverChannels = new List<IReceiverChannel>();
public CompositeClient(IReceiverChannel priority, IReceiverChannel normal)
{
ReceiverChannels.Add(priority);
ReceiverChannels.Add(normal);
}
public async Task<IEnumerable<Request>> ReceiveBatchAsync(int batchSize)
{
var req = new List<Request>();
foreach (var channel in ReceiverChannels)
{
req.AddRange(await channel.ReceiveBatchAsync(batchSize - req.Count).ConfigureAwait(false));
if (req.Count >= batchSize)
{
break;
}
}
return req;
}
}
Running the unit test below with all the other unit tests in the solution yield me a failed result. But if I run this test alone, it will pass.
[TestMethod]
public async Task ReceivedRequestShouldComeFromPriorityClientFirst()
{
var normalPriorityClient = GetNormalClientMock();
var highPriorityClient = GetPriorityClientMock();
var compositeClient = new CompositeClient(highPriorityClient, normalPriorityClient);
var requests = await compositeClient.ReceiveBatchAsync(1);
requests.Should().HaveCount(1);
requests.First().Origin.Should().BeSameAs("priority");
normalPriorityClient.CallCount.Should().Be(1); // It will fail here with actual CallCount = 0.
highPriorityClient.CallCount.Should().Be(0);
}
private static ReceiverChannelMock GetNormalClientMock()
{
return new ReceiverChannelMock("normal");
}
private static ReceiverChannelMock GetPriorityClientMock()
{
return new ReceiverChannelMock("priority");
}
private class ReceiverChannelMock : IReceiverChannel
{
private readonly string name;
public ReceiverChannelMock(string name)
{
this.name = name;
}
public int CallCount { get; private set; }
public Task<IEnumerable<Request>> ReceiveBatchAsync(int batchSize)
{
this.CallCount++;
return Task.FromResult<IEnumerable<Request>>(
new List<Request>
{
new Request
{
Origin = this.name
}
});
}
}
Tools used:
Visual Studio 2013
.NET Framework 4.5.2
Resharper 9.2
FluentAssertion
As David pointed out, I overlooked the static field that I declared in the CompositeClient class. Removing the static keyword solved the issue.