I am unable to find a single post where i can automate mobile testing with appium in C#.
I have written my Website automation code in the specflow. Can I also Reuse it ?
Appium provides the dotnet-appium-driver which is your API to interface with Appium. You can use that to write your app automation.
You did not provide any example here nor code, so I cannot really act on something to show you. I will just write down some C# code to let you understand how a simple test in C# can be written:
namespace AppiumTests
{
using System;
// .NET unit test namespaces needed here as well, just not mentioning them
using OpenQA.Selenium; /* Appium is based on Selenium, we need to include it */
using OpenQA.Selenium.Appium; /* This is Appium */
[TestClass]
public class TestSuite
{
private AppiumDriver driver;
private static Uri testServerAddress = new Uri("http:127.0.01:4723/wd/hub"); // If Appium is running locally
private static TimeSpan INIT_TIMEOUT_SEC = TimeSpan.FromSeconds(180); /* Change this to a more reasonable value */
private static TimeSpan IMPLICIT_TIMEOUT_SEC = TimeSpan.FromSeconds(10); /* Change this to a more reasonable value */
[TestInitialize]
public void BeforeAll()
{
DesiredCapabilities testCapabilities = new DesiredCapabilities();
testCapabilities.App = "<your-app-file>";
testCapabilities.AutoWebView = true;
testCapabilities.AutomationName = "";
testCapabilities.BrowserName = String.Empty; // Leave empty otherwise you test on browsers
testCapabilities.DeviceName = "Needed if testing on IOS on a specific device. This will be the UDID";
testCapabilities.FwkVersion = "1.0"; // Not really needed
testCapabilities.Platform = TestCapabilities.DevicePlatform.Android; // Or IOS
testCapabilities.PlatformVersion = String.Empty; // Not really needed
driver = new AppiumDriver(testServerAddress, capabilities, INIT_TIMEOUT_SEC);
driver.Manage().Timeouts().ImplicitlyWait(IMPLICIT_TIMEOUT_SEC);
}
[TestCleanup]
public void AfterAll()
{
driver.Quit(); // Always quit, if you don't, next test session will fail
}
///
/// Just a simple test to heck out Appium environment.
///
[TestMethod]
public void CheckTestEnvironment()
{
var context = driver.GetContext();
Assert.IsNotNull(context);
}
}
}
You can find more in this article I wrote.
Finally reached to the solution run a test in C#. Many Thanks to Andry.
This solution runs the website in the chrome browser of your phone connected to the computer :
Steps and short program to setup and run a C# program on an android device using Appium:
namespace poc
{
using NUnit.Framework;
using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Interfaces;
using OpenQA.Selenium.Appium.MultiTouch;
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Appium.Android;
[TestFixture()]
public class TestAppium
{
public IWebDriver driver;
[TestFixtureSetUp]
public void SetUp()
{
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.SetCapability("device", "Android");
capabilities.SetCapability("browserName", "chrome");
capabilities.SetCapability("deviceName", "Motorola Moto g");
capabilities.SetCapability("platformName", "Android");
capabilities.SetCapability("platformVersion", "5.0.2");
driver = new RemoteWebDriver(new Uri("http://127.0.0.1:4723/wd/hub"), capabilities, TimeSpan.FromSeconds(180));
}
[Test()]
public void OpenHofHomePage()
{
driver.Navigate().GoToUrl("http://YourWebsiteToTest.com");
Assert.IsTrue(driver.Title.Equals("Your Website")," Sorry , the website didnt open!!");
}
[TestFixtureTearDown]
public void End()
{
driver.Dispose();
}
}
}
1) Set up usual project in C# , install Appium , Selenium using NuGet package manager , Also install Nunit with the same process.
2) Download Android SDK
3) Envrionment variables : Add variable name "ANDROID_HOME" and in variable give path to sdk folder , In PATH (found in System variable) , append path to tools in sdk folder and append path to platform tools.
4) Connect your device (a.mobile device's drivers should be installed in the computer (my case moto g adb drivers installed) b. device should have developer mode option ON and debugger option checked and always awake option checked)
5) Now download Appium and open the Appium.exe.
6) Appium window -> In Android setting (first button) , check on "Use Browser" option and select "Browser" as an option.
7) Start the appium node server (play button at the top).
8) now run the test from the visual studio and you will see website opening in the browser of phone.
To make this more comprehensive, I have written a blog post which explains all the steps clearly with images. Its a step by step tutorial to use appium with c# and MSTest
You can read it here.
http://www.binaryclips.com/2016/03/test-automation-on-android-using-appium.html
This is how I have implemented Appium. I think it adds some depth to the discussion.
Appium is both a WebDriver that can interact with a device in your project as well as a Server that bridges your project to your physical device or emulator.
You have to run the Appium server as a listener and set up your Capabilities to connect to it. That will start the app and execute your test.
There is a lot more to it than this but here is a setup that you can try to get you on your way:
In a WebDriver Support Class use the [BeforeScenario] tag to execute this code before your test. This has a Sauce Labs implementation.
[BeforeScenario]
public void BeforeScenario()
{
var name = Locale == "sauce" ? "AppiumSauceDriver" : "AppiumDriver";
InitializeServiceLocator();
if (WebDriver == null)
{
InitializeWebDriver(name);
}
ObjectContainer.RegisterInstanceAs(WebDriver);
if (WebDriver != null)
{
Console.WriteLine("Driver Already Exists");
}
}
Then you need to populate the InitializeServiceLocator() method. There is a Device Selector Method here that doesn't do anything other than set the name. You can just hard code it to your capabilities instead.
private static void InitializeServiceLocator()
{
if (ServiceLocator == null)
{
var ip = "";
var deviceType = TestSetupHelper.DeviceSelector["iphone7"];
var capabilities = TestSetupHelper.SetAppiumCababilities(deviceType, Locale);
string server = MSTestContextSupport.GetRunParameter("appiumServer");
var port = Convert.ToInt32(MSTestContextSupport.GetRunParameter("appiumPort"));
if (Locale != "sauce")
{
ip = TestSetupHelper.GetComputerIpAddress(server, port, true);
}
var kernel = new StandardKernel();
kernel.Bind<IOSDriver<AppiumWebElement>>().ToConstructor(x => new IOSDriver<AppiumWebElement>(new Uri("http://" + ip + ":" + port + "/wd/hub"), capabilities, TimeSpan.FromMinutes(10))).Named("AppiumDriver");
kernel.Bind<IOSDriver<AppiumWebElement>>().ToConstructor(x => new IOSDriver<AppiumWebElement>(new Uri("http://ondemand.saucelabs.com:80/wd/hub"), capabilities, TimeSpan.FromMinutes(10))).Named("AppiumSauceDriver");
ServiceLocator = new NinjectServiceLocator(kernel);
Microsoft.Practices.ServiceLocation.ServiceLocator.SetLocatorProvider(() => ServiceLocator);
}
}
From here you need to set up your capabilities. The below method is how I have done it.
public static DesiredCapabilities SetAppiumCababilities(string deviceType, string locale, bool realDevice = false, string udid = "FAKEUDIDFOREXAMPLE")
{
string appiumAppFilePath = MSTestContextSupport.GetRunParameter("appiumAppFilePath");
//Universal Capabilities
capabilities.SetCapability("platformName", "iOS");
capabilities.SetCapability("deviceName", deviceType);
capabilities.SetCapability("automationName", "XCUITest");
capabilities.SetCapability("app", appiumAppFilePath);
capabilities.SetCapability("xcodeOrgId", "GET_THIS_FROM_APP_DEV");
capabilities.SetCapability("xcodeSigningId", "iPhone Developer");
capabilities.SetCapability("noReset", true);
capabilities.SetCapability("newCommandTimeout", 80);
if (locale == "sauce")//Sauce specific capabilities
{
capabilities.SetCapability("appiumVersion", "1.7.2");
capabilities.SetCapability("name", MSTestContextSupport.GetRunParameter("appiumJobName"));
capabilities.SetCapability("username", MSTestContextSupport.GetRunParameter("sauceId"));
capabilities.SetCapability("accessKey", MSTestContextSupport.GetRunParameter("sauceKey"));
capabilities.SetCapability("tunnel-identifier", MSTestContextSupport.GetRunParameter("sauceTunnel"));
capabilities.SetCapability("browserName", "");
capabilities.SetCapability("platformVersion", "11.2");
}
else//Local specific capabilities
{
capabilities.SetCapability("platformVersion", "11.3");
}
if (realDevice)//Sauce real device testing will not need this. This is for Local Real Device testing only
{
capabilities.SetCapability("udid", udid);
}
return capabilities;
}
Anything referencing GetRunParameter is getting a value from a .runsettings file.
Example:
<TestRunParameters>
<Parameter name="appiumJobName" value="OBI App Automated Test"/>
</TestRunParameters>
You have to add all of the stuff to the .runsettings file you are using to run the project that you want to source from there. You can hardcode it if you don't want to go through the runsettings file but I prefer to do it that way. You can make things variable that way based on the runsettings file you have selected.
Then you have to define TestSetupHelper.GetComputerIpAddress(server, port, true); If you have a hard coded IP Address to the computer that is running your Appium server you can just hard code that. Otherwise you need a Helper class somewhere (In this example it is TestSetupHelper). This example initiates a Socket Connection to the server and returns all of the details. That way you don't have to worry about IP addresses changing. You just pass the host name and the port you have Appium configured to run on and you can get the connection information from that connection and make your actual connection to the Appium Server instance.
public static string GetComputerIpAddress(string computerName, int port, bool remote)
{
var ip = "";
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
{
socket.Connect(computerName, port);
//If remote it gets the connection destination IP, else it gets the connection initiator IP
IPEndPoint endPoint = remote ? socket.RemoteEndPoint as IPEndPoint : socket.LocalEndPoint as IPEndPoint;
ip = endPoint.Address.ToString();
}
return ip;
}
This isn't going to get you completely there but it will get you close to a working environment. Then you can start working on getting a test to actually run.
I hope this helps.
Related
I'm new to programming and I have worked in Python Selenium for web automation in the past. Now, I'm using Appium Windows driver to automate Desktop UI using C#. Here is the sample code which I trying to work with,
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Remote;
using System;
namespace CalculatorTest
{
public class CalculatorSession
{
// Note: append /wd/hub to the URL if you're directing the test at Appium
private const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";
private const string CalculatorAppId = "Microsoft.WindowsCalculator_8wekyb3d8bbwe!App";
protected static WindowsDriver<WindowsElement> session;
public static void Setup(TestContext context)
{
// Launch Calculator application if it is not yet launched
if (session == null)
{
// Create a new session to bring up an instance of the Calculator application
// Note: Multiple calculator windows (instances) share the same process Id
DesiredCapabilities appCapabilities = new DesiredCapabilities();
appCapabilities.SetCapability("app", CalculatorAppId);
appCapabilities.SetCapability("deviceName", "WindowsPC");
session = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), appCapabilities);
Assert.IsNotNull(session);
// Set implicit timeout to 1.5 seconds to make element search to retry every 500 ms for at most three times
session.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1.5);
}
}
public static void TearDown()
{
// Close the application and delete the session
if (session != null)
{
session.Quit();
session = null;
}
}
}
}
I'm getting an error says "DesiredCapabilities is inaccessible due to its protection level". Can you please tell me how I can overcome this error?
Now I ran into the same thing. Downgrading Selenium.WebDriver helped. Decreased to 3.10
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Remote;
using System;
using System.Threading;
using Xunit;
namespace XUnitTestProject2
{
public class UnitTest1
{
[Fact]
public void Test1()
{
Thread.Sleep(3000);
var driver = InitiliseDriver();
//Login Page
driver.FindElement(By.Id("username")).SendKeys("TestUser");
driver.FindElement(MobileBy.Id("password")).SendKeys("newpassword");
driver.FindElement(MobileBy.Id("91268f5d-d21e-4ef5-9886-c1d19f2799a7")).Click();
}
private RemoteWebDriver InitiliseDriver()
{
//Set the capabilities
DesiredCapabilities cap = new DesiredCapabilities();
cap.SetCapability("platformName", "Android");
cap.SetCapability("platformVersion", "9.0");
cap.SetCapability("app", "C:\\Users\\jamesa\\Downloads\\app-debug.apk");
//cap.SetCapability("automationName", "uiautomator2");
return new RemoteWebDriver(new Uri("http://127.0.0.1:4723/wd/hub"), cap);
}
}
}
My nuget packages are Appium.WebDriver(4.1.1); Microsoft.Net.Test.Sdk(16.0.1); Selenium.Support(3.141.0); Selenium.WebDriver(3.141.0); Xunit(2.4.0); Xunit.runner.visualstudios(2.4.0); MSTest.TestAdapter(2.1.2); MSTest.TestFramework(2.1.2)
My Desired Capabilities are platformName- Text- Android; platformVersion- Text - 9.0; app - Text - \\\
This is the session details I am getting I do have selector ID but the server is not picking it
Note: - I am able to launch the application successfully but the server is unable to pick the element. - The application is built in an Ionic way (Ionic 5)
You need to set your driver context to WEBVIEW, ionic builds apps with webview (not native), also you might want to build your app in debug mode. Here is more details on testing hybrid applications with appium.
In your case you would need to add something like this, before executing the tests
driver.context("WEBVIEW");
I'm implemented Nunit selenium C# testing in visual studio (Console Application n Class Library). My project in visual studio is console application. I started the selenium grid using
java -Dwebdriver.gecko.driver="..\jar\geckodriver.exe" -Dwebdriver.chrome.driver="..\jar\chromedriver.exe" -Dwebdriver.ie.driver="..\jar\IEDriverServer.exe" -jar ..\jar\selenium-server-standalone-3.14.0.jar -role hub -port 4444
Code:
using Automation_Framework.Manager;
using NUnit.Framework;
using OpenQA.Selenium;
using System;
using System.Collections.Generic;
using System.Text;
namespace Automation_Framework.TestManager
{
[TestFixture]
class ChromeTestManager
{
private WebDriverManager webDriverManager;
private IWebDriver driver;
public ChromeTestManager()
{
webDriverManager = new WebDriverManager();
}
[SetUp]
public void setup()
{
webDriverManager.createDriver("chrome");
driver = webDriverManager.getDriver();
}
[Test]
public void test()
{
driver.Url = "http://www.google.com.my";
driver.Navigate();
}
[TearDown]
public void shutdown()
{
driver.Close();
}
}
}
I had tried execute using Test Explorer but it does not open any browser. I"m following this tutorial.
Questions:
How to run the project with browser open and see all actions?
How to run using Nunit-console-runner.
Please help me. Thanks.
I assume that:
1. You have tried your code locally and your test is opening the browser when you run it on your machine without the grid.
2. Your nodes are set up and registered with the hub.
You need to:
1. Use RemoteWebDriver:
var uri = 'uri_to_your_grid_hub';
var capabilities = new ChromeOptions().ToCapabilities();
var commandTimeout = TimeSpan.FromMinutes(5);
var driver = new RemoteWebDriver(new Uri(uri),capabilities,commandTimeout)
Add the attribute to a class: [Parallelizable(ParallelScope.Self)] in order to run your tests in parallel with other test classes.
In order to verify whether the hub is running, open the browser and navigate to http://localhost:4444 on the hub machine.
Sources:
How can I run NUnit(Selenium Grid) tests in parallel?
Selenium Grid in C#
Useful C# WebDriver examples
Selenium Grid set up
I haven't used grid in .Net but here my answer:
your command is just register a hub, which needs to keep running (open a browser and test it is working)
you need to register your nodes under that hub (different ports) (open a browser and test it is working)
in your code, you should use "RemoteWebDriver" to connect to the hub.
something along these lines (it is in java but I hope it gives you a starting point)
public class Gmail
{
public WebDriver driver=null;
#Parameters("browser") //testng.xml
#Test()
public void GmailTest(String browser)
{
System.out.println("Gmail " + browser);
// RemoteWebdriver
DesiredCapabilities cap = null;
if(browser.equals("firefox")){
cap = DesiredCapabilities.firefox();
cap.setBrowserName("firefox");
cap.setPlatform(Platform.ANY);
}else if (browser.equals("iexplore")){
cap = DesiredCapabilities.internetExplorer();
cap.setBrowserName("iexplore");
cap.setPlatform(Platform.WINDOWS);
}
driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"),cap);
driver.get("http://gmail.com");
driver.findElement(By.id("Email")).sendKeys("abcd");
driver.quit();
}
I hope this helps.good luck
I'm trying to pass messages with NetMQ in C# UWP to python.
The python acts as Subscriber, and the C# as Publisher.
When I use C# .Net Core, I can see messages get to the python subscriber, but when I use C# UWP, nothing happens, though the code is exactly the same and I can see Publisher is sending the messages.
The code in python: (Working)
import zmq
import time
def subscribe():
port = "6789"
context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.connect("tcp://localhost:%s" % port)
topicfilter = "abcde"
socket.setsockopt(zmq.SUBSCRIBE, topicfilter)
while True:
string = socket.recv()
print string
subscribe()
The code in .Net Core: (Working)
using System.Threading;
using System.Threading.Tasks;
using NetMQ;
using NetMQ.Sockets;
namespace Examples
{
static partial class Program
{
public static void Main(string[] args)
{
Publisher();
}
public static void Publisher()
{
Task.Run(async () =>
{
using (var pubSocket = new PublisherSocket())
{
pubSocket.Bind("tcp://*:6789");
for (var i = 0; i < 10; i++)
{
pubSocket.SendFrame("abcde" + i.ToString());
Thread.Sleep(1000);
}
}
});
}
}
}
But the code in UWP (Not working):
using NetMQ;
using NetMQ.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using System;
namespace test_NetMQ_UWP
{
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
DataContext = this;
}
// this event happen when I click on a button in MainPage.xaml
private void Publisher_Click(object sender, RoutedEventArgs e)
{
Task.Run(async () =>
{
using (var pubSocket = new PublisherSocket())
{
pubSocket.Bind("tcp://*:6789");
for (var i = 0; i < 10; i++)
{
pubSocket.SendFrame("abcde" + i.ToString());
Thread.Sleep(1000);
}
}
});
}
}
}
What am I doing wrong?
It's normal behavior. You're using an IP loopback address for Network communications between a UWP app and a different process (a different UWP app or a desktop app). This is restricted by network isolation.
You could run your server and client on different machine to test. Please see the document How to enable loopback and troubleshoot network isolation (Windows Runtime apps). It has explained this scenario:
Loopback is permitted only for development purposes. Usage by a Windows Runtime app installed outside of Visual Studio is not permitted. Further, a Windows Runtime app can use an IP loopback only as the target address for a client network request. So a Windows Runtime app that uses a DatagramSocket or StreamSocketListener to listen on an IP loopback address is prevented from receiving any incoming packets.
In your case, if you just want to test if the UWP app can send message to your python subscriber successfully. You could run the UWP app on another machine. I used your code to make a UWP app to send message and make a console application as subscriber which is run on a different machine. The console application can receive the message.
Please note that because your UWP app need to access the Network at runtime, you need to enable the Netwrok capabilities(Internet(Client) Internet(Client & Server) Private Networks(Client & Server)) in Package.appxmanifest file.
I cannnot subscribe to any topics using RosSharp. I am working entirely locally-on the same machine. I am fairly certain I have the right uri with the right port and cannot get any communication. I am running ROS through Win-ROS: https://ros-win.visualstudio.com/_git/ros-win?path=%2Fdoc%2FSetup.md&version=GBmaster
On my local Windows host machine, I am running roscore and publishing a node with:
rostopic pub -r 10 /testtopic std_msgs/String "whatever".
I'm able to run a subscriber from the command prompt with rostopic echo /testtopic and receive the correct data back, but I cannot get this subscriber to work in Unity. I'm expecting SubscriptionHandler to at least be called. This is the code I'm trying to replicate: https://github.com/siemens/ros-sharp/blob/a45e847f96d7dd1a7859dd9595e05ef27b27b5c3/Libraries/RosBridgeClientTest/RosSocketTests.cs
Below is my code attached to a GameObject, any help would be appreciated. Thank you.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RosSharp.RosBridgeClient;
using std_msgs = RosSharp.RosBridgeClient.Messages.Standard;
public class BasicROS : MonoBehaviour {
public string uri = "ws://18.40.26.172:11311";
private RosSocket rosSocket;
string subscriptionId = "";
void Start () {
rosSocket = new RosSocket(new
RosSharp.RosBridgeClient.Protocols.WebSocketNetProtocol(uri)); // 10.189.42.225:9090
Subscribe("/testtopic");
}
public void Subscribe(string id)
{
subscriptionId = rosSocket.Subscribe<std_msgs.String>(id, SubscriptionHandler);
StartCoroutine(WaitForKey());
}
private IEnumerator WaitForKey()
{
Debug.Log("Press any key to close...");
while (!Input.anyKeyDown)
{
yield return null;
}
Debug.Log("Closed");
// rosSocket.Close();
}
private void SubscriptionHandler(std_msgs.String message)
{
Debug.Log("Message received!");
Debug.Log(message.data);
}
}
At the time of this writing, ros-win doesn't support rosbridge, which is the ros-websocket bridge that rosSharp can communicate with. If you must use ros-win and use RosSharp to do websocket communication in unity, then you will need to wait or write your own port of rosbridge.
If you can replace ros-win with ros on WSL, then you can do the following:
Install the ros-websocket bridge suite. (Note: desktop-full does not include this, so be sure you actually get this package)
sudo apt-get install ros-<rosdistro>-rosbridge-suite
e.g.,
sudo apt-get install ros-melodic-rosbridge-suite
Run it with roslaunch rosbridge_server rosbridge_websocket.launch in order for web socket messages to reach the ros network.
Doing that will create a websocket listener on port 9090, so you need to change your uri to use port 9090.
Also, you may want to use localhost, i.e., uri = "ws://localhost:9090";, to avoid routing issues.