Monday, 3 September 2018

Handling Authentication required dialog using Winium with C# for Chrome Browser (Best)


// Sending user name on Authentication required dialog

Your WiniumObject.FindElementsByName("Username").SendKeys(Your user name);

System.Windows.Forms.SendKeys.SendWait("{TAB}"); // Not mandatory

// Sending password on Authentication required dialog
Your WiniumObject.FindElementsByName("Password").SendKeys(Your password);

System.Windows.Forms.SendKeys.SendWait("{TAB}"); // Not mandatory

// Click on Sign in button

System.Windows.Forms.SendKeys.SendWait("{ENTER}"); or

 Your WiniumObject.FindElementsByName("Sign in").Click




ReUse Browser session using Selenium with Java


Attempt 1

ChromeDriver driver = new ChromeDriver();
HttpCommandExecutor executor = (HttpCommandExecutor) driver.getCommandExecutor();
URL url = executor.getAddressOfRemoteServer();
SessionId session_id = driver.getSessionId();

RemoteWebDriver driver2 = new RemoteWebDriver(executor, new DesiredCapabilities());

When we run the above code, it errors out with below exception

objc[52278]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_102.jdk/Contents/Home/bin/java (0x1040284c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_102.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x1040f04e0). One of the two will be used. Which one is undefined.
Starting ChromeDriver 2.27.440174 (e97a722caafc2d3a8b807ee115bfb307f7d2cfd9) on port 29572
Only local connections are allowed.
Jun 01, 2017 12:24:17 AM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: OSS
Exception in thread "main" org.openqa.selenium.SessionNotCreatedException: Session already exists
Build info: version: '3.4.0', revision: 'unknown', time: 'unknown'
Driver info: driver.version: RemoteWebDriver
    at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:138)
    at org.openqa.selenium.remote.service.DriverCommandExecutor.execute(DriverCommandExecutor.java:82)
    at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:637)
    at org.openqa.selenium.remote.RemoteWebDriver.startSession(RemoteWebDriver.java:250)
    at org.openqa.selenium.remote.RemoteWebDriver.startSession(RemoteWebDriver.java:236)
    at org.openqa.selenium.remote.RemoteWebDriver.<init>(RemoteWebDriver.java:137)
    at TestApp.main(TestApp.java:79)

Attempt 2

Next guess was to override “org.openqa.selenium.remote.HttpCommandExecutor.execute” and override the response for the newSession command

public static RemoteWebDriver createDriverFromSession(final SessionId sessionId, URL command_executor){
    CommandExecutor executor = new HttpCommandExecutor(command_executor) {

        @Override
        public Response execute(Command command) throws IOException {
            Response response = null;
            if (command.getName() == "newSession") {
                response = new Response();
                response.setSessionId(sessionId.toString());
                response.setStatus(0);
                response.setValue(Collections.<String, String>emptyMap());


            } else {
                response = super.execute(command);
            }
            return response;
        }
    };

    return new RemoteWebDriver(executor, new DesiredCapabilities());
}

public static void main(String [] args) {

    ChromeDriver driver = new ChromeDriver();
    HttpCommandExecutor executor = (HttpCommandExecutor) driver.getCommandExecutor();
    URL url = executor.getAddressOfRemoteServer();
    SessionId session_id = driver.getSessionId();


    RemoteWebDriver driver2 = createDriverFromSession(session_id, url);
}

The driver2 objects get created, which was a wow moment. Next thing was to test a simple command on the driver

driver2.get("http://google.com");

 exception

objc[53332]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_102.jdk/Contents/Home/bin/java (0x10ec5c4c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_102.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x10ed244e0). One of the two will be used. Which one is undefined.
Starting ChromeDriver 2.27.440174 (e97a722caafc2d3a8b807ee115bfb307f7d2cfd9) on port 5528
Only local connections are allowed.
Jun 01, 2017 12:41:02 AM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: OSS
Exception in thread "main" org.openqa.selenium.WebDriverException: No command or response codec has been defined. Unable to proceed
Build info: version: '3.4.0', revision: 'unknown', time: 'unknown'
Driver info: driver.version: RemoteWebDriver
    at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:154)
    at TestApp$1.execute(TestApp.java:54)
    at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:637)
    at org.openqa.selenium.remote.RemoteWebDriver.get(RemoteWebDriver.java:364)
    at TestApp.main(TestApp.java:74)

