FindElement doesn't find anything with Appium - c#

Using the following test method (simplified here for the purpose of the question, please ignore the design issues as this isn't the actual code)
[TestClass]
public class LoginTests
{
protected static WindowsDriver<WindowsElement> session;
[TestMethod]
public void Login_WithValidCredentials_Logsin()
{
if (session == null)
{
var options = new AppiumOptions();
options.AddAdditionalCapability("app", #"D:\myApp.exe");
options.AddAdditionalCapability("deviceName", "WindowsPC");
session = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), options);
Assert.IsNotNull(session);
session.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1.5);
}
//Arrange
string username = "username";
var txtUsername = session.FindElementByName("txtUsername");
//Act
//To be developed
//Assert
Assert.AreEqual(username, txtUsername.Text);
}
}
I can't find any element in the UI, the app launches correctly when the test is ran (having WinAppDriver running in the background) but no matter how I try to find the elements on the screen I always get the same error message
"error":"no such element"
"message":"An element could not be located on the page using the given search parameters."
Update 1
Based on the first comment below, I used inspect.exe to confirm the name of the element on the screen that I'm trying to get and nop, I can't find it in the test.

Related

MSTest Login to web app and assert web app is loaded before beginning tests

I am currently using MSTest in visual studio 2019 and am having an issue with selenium finding an element on the dashboard after login. After some debugging I found that the login method that I have set in my OneTimeSetUp method isn't fully finishing, before the other tests in the test class run., isn't this weird behavior? I would expect for my login method with an assert that the page is loaded to finish before moving to the other tests being that it is in the OneTimeSetUp method. Am I doing something wrong? Is there something that anyone would recommend? Again, I am trying to login and validate that the dashboard is loaded before running the test methods in the test class. Thank you!
Here is my login method:
public void Login()
{
var _driver = DriverHelper.Driver;
//Creates a login page object
var loginPage = new HomeBankingLoginPage();
//Enters login credentials
loginPage.UserName.SendKeys(getElementsXML("userName"));
loginPage.Password.SendKeys(getElementsXML("password"));
loginPage.Submit.Click();
WebDriverWait implicitlyWait = new WebDriverWait(_driver, TimeSpan.FromSeconds(20));
IWebElement firstResult = implicitlyWait.Until(e => e.FindElement(By.LinkText("Transfer")));
}
Here is my oneTimeSetUp method:
//Executes before tests start
//Opens Homebanking URL and Login
[OneTimeSetUp]
public void Setup()
{
Login();
}
See this line,
WebDriverWait implicitlyWait = new WebDriverWait(_driver, TimeSpan.FromSeconds(20));
I know that implicitlyWait is just a reference. But new WebDriverWait(_driver, TimeSpan.FromSeconds(20)); this is Explicit waits. See here
as well as I do not see assertion. What I would suggest is :-
Use Explicit wait to login into the application.
Assert the dashboard page (either title, or webpage heading or Profile pic, just make sure whatever you are asserting should be one of the last loaded element in UI after login)
Your next test method after login should be invoked.
I will be using [TestInitialize] -
Marks a method that should be called before each test method. One such
method should be present per test class.
Sample code :-
[TestClass]
public class TestClass
{
public void Login()
{
var _driver = DriverHelper.Driver;
_driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10); // This is implicit wait
//Creates a login page object
var loginPage = new HomeBankingLoginPage();
//Enters login credentials
loginPage.UserName.SendKeys(getElementsXML("userName"));
loginPage.Password.SendKeys(getElementsXML("password"));
loginPage.Submit.Click();
//If you have NuGet, search for DotNetSeleniumExtras.WaitHelpers, and import that namespace into your class.
var wait = new WebDriverWait(driver, new TimeSpan(0, 0, 30));
var element = wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementIsVisible(By.LinkText("Transfer")));
string dashBoardText = element.Text;
string expectedText = "you should write expected text from transfer from Dashboard here"
Assert.IsTrue(expectedText.Contains(dashBoardText));
// if the above does not work, you can still try with lamba expression :-
WebDriverWait explicitWait = new WebDriverWait(_driver, TimeSpan.FromSeconds(20));
IWebElement firstResult = explicitWait.Until(e => e.FindElement(By.LinkText("Transfer")));
string dashBoardText = firstResult.Text;
string expectedText = "you should write expected text from transfer from Dashboard here"
Assert.IsTrue(expectedText.Contains(dashBoardText));
}
[TestInitialize]
public void TestInitialize()
{
Console.WriteLine("Inside TestInitialize");
Login();
}
[TestMethod]
public void Test()
{
Console.WriteLine("Inside TestMethod");
}
[TestCleanup]
public void TestCleanup()
{
Console.WriteLine("Inside TestCleanup");
}
}
You can try setting the implicit wait, then just try and locate an element you know should be visible when the page is ready. The driver will then wait for the implicit wait time before throwing an exception.
public void Login()
{
var _driver = DriverHelper.Driver;
_driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(20);
// Let set the page timeout too, just to be safe.
_driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(20);
//Creates a login page object
var loginPage = new HomeBankingLoginPage();
//Enters login credentials
loginPage.UserName.SendKeys(getElementsXML("userName"));
loginPage.Password.SendKeys(getElementsXML("password"));
loginPage.Submit.Click();
// Locate the element you know should be there, it will time out after the implicitWait.
var transferElement = _driver.FindElement(By.LinkText("Transfer"));
}