So I digged into Selenium source code for file HttpCommandExecutor.java and found the below lines of code

    if (NEW_SESSION.equals(command.getName())) {
      if (commandCodec != null) {
        throw new SessionNotCreatedException("Session already exists");
      }
      ProtocolHandshake handshake = new ProtocolHandshake();
      log(LogType.PROFILER, new HttpProfilerLogEntry(command.getName(), true));
      ProtocolHandshake.Result result = handshake.createSession(client, command);
      Dialect dialect = result.getDialect();
      commandCodec = dialect.getCommandCodec();
      for (Map.Entry<String, CommandInfo> entry : additionalCommands.entrySet()) {
        defineCommand(entry.getKey(), entry.getValue());
      }
      responseCodec = dialect.getResponseCodec();
      log(LogType.PROFILER, new HttpProfilerLogEntry(command.getName(), false));
      return result.createResponse();
    }

    if (commandCodec == null || responseCodec == null) {
      throw new WebDriverException(
        "No command or response codec has been defined. Unable to proceed");
    }

So we can see that when we override the newSession command, we just return a response. But we don’t set the commandCodec and responseCodec. Which then errors out at the next command we executed.

If we look at the definition of these two objects in the class

  private CommandCodec<HttpRequest> commandCodec;
  private ResponseCodec<HttpResponse> responseCodec;

They both are private field.
Attempt 3:

So in our overriden HttpCommandExecutor, we need to set the commandCodec and responseCodec variables.

I believe these changes were made for supporting both W3C and the old command and response formats. Since I am using the latest version, I will override these to the W3C objects required.

public static RemoteWebDriver createDriverFromSession(final SessionId sessionId, URL command_executor){
    CommandExecutor executor = new HttpCommandExecutor(command_executor) {

    @Override
    public Response execute(Command command) throws IOException {
        Response response = null;
        if (command.getName() == "newSession") {
            response = new Response();
            response.setSessionId(sessionId.toString());
            response.setStatus(0);
            response.setValue(Collections.<String, String>emptyMap());

            try {
                Field commandCodec = null;
                commandCodec = this.getClass().getSuperclass().getDeclaredField("commandCodec");
                commandCodec.setAccessible(true);
                commandCodec.set(this, new W3CHttpCommandCodec());

                Field responseCodec = null;
                responseCodec = this.getClass().getSuperclass().getDeclaredField("responseCodec");
                responseCodec.setAccessible(true);
                responseCodec.set(this, new W3CHttpResponseCodec());
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        } else {
            response = super.execute(command);
        }
        return response;
    }
    };

    return new RemoteWebDriver(executor, new DesiredCapabilities());
}

public static void main(String [] args) {

    ChromeDriver driver = new ChromeDriver();
    HttpCommandExecutor executor = (HttpCommandExecutor) driver.getCommandExecutor();
    URL url = executor.getAddressOfRemoteServer();
    SessionId session_id = driver.getSessionId();


    RemoteWebDriver driver2 = createDriverFromSession(session_id, url);
    driver2.get("http://www.google.com");
}

ReUsing existing Browser Session in Selenium with C#



var driver = new ChromeDriver();

Two things that we need is the Session Id and the Executor url to be able to re-create the driver.
Getting the Session Id

Getting the Session Id is quite simple:

              Console.WriteLine(driver.SessionId.ToString());

Getting the Executor URL:

                Getting the Executor URL is not straight forward. So we will see how to get it in multiple steps

If we look at the RemoteWebDriver class source code we will find the executor is stored in a private field

private ICommandExecutor executor;

To get a private field we need to use Reflection concepts in C#

var executorField = driver.GetType().GetField("executor", BindingFlags.NonPublic |BindingFlags.Instance);

The executorField value will come as null. The reason this happens is that we had initiated the driver as a ChromeDriver and the private field is of RemoteWebDriver which means it is not accesible to the ChromeDriver class also. So we need to go to it’s base class to fetch the executor field.

So we update our code as below

var executorField = driver.GetType().GetField("executor", BindingFlags.NonPublic |BindingFlags.Instance);
if (executorField == null )
{
    executorField = driver.GetType().BaseType.GetField("executor", BindingFlags.NonPublic |BindingFlags.Instance);
}

object executor = executorField.GetValue(driver);

Now if we look the executor object value. It is of type OpenQA.Selenium.Remote.DriverServiceCommandExecutor. If we look at the source code of this class

internal class DriverServiceCommandExecutor : ICommandExecutor
{
    private DriverService service;

    private HttpCommandExecutor internalExecutor;

This is a internal class, so we can’t cast this object. Also since the internalExecutor is a private field, we anyways have to use reflection further.

var internalExecutorField = executor.GetType().GetField("internalExecutor", BindingFlags.Instance | BindingFlags.NonPublic);
object internalExecutor = internalExecutorField.GetValue(executor);

The internalExecutor object has a type of OpenQA.Selenium.Remote.HttpCommandExecutor. The source code of the class is as below

internal class HttpCommandExecutor : ICommandExecutor
{
    private const string JsonMimeType = "application/json";

    private const string ContentTypeHeader = "application/json;charset=utf-8";

    private const string RequestAcceptHeader = "application/json, image/png";

    private Uri remoteServerUri;

So there is the field we are interested in remoteServerUri. We can get it the same way we got remoteServerUri.

var remoteServerUriField = internalExecutor.GetType().GetField("remoteServerUri", BindingFlags.Instance | BindingFlags.NonPublic);
var remoteServerUri = remoteServerUriField.GetValue(internalExecutor) as Uri;

The value of remoteServerUri comes out be http://localhost:52600/ in my case. This would change everytime we launch a new driver.

Here is a refactored and more polished version of the code we wrote

public static Uri GetExecutorURLFromDriver(OpenQA.Selenium.Remote.RemoteWebDriver driver)
{
    var executorField = typeof(OpenQA.Selenium.Remote.RemoteWebDriver)
        .GetField("executor",
                  System.Reflection.BindingFlags.NonPublic
                  | System.Reflection.BindingFlags.Instance);
   
    object executor = executorField.GetValue(driver);
   
    var internalExecutorField = executor.GetType()
        .GetField("internalExecutor",
                  System.Reflection.BindingFlags.NonPublic
                  | System.Reflection.BindingFlags.Instance);
    object internalExecutor = internalExecutorField.GetValue(executor);
   
    //executor.CommandInfoRepository
    var remoteServerUriField = internalExecutor.GetType()
        .GetField("remoteServerUri",
                  System.Reflection.BindingFlags.NonPublic
                  | System.Reflection.BindingFlags.Instance);
    var remoteServerUri = remoteServerUriField.GetValue(internalExecutor) as Uri;
   
    return remoteServerUri;
}

So now we have solved the first issue, which is to have the Session Id and Exeuctor URL for us to save before reconstructing the WebDriver next time.
Reconstructing the driver

So we have what we need to re-create the driver, now it is for us to look at what we need from a C# language perspective. We need to create a new class with base class OpenQA.Selenium.Remote.RemoteWebDriver class and override the execute method to change the NewSession command response

public class ReuseRemoteWebDriver: OpenQA.Selenium.Remote.RemoteWebDriver{
    private String _sessionId;
   
    public ReuseRemoteWebDriver(Uri remoteAddress, String sessionId)
        :base( remoteAddress,  new OpenQA.Selenium.Remote.DesiredCapabilities()) {
        this._sessionId = sessionId;
    }
   
    protected override OpenQA.Selenium.Remote.Response
        Execute(string driverCommandToExecute, System.Collections.Generic.Dictionary<string, object> parameters)
    {
        if (driverCommandToExecute == OpenQA.Selenium.Remote.DriverCommand.NewSession)
        {
            var resp =  new OpenQA.Selenium.Remote.Response();
            resp.Status = OpenQA.Selenium.WebDriverResult.Success;
            resp.SessionId = this._sessionId;
            resp.Value = new System.Collections.Generic.Dictionary<String, Object>();
            return resp;
        }
        var respBase = base.Execute(driverCommandToExecute, parameters);
        return respBase;
    }
}

This code creates a the driver successfully. Now let’s test it.

var driverReUse = new ReuseRemoteWebDriver(remoteUri, driverChrome.SessionId.ToString());
driverReUse.Url = "http://tarunlalwani.com";

Console.WriteLine(driverReUse.Url);

The line of code to set Url of the driver doesn’t do anything, but doesn’t error too. On printing the Url a exception is raised

OpenQA.Selenium.WebDriverException: no such session
  (Driver info: chromedriver=2.30.477700 (0057494ad8732195794a7b32078424f92a5fce41),platform=Windows NT 6.1.7601 SP1 x86_64)

   at OpenQA.Selenium.Remote.RemoteWebDriver.UnpackAndThrowOnError(Response errorResponse)
   at OpenQA.Selenium.Remote.RemoteWebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at SeleniumReuseSession.ReuseRemoteWebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters) in c:\Users\tarun\Documents\SharpDevelop Projects\SeleniumReuseSession\SeleniumReuseSession\Program.cs:line 40
   at OpenQA.Selenium.Remote.RemoteWebDriver.get_Url()
   at SeleniumReuseSession.Program.Main(String[] args) in c:\Users\tarun\Documents\SharpDevelop Projects\SeleniumReuseSession\SeleniumReuseSession\Program.cs:line 83

After debugging the code, I found the issue. The problem is that the base class constructor is called first and then the line this._sessionId = sessionId. The base constructor calls the Execute method. So when we set the sessionId in the dummy response, the ID is null as our constructor code has not been called yet.

Now this is the way constructor are suppose to work and there is no workaround to this behavior. So we need to dig into the constructor to find our workaround

The constructor calls the StartSession method which in turn executes the NewSession command and then save the session in sessionId private field.

//https://github.com/SeleniumHQ/selenium/blob/master/dotnet/src/webdriver/Remote/RemoteWebDriver.cs#L1100

protected void StartSession(ICapabilities desiredCapabilities)
{
    Dictionary<string, object> parameters = new Dictionary<string, object>();
    parameters.Add("desiredCapabilities", this.GetLegacyCapabilitiesDictionary(desiredCapabilities));

    Dictionary<string, object> firstMatchCapabilities = this.GetCapabilitiesDictionary(desiredCapabilities);

    List<object> firstMatchCapabilitiesList = new List<object>();
    firstMatchCapabilitiesList.Add(firstMatchCapabilities);

    Dictionary<string, object> specCompliantCapabilities = new Dictionary<string, object>();
    specCompliantCapabilities["firstMatch"] = firstMatchCapabilitiesList;
    parameters.Add("capabilities", specCompliantCapabilities);

    Response response = this.Execute(DriverCommand.NewSession, parameters);

    Dictionary<string, object> rawCapabilities = (Dictionary<string, object>)response.Value;
    DesiredCapabilities returnedCapabilities = new DesiredCapabilities(rawCapabilities);
    this.capabilities = returnedCapabilities;
    this.sessionId = new SessionId(response.SessionId);
}

So basically when our constructor gets called and the sessionId on our base class is set to null because of our overriden response. The fix is to set the sessionId in our constructor. So if update the constructor as below

public ReuseRemoteWebDriver(Uri remoteAddress, String sessionId)
    :base( remoteAddress,  new OpenQA.Selenium.Remote.DesiredCapabilities()) {
    this._sessionId = sessionId;
    var sessionIdBase = this.GetType()
        .BaseType
        .GetField("sessionId",
                  System.Reflection.BindingFlags.Instance |
                  System.Reflection.BindingFlags.NonPublic);
    sessionIdBase.SetValue(this, new OpenQA.Selenium.Remote.SessionId(sessionId));
}

The finally updated code for our class is as below

public class ReuseRemoteWebDriver: OpenQA.Selenium.Remote.RemoteWebDriver{
    private String _sessionId;
   
    public ReuseRemoteWebDriver(Uri remoteAddress, String sessionId)
        :base( remoteAddress,  new OpenQA.Selenium.Remote.DesiredCapabilities()) {
        this._sessionId = sessionId;
        var sessionIdBase = this.GetType()
            .BaseType
            .GetField("sessionId",
                      System.Reflection.BindingFlags.Instance |
                      System.Reflection.BindingFlags.NonPublic);
        sessionIdBase.SetValue(this, new OpenQA.Selenium.Remote.SessionId(sessionId));
    }
   
    protected override OpenQA.Selenium.Remote.Response
        Execute(string driverCommandToExecute, System.Collections.Generic.Dictionary<string, object> parameters)
    {
        if (driverCommandToExecute == OpenQA.Selenium.Remote.DriverCommand.NewSession)
        {
            var resp =  new OpenQA.Selenium.Remote.Response();
            resp.Status = OpenQA.Selenium.WebDriverResult.Success;
            resp.SessionId = this._sessionId;
            resp.Value = new System.Collections.Generic.Dictionary<String, Object>();
            return resp;
        }
        var respBase = base.Execute(driverCommandToExecute, parameters);
        return respBase;
    }
}

Wednesday, 30 May 2018


How to run Cucumber Feature file using Junit or TestNg



Run through Junit


@RunWith(Cucumber.class)
@CucumberOptions(
                  features = “src/test/Your features file“,
                  glues="src/test/Your Step Defnitio File"
                  tags ={“@SoapUI,@Functional"},...  )

public class TestRunnerClass
{
// You should keep empty

}

Run through TestNG

@CucumberOptions(
        format={"pretty","json:path/to/json_report.json"},
        features = "Path_to_Your_features_file",
        glue="Path_to_Your_stepDefinition file",
        tags={"@smoke,@regression")
        )

public class TestRunner extends AbstractTestNGCucumberTests

{
  //keep empty
}

Difference between Nunit and Xunit and MSTest

Attributes ( like Annotations in junit or TestNG)
NUnit 3.xMSTest 15.xxUnit.net 2.xComments
[Test][TestMethod][Fact]Marks a test method.
[TestFixture][TestClass]n/axUnit.net does not require an attribute for a test class; it looks for all test methods in all public (exported) classes in the assembly.
Assert.That
Record.Exception
[ExpectedException]Assert.Throws
Record.Exception
xUnit.net has done away with the ExpectedException attribute in favor of Assert.Throws. See Note 1
[SetUp][TestInitialize]ConstructorWe believe that use of [SetUp] is generally bad. However, you can implement a parameterless constructor as a direct replacement.
[TearDown][TestCleanup]IDisposable.DisposeWe believe that use of [TearDown] is generally bad. However, you can implement IDisposable.Disposeas a direct replacement.
[OneTimeSetUp][ClassInitialize]IClassFixture<T>To get per-class fixture setup, implement IClassFixture<T> on your test class.
[OneTimeTearDown][ClassCleanup]IClassFixture<T>To get per-class fixture teardown, implement IClassFixture<T> on your test class.
n/an/aICollectionFixture<T>To get per-collection fixture setup and teardown, implement ICollectionFixture<T> on your test collection.
[Ignore("reason")][Ignore][Fact(Skip="reason")]Set the Skip parameter on the [Fact] attribute to temporarily skip a test.
[Property][TestProperty][Trait]Set arbitrary metadata on a test
[Theory][DataSource][Theory]
[XxxData]
Theory (data-driven test).

Difference between Junit and TestNG

FeatureJUnit 4TestNG
test annotation@Test@Test
run before all tests in this suite have run@BeforeSuite
run after all tests in this suite have run@AfterSuite
run before the test@BeforeTest
run after the test@AfterTest
run before the first test method that belongs to any of these groups is invoked@BeforeGroups
run after the last test method that belongs to any of these groups is invoked@AfterGroups
run before the first test method in the current class is invoked@BeforeClass@BeforeClass
run after all the test methods in the current class have been run@AfterClass@AfterClass
run before each test method@Before@BeforeMethod
run after each test method@After@AfterMethod
ignore test@ignore@Test(enbale=false)
expected exception@Test(expected = ArithmeticException.class)@Test(expectedExceptions = ArithmeticException.class)
timeout@Test(timeout = 1000)@Test(timeout = 1000)

Selenium, C# with SpecFlow Setup requirement

1. Selenium WebDriver

Site : http://selenium-release.storage.googleapis.com/index.html?path=3.12/

File Name : selenium-dotnet-3.12.1.zip


2. Visual Studio

3. Nunit
       Go with Nunit 3 as it supports Parallel execution

      Site: http://nunit.org/download/

4. C# ( will be supported by Visual Studio by default)
5. Reporting ( NuGet Gallery | NUnitOrange 2.1.0)

It will covert TestResults.xml to Test Results.html

6. Building Tool ( MSbuild) - it is coming by default with OS
7. Jenkins ( https://jenkins.io)
8. Object Identification ( You can use Xpath and CSS, Please refer my previous post)
9. SpecFlow (BDD)
               Site : https://github.com/techtalk/SpecFlow/wiki/Advanced-Project-Setup