System.NullReferenceException when reading browser log with selenium

I am writing using C#, selenium chromeWebDriver. When I try to read the browser console log file with selenium I get:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
private void button1_Click(object sender, EventArgs e)
{
ChromeOptions options = new ChromeOptions();
options.SetLoggingPreference(LogType.Browser, LogLevel.Warning);
IWebDriver driver = new ChromeDriver(options);
driver.Url = "https://www.google.com/";
var entries = driver.Manage().Logs.GetLog(LogType.Browser); // System.NullReferenceException
foreach (var entry in entries)
{
Console.WriteLine(entry.ToString());
}
}
This is my solution until Selenium 4 is out (will work also with Selenium 4).
It is quick and dirty and was design to demonstrate how it can be done. Feel free to alter and improve.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Remote;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Reflection;
using System.Text;
namespace GetChromeConsoleLog
{
internal static class Program
{
private static void Main()
{
// setup options
var options = new ChromeOptions();
options.SetLoggingPreference(LogType.Browser, LogLevel.All);
// do whatever actions
var driver = new ChromeDriver(options)
{
Url = "https://www.yahoo.com/"
};
var logs = driver.GetBrowserLogs();
// extract logs (using the extension method GetBrowserLogs)
foreach (var log in driver.GetBrowserLogs())
{
Console.WriteLine($"{log["timestamp"]}: {log["message"]}");
}
// cleanup
driver.Dispose();
// hold console
Console.WriteLine();
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
}
public static class WebDriverExtensions
{
public static IEnumerable<IDictionary<string, object>> GetBrowserLogs(this IWebDriver driver)
{
// not a chrome driver
if(driver.GetType() != typeof(ChromeDriver))
{
return Array.Empty<IDictionary<string, object>>();
}
// setup
var endpoint = GetEndpoint(driver);
var session = GetSession(driver);
var resource = $"{endpoint}session/{session}/se/log";
const string jsonBody = #"{""type"":""browser""}";
// execute
using (var httpClient = new HttpClient())
{
var content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
var response = httpClient.PostAsync(resource, content).GetAwaiter().GetResult();
var responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return AsLogEntries(responseBody);
}
}
private static string GetEndpoint(IWebDriver driver)
{
// setup
const BindingFlags Flags = BindingFlags.Instance | BindingFlags.NonPublic;
// get RemoteWebDriver type
var remoteWebDriver = GetRemoteWebDriver(driver.GetType());
// get this instance executor > get this instance internalExecutor
var executor = remoteWebDriver.GetField("executor", Flags).GetValue(driver) as ICommandExecutor;
// get URL
var uri = executor.GetType().GetField("remoteServerUri", Flags).GetValue(executor) as Uri;
// result
return uri.AbsoluteUri;
}
private static Type GetRemoteWebDriver(Type type)
{
if (!typeof(RemoteWebDriver).IsAssignableFrom(type))
{
return type;
}
while (type != typeof(RemoteWebDriver))
{
type = type.BaseType;
}
return type;
}
private static SessionId GetSession(IWebDriver driver)
{
if (driver is IHasSessionId id)
{
return id.SessionId;
}
return new SessionId($"gravity-{Guid.NewGuid()}");
}
private static IEnumerable<IDictionary<string, object>> AsLogEntries(string responseBody)
{
// setup
var value = $"{JToken.Parse(responseBody)["value"]}";
return JsonConvert.DeserializeObject<IEnumerable<Dictionary<string, object>>>(value);
}
}
}
get logs
I have previously used the property above but I cannot get it to work currently with the ChromeDriver 75+. I found issues related to it reported here.
This issue was supposedly fixed as it was reported in GitHub issue #7323 here, but I have actually attempted to test this fix in ChromeDriver Nuget version 77.0.3865.4000 and it still proves to be an issue.
Further experimentation with newer Chromedriver version 78.0.3904.7000 (currently Latest Stable version at the time of writing) shows that the issue still exists.
I have also experimented with using workarounds provided in other Selenium issue #7335 here back in September, and while this workaround does allow the driver to be instantiated, the logs are still inaccessible (and null).
Workaround when creating chromedriver instance: typeof(CapabilityType).GetField(nameof(CapabilityType.LoggingPreferences), BindingFlags.Static | BindingFlags.Public).SetValue(null, "goog:loggingPrefs");
Based on what MatthewSteeples said in that issue (see quote below), the fix is in place just not yet fully released to Nuget. Hopefully it will come in with the next release.
"The issue has been resolved but the fix is not (yet) available on NuGet so you'll need to roll your own if you need it before the next release is out" - MatthewSteeples September 25'th 2019
Edit: It may be worth mentioning the reason for using an older ChromeDriver Nuget is so that running automated tests locally and in the Hosted Azure Devops release pipeline is possible without manually modifying the nuget version locally.

How do I run a test on multiple browsers at the same time? Selenium Grid, C#, Specflow, NUnit

I have been bouncing between guides and YouTube videos trying to implement Selenium Grid 2 on an existing project for a couple of days and I've gotten stuck, please help!
Our framework is Specflow 3.0.220, Selenium WebDriver 3.141.0, C#, NUnit 3.12.0, Selenium Grid selenium-server-standalone-3.141.59.
My initial objectives to implement Selenium Grid 2 are as follows:
Set up a hub and node(s) on my local machine = done.
Run a test through one of the nodes = done.
Run a test on all nodes simultaneously = headache.
Regarding item 2, I have set up two nodes, one is a Chrome node and one is a Firefox node. I can run a test through both of them, but not simultaneously.
I feel that I missing a piece of the puzzle here.
Here's the set-up:
Scenario Outline: Log in
Given I launch the site for <profile> and <environment> and <parallelEnvironment>
When I log in to the Normal account
Then I see that I am logged in
Examples:
| profile | environment | parallelEnvironment |
| parallel | Chrome75 | grid |
If profile is parallel and parallelEnvironment is grid, environment is ignored. The reason for parallelEnvironment is because we may still use Browserstack in the interim while setting up Selenium Grid.
These steps use relevant step files etc and page files (but not using Page Object Model as that's been deprecated).
The driver set-up is as follows:
namespace OurAutomation
{
[Binding]
public sealed class BrowserStack
{
private BrowserStackDriver bsDriver;
public static BrowserStackDriver bdriver;
[BeforeScenario]
public void BeforeScenario()
{
bsDriver = new BrowserStackDriver();
bdriver = bsDriver;
}
[AfterScenario]
public void AfterScenario()
{
bsDriver.Cleanup();
}
}
public class CustomRemoteWebDriver : RemoteWebDriver
{
public CustomRemoteWebDriver(Uri remoteAddress, ChromeOptions options) : base(remoteAddress, options)
{
}
public string getSessionID()
{
return base.SessionId.ToString();
}
}
public class BrowserStackDriver
{
private IWebDriver driver;
public static bool isBrowserStack = false;
public static string Platform;
public static string theEnvironment;
public static string sessionId;
public BrowserStackDriver()
{
}
public string GetString(string property)
{
if (TestContext.Parameters[property] == null)
{
throw new ArgumentException("Property does not exist, does not have a value, or a test setting is not selected. You may need to add the .runsettings file in Visual Studio (Test > Test Settings > Select Test Settings File).");
}
else
{
return TestContext.Parameters[property].ToString();
}
}
public IWebDriver Init(string profile, string environment, string parallelEnvironment)
{
String testString = GetString("BuildNumber");
theEnvironment = environment;
NameValueCollection caps = ConfigurationManager.GetSection("capabilities/" + profile) as NameValueCollection;
NameValueCollection settings = ConfigurationManager.GetSection("environments/" + environment) as NameValueCollection;
ChromeOptions chromeOptions = new ChromeOptions();
if (profile == "single")
{
// logic to invoke relevant browser locally based on Specflow parameter 'profile'
Thread.Sleep(3000);
}
else if (profile == "parallel")
{
if (parallelEnvironment == "browserstack")
{
foreach (string key in caps.AllKeys)
{
chromeOptions.AddAdditionalCapability(key, caps[key]);
}
foreach (string key in settings.AllKeys)
{
chromeOptions.AddAdditionalCapability(key, settings[key]);
}
string username = Environment.GetEnvironmentVariable("BROWSERSTACK_USERNAME");
if (username == null)
{
username = ConfigurationManager.AppSettings.Get("user");
}
string accesskey = Environment.GetEnvironmentVariable("BROWSERSTACK_ACCESS_KEY");
if (accesskey == null)
{
accesskey = ConfigurationManager.AppSettings.Get("key");
}
chromeOptions.AddAdditionalCapability("browserstack.user", username);
chromeOptions.AddAdditionalCapability("browserstack.key", accesskey);
chromeOptions.AddAdditionalCapability("browserstack.local", "true");
chromeOptions.AddAdditionalCapability("build", GetString("BuildNumber"));
chromeOptions.AddAdditionalCapability("name", TestContext.CurrentContext.Test.MethodName);
chromeOptions.AddAdditionalCapability("project", GetString("Project"));
BrowserStackDriver.isBrowserStack = true;
driver = new CustomRemoteWebDriver(
new Uri("http://" + ConfigurationManager.AppSettings.Get("server") + "/wd/hub/"), chromeOptions);
CustomRemoteWebDriver browserRemoteDriver = driver as CustomRemoteWebDriver;
sessionId = browserRemoteDriver.getSessionID();
}
else if (parallelEnvironment == "grid")
{
driver = new RemoteWebDriver(new Uri("http://000.00.00.00:4444/wd/hub"), chromeOptions);
}
}
return driver;
}
public void Cleanup()
{
Thread.Sleep(2000);
if (isBrowserStack)
{
Log.Status status = (TestContext.CurrentContext.Result.Message == null) ? Log.Status.Passed : Log.Status.Failed;
string reason = (TestContext.CurrentContext.Result.Message == null) ? "Passed" : "Error see exception";
Log.UpdateTestStatus(status, reason, sessionId);
}
driver.Quit();
driver = null;
}
}
}
So in here...
else if (parallelEnvironment == "grid")
{
driver = new RemoteWebDriver(new Uri("http://000.00.00.00:4444/wd/hub"), chromeOptions);
}
...I enter the address of one of the nodes and the test gets conducted. However, I just want to send the test to the hub and for it to then execute that one test on all active nodes in their related browsers simultaneously. How do I achieve that? The guides and videos only seem to be taking me so far.
thank you
UPDATE:
So I'm inching further in the right direction I think. Had to roll this back to basics, so I can see how to implement this in my existing project. I've made this work in my grid: https://github.com/teixeira-fernando/Parallel-Execution-with-Selenium-Grid
However I note that I need to add attributes to the tests (to run one test on multiple browsers simultaneously)...
namespace Tutorial_parallel_execution
{
[TestFixture(BrowserType.Chrome)]
[TestFixture(BrowserType.Firefox)]
[TestFixture(BrowserType.Opera)]
[TestFixture(BrowserType.IE)]
[Parallelizable(ParallelScope.Fixtures)]
public class GoogleTesting : Hooks
{
public GoogleTesting(BrowserType browser) : base(browser)
{
}
[Test]
public void GoogleTest()
{
Driver.Navigate().GoToUrl("http://www.google.com");
Driver.FindElement(By.Name("q")).SendKeys("selenium");
Driver.FindElement(By.Name("btnK")).Click();
Assert.That(Driver.PageSource.Contains("Selenium"), Is.EqualTo(true),
"The text selenium doenst exist");
}
}
}
However, since my project started complaining similarly to this SpecFlow Visual Studio extension attempted to use SpecFlow code-behind generator 1.9, I started using SpecFlow.Tools.MsBuild.Generation and lost access to the tests (the code-behind files) in order to add the attributes. The only attribute I can add is [Parallelizable(ParallelScope.Fixtures)] but I have to put this in AssemblyInfo.cs - the other attributes can't be added there.
Do I need to be downgrading the versions of Specflow/Selenium etc in order to make this work??
I was able to strip out the code necessary to implement parallel execution using ThreadLocal from https://github.com/minhhoangvn/AutomationFramework
Add this to your AssemblyInfo.cs file:
[assembly: Parallelizable(ParallelScope.Fixtures)]
[assembly: LevelOfParallelism(4)]
Where you see 4 is the number of tests you want to run at the same time. So if you have 2 nodes, but you want to run 4 tests at the same time, then each node will get 2 chrome browsers.
When you use MsBuild.Generation the feature.cs files are still there, they just don't show up in visual studio.
You could try adding this to your Hooks.cs file when creating the driver:
ScenarioContext _scenarioContext;
IWebDriver _currentWebDriver;
_currentWebDriver = new RemoteWebDriver(new Uri(Utilities.SeleniumHub), options.ToCapabilities(), TimeSpan.FromMinutes(3));
_scenarioContext.ScenarioContainer.RegisterInstanceAs<IWebDriver>(_currentWebDriver);
And then this when you are done with the scenario:
[AfterScenario]
public void CloseBrowserAfterScenario()
{
string driver_process_name = null;
string browser_process_name = null;
switch (browser)
{
case "Chrome":
driver_process_name = "chromedriver.exe";
break;
case "IEX64":
case "IEX86":
driver_process_name = "IEDriverServer.exe";
break;
case "Edge":
driver_process_name = "MicrosoftWebDriver.exe";
browser_process_name = "MicrosoftEdge.exe";
break;
case "Firefox":
driver_process_name = "geckodriver.exe";
break;
default:
LogMessage(browser + "is not found or not supported... Please update the TestUI.dll.Config File");
break;
}
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName(driver_process_name);
foreach (System.Diagnostics.Process app_process in process)
{
if (!string.IsNullOrEmpty(app_process.ProcessName))
{
try
{
app_process.Kill();
}
catch
{
FunctionalUtil.LogMessage("app_process.Kill(); failed in CloseBrowserAfterScenario");
}
}
}

Selenium. Bring-up window on the front

If you run the following code, then at each iteration of the cycle, the browser will bring up on the front and get focus.
public class Program
{
private static void Main()
{
var driver = new ChromeDriver();
driver.Navigate().GoToUrl("https://i.imgur.com/cdA7SBB.jpg");
for (int i = 0; i < 100; i++)
{
var ss = ((ITakesScreenshot)driver).GetScreenshot();
ss.SaveAsFile("D:/imgs/i.jpg");
}
}
}
The question is: why does this happen and can it be turned off? headless mod does not fit.
It seems that this always happens when Selenium needs to save / read the file or start the process.
To take a screenshot, chromedriver activates the window. It's by design and there's no option to avoid it even though it's technically possible.
For the relevant sources have a look at window_commands.cc.
You could however avoid the effect by moving the window off-screen:
driver.Manage().Window.Position = new Point(-32000, -32000);
or by launching the browser off-screen:
var options = new ChromeOptions();
options.AddArgument("--window-position=-32000,-32000");
UPDATE
You can avoid the activation by taking the screenshot directly via the devtool API. Here's a class to override GetScreenshot:
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Remote;
using JObject = System.Collections.Generic.Dictionary<string, object>;
class ChromeDriverEx : ChromeDriver
{
public ChromeDriverEx(ChromeOptions options = null)
: base(options ?? new ChromeOptions()) {
var repo = base.CommandExecutor.CommandInfoRepository;
repo.TryAddCommand("send", new CommandInfo("POST", "/session/{sessionId}/chromium/send_command_and_get_result"));
}
public new Screenshot GetScreenshot() {
object response = Send("Page.captureScreenshot", new JObject {{"format", "png"}, {"fromSurface", true}});
string base64 = (string)((JObject)response)["data"];
return new Screenshot(base64);
}
protected object Send(string cmd, JObject args) {
return this.Execute("send", new JObject {{"cmd", cmd}, {"params", args}}).Value;
}
}
usage:
var driver = new ChromeDriverEx();
driver.Url = "https://stackoverflow.com";
driver.GetScreenshot().SaveAsFile("/tmp/screenshot.png");
driver.Quit();
When you invoke Navigate().GoToUrl("url") method through your Automation script, it is expected that your script will be interacting with some of the elements on the webpage. So for Selenium to interact with those elements, Selenium needs focus. Hence opening up the browser, bring up on the front and getting the focus is the default phenomenon implemented through Navigate().GoToUrl("url").
Now Default Mode or Headless Mode is controlled by the ChromeOption/FirefoxOptions class which is passed as an argument while initializing the WebDriver instance and will call Navigate().GoToUrl("url"). So, Navigate().GoToUrl("url") would have no impact how the WebDriver instance is controlling the Mode of Operation i.e. Default Mode or Headless Mode.
Now when you try to invoke the method from ITakesScreenshot Interface i.e. ITakesScreenshot.GetScreenshot Method which is defined as :
Gets a Screenshot object representing the image of the page on the screen.
In case of WebDriver instance which extends ITakesScreenshot, makes the best effort depending on the browser to return the following in order of preference:
Entire page
Current window
Visible portion of the current frame
The screenshot of the entire display containing the browser
There may be some instances when the browser looses the focus. In that case you can use IJavascriptExecutor to regain the focus as follows :
((IJavascriptExecutor) driver).executeScript("window.focus();");
I was struggling with an issue when generic GetScreenshot() in parallel testing was causing browser to lose focus. Some elements were being removed from DOM and my tests were failing. I've come up with a working solution for Edge and Chrome 100+ with Selenium 4.1:
public Screenshot GetScreenshot()
{
IHasCommandExecutor executor = webDriverInstance as IHasCommandExecutor;
var sessionId = ((WebDriver)webDriverInstance).SessionId;
var command = new HttpCommandInfo(HttpCommandInfo.PostCommand, $"/session/{sessionId}/chromium/send_command_and_get_result");
executor.CommandExecutor.TryAddCommand("Send", command);
var response = Send(executor, "Page.captureScreenshot", new JObject { { "format", "png" }, { "fromSurface", true } });
var base64 = ((Dictionary<string, object>)response.Value)["data"];
return new Screenshot(base64.ToString());
}
private Response Send(IHasCommandExecutor executor, string cmd, JObject args)
{
var json = new JObject { { "cmd", cmd }, { "params", args } };
var command = new Command("Send", json.ToString());
return executor.CommandExecutor.Execute(command);
}

GUI interface testing using NUnit.Forms

I'm trying to write some tests for testing GUI interface. I decided to choose NUnit.Forms. But the tests fall with the following error:
TearDown : System.ComponentModel.Win32Exception : The requested resource is in use
I have two versions of the source code tests.
First:
using System.Windows.Forms;
using NUnit.Extensions.Forms;
using NUnit.Framework;
using YAMP;
namespace Tests.GUITests
{
[TestFixture]
public class GuiTest : NUnitFormTest
{
private FrmMain _frm;
//[SetUp] // or it is still needed
public override void Setup()
{
base.Setup();
_frm = new FrmMain();
_frm.Show();
}
[Test]
public void TestData()
{
var txtInput = new TextBoxTester("txtInput") {["Text"] = "2+2"};
var txtOutput = new TextBoxTester("txtOutput");
Assert.AreEqual("2+2", txtInput.Text);
var btnRes = new ButtonTester("btnRes");
btnRes.Click();
Assert.AreEqual("4", txtOutput.Text);
}
}
}
Second:
using System.Windows.Forms;
using NUnit.Extensions.Forms;
using NUnit.Framework;
using YAMP;
namespace Tests.GUITests
{
[TestFixture]
public class GuiTest : NUnitFormTest
{
private FrmMain _frm;
//[SetUp] // or it is still needed
public override void Setup()
{
base.Setup();
_frm = new FrmMain();
_frm.Show();
}
[TearDown]
public override void TearDown()
{
_frm.Close();
_frm.Dispose();
}
[Test]
public void TestData()
{
var txtInput = new TextBoxTester("txtInput") {["Text"] = "2+2"};
var txtOutput = new TextBoxTester("txtOutput");
Assert.AreEqual("2+2", txtInput.Text);
var btnRes = new ButtonTester("btnRes");
btnRes.Click();
Assert.AreEqual("4", txtOutput.Text);
}
}
}
And there are two different versions of the method TestNoData:
public void TestFormNoDataHandler()
{
var messageBoxTester = new MessageBoxTester("Message");
messageBoxTester.ClickOk();
}
[Test]
public void TestNoData()
{
ExpectModal("Message", TestFormNoDataHandler);
var txtInput = new TextBoxTester("txtInput") {["Text"] = string.Empty};
Assert.AreEqual(string.Empty, txtInput.Text);
var btnRes = new ButtonTester("btnRes");
btnRes.Click();
Assert.IsFalse(_frm.DialogResult == DialogResult.OK);
}
[Test]
public void TestNoData()
{
var txtInput = new TextBoxTester("txtInput") {["Text"] = string.Empty };
Assert.AreEqual(string.Empty, txtInput.Text);
var btnRes = new ButtonTester("btnRes");
btnRes.Click();
Assert.IsFalse(_frm.Enable);
}
Testable form is very simple. There are two TextBox - "txtInput", "txtOutput" and button - "btnRes". In "txtInput" introduced a mathematical expression, and "txtOutput" output response. The decision of expression occurs when you press "btnRes". If the field "txtInput" empty, the button is disabled and you can not click on it.
When searching for solutions to this problem came on the following links:
AutomaticChainsaw: WinForms testing using NUnitForms
c# - I need to create a windows form from within a NUnit test - Stack Overflow
Unfortunately I can attach only 2 links. But the information I learned is very different. Especially the part of writing methods Setup and TearDown.
In any case, I specify the version I use:
Visual Studio 2015 Community
NUnit - 2.6.4.14350
NUnitForms - 1.3.1771.29165
Because it seems to me that the problem might be too recent versions of frameworks, as article I learned quite old.
Thank you for any suggestion.
UseHidden Property: Tests are run on a separate hidden desktop. This makes them much faster and it works for any tests that are not using the keyboard or mouse controllers. They are less disruptive and input tests cannot interfere with other applications.
UseHidden property controls whether a separate desktop is used at all.
Though tests on the separate desktop are faster and safer (There is no danger of keyboard or mouse input going to separate running applications.), however for some operating systems or environments the separate desktop does not work. And the tests throw up errors like:
System.ComponentModel.Win32Exception : The requested resource is in use
--TearDown
at NUnit.Extensions.Forms.Desktop.Destroy()
at NUnit.Extensions.Forms.Desktop.Dispose()
at NUnit.Extensions.Forms.NUnitFormTest.Verify()
In that case you can override UseHidden property from test class and set it to return false. This will cause the tests to run on original, standard desktop.

Categories