Showing posts with label c#. Show all posts
Showing posts with label c#. Show all posts

Saturday, June 6, 2020

Test patterns The "Test Data Faker" instead of "Test Data Builder"

Many engineers have seen the benefits of using builders in there Unit tests. You can find blog are articles expounding on their benefits. My personal favorite is the articles in From Test Data Builders to the identity functor by Mark Seemann. (I think if you read each one of marks articles you'll be a better person)

My thoughts on Builders
 The benefits of a builder pattern are obvious:  you'll write less code, your code will be more readable, easier to refactor and maintain, etc...  Here's two tests, they test adding a user with an Android smartphone, both tests use the exact same data every time they execute. 
Constructor InitializationTest Data Builder Pattern
[Test]
public void ContrustorTest()
{
    // arange 
    var user = new User(
        "Kimberly",
        "Kim",
        "k.kim@at.com",
        new Device(
            "Android",
            "1"),
        new Address(
            "123 Sesame Street",
            "Garbage Can",
            "Manhattan",
            "NY",
            "12345"));
    // act
    var service = new UserService();
    var userResponse = service.AddUser(user);
    // assert
    Assert.NotNull(userResponse.Id);
}
[Test]
public void BuilderPatternTest()
{
    // arange 
    var user = new UserBuilder()
        .With(new DeviceBuilder()
            .WithOs("Android")
            .Build())
        .Build();
    // act
    var service = new UserService();
    var userResponse = service.AddUser(user);
    // assert
    Assert.NotNull(userResponse.Id);
}
This pattern works great for Unit testing but does this pattern work well for Functional testing? In short, ya no ya, but it could be better. 
The main problem I see when using this pattern in Functional (or System) testing is that the data isn't real and it never changes. So after 20k tests have executed there will be 20k users with the first name "Kimberly"

A Fake Builder
These two tests are the same, they test adding a user with an Android smartphone, but the second test doesn't use the same data every time it executes.   
Test Data Builder Pattern Test Data Faker Pattern
[Test]
public void BuilderPatternTest()
{
    // arange 
    var user = new UserBuilder()
        .With(new DeviceBuilder()
            .WithOs("Android")
            .Build())
        .Build();
    // act
    var service = new UserService();
    var userResponse = service.AddUser(user);
    // assert
    Assert.NotNull(userResponse.Id);
}
[Test]
public void FakerPatternTest()
{
    // arange 
    var user = new UserFaker()
        .With(new DeviceFaker()
            .WithOs("Android")
            .Fake())
        .Fake();
    // act
    var service = new UserService();
    var userResponse = service.AddUser(user);
    // assert
    Assert.NotNull(userResponse.Id);
}
Now some will say these two test are not the same because they will use different data, the test using the faked data will have a different FirstName each time you run it and the one with the builder will always have Kimberly as the FirstName. You have to ask yourself what's the value in having 20k identical users? How often will the production environment have 20k identical users?

So whats the Differnce
In a Builder below we define the default values in the constructor making sure every time we create a new instance it has the same values (20k Kimberly's in the db). In the Faker below is using Bogus to fake the users data, each time we run this we can get unique human readable meaningful data.
 UserBuilder.csUserFaker.cs
public class UserBuilder : User
{
    public UserBuilder()
    {
        FirstName = "Kimberly";
        LastName = "Kim";
        Email = "K.Kim@earthlink.net";
        Device = new DeviceBuilder();
        Address = new AddressBuilder();
    }

    public UserBuilder WithName(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
        return this;
    }

    public UserBuilder WithEmail(string email)
    {
        Email = email;
        return this;
    }

    public UserBuilder With(Device device)
    {
        Device = device;
        return this;
    }

    public UserBuilder With(Address address)
    {
        Address = address;
        return this;
    }

    public User Build()
    {
        return (User)this;
    }
}
public class UserFaker : User
{
    public UserFaker()
    {
        var person = new Bogus.Person();
        FirstName = person.FirstName;
        LastName = person.LastName;
        Email = person.Email;
        Device = new DeviceFaker();
        Address = new AddressFaker();
    }

    public UserFaker WithName(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
        return this;
    }

    public UserFaker WithEmail(string email)
    {
        Email = email;
        return this;
    }

    public UserFaker With(Device device)
    {
        Device = device;
        return this;
    }

    public UserFaker With(Address address)
    {
        Address = address;
        return this;
    }

    public User Fake()
    {
        return (User)this;
    }
}
So one test will create 20k Android users that are all identical in every way, the other test will create 20k Android users that have randomized addresses, names and emails, etc. 

The Test Data Faker Pattern is basically just the Test Data Builder Pattern but except in the constructor instead of defining rigid values that never change an faking library is used. In this case faking library is called Bogus, in the above example a random "Person" is generated and the values are assigned to the "User" object, this has the added benefit of having the first and last names in the email. 

I think The Test Data Builder Pattern s perfect for Unit testing, but for Functional testing I prefer the Test Data Faker Pattern.  There is value real value in not deleting test data after execution is complete when doing Functional testing, and there is real value in having unique real world random data when testing any system. What if we need to test: pagination, search/queries, sorting, etc... 

The Test Data Faker Pattern ensures over time you'll expose potential extraneous conditions found normally: by users, during test fuzzing, in exploratory testing, in load testing, etc... Eventually your DB will have so much data you be able to find issues developers say could never happen in production (make sure to get that in writing, or place a bet with that savvy developer)  

In the "real" world
I personally don't met engineers in person that uses this pattern often, because most of the engineers I've worked with either don't test their code at all or only do a minimal amount of testing and know no testing patterns. 

I think the natural path most developers take is to just use Constructor Initialization and after they write 20 tests they notice that 10 of the test use the same object so they switch to the Object Mother pattern, now 30 days later they have a Object Mother class with 200 methods for creating a object, and if they have been extremely delegante none of the methods create duplicate object. At some point they hier a new developer and that dev said this is not maintainable we should fix this, but know one wants to break X many tests. So the option is to memorize 200 user definitions and add a new definition, this is how 200 methods quickly become 400 methods. I think this happens because people are lazy and do what's easiest in the moment and that's why they will make these typical mistakes, but the really good developers are extremely lazy, and they plan and design with good patterns so they can write twice the code in half the time like me.

In the past I would usually build my own Faker for generating test data, I find it useful to have entities in the SUT (System Under Test) data base that have realistic data like: names, address, phone numbers, account numbers, etc. Over time the system will have rich data that can be used for performance testing features that require lots of unique data like search. I no longer will build my own Faker as someone has done it better and it's a lot of work at each new gig to build and I never pu it in my open source, I use the the Bogus. Below is the UserBuilder object. In the above example the Device OS is set to Android and then then Fake() is called, any data that is null will be faked using the Bogus Faker and a User object is returned.

Imaginary Q&A:
Q: I'm really good at Unit testing, and a Unit test should only test one thing, that's the rule bro?
A: I don't care go away (PS: I don't do Unit tests, I do Functional (or System) test) 

Q: But I alway do as I'm told, and everyone always knows you have to delete all data at the end of every test or you'll die and never go to heaven?
A: That wasn't a question it was a statement, and your Unit testing dogma can't defeat me. 

Q: My mom made me wear a helmet to get the mail, and my dad never hugged me!
A: Ya You sound like you'd be great at Unit testing buddy.

Q: You said those two tests at the top are the same but they aren't because... bla bla bla
A: Sorry to interrupt you, but skip to the end, I don't care go away

If you have a problem with anything I have said, please keep in mind that I never wanted you to read this, you have violated my privacy. I wrote this for friends of the cause, someone just like you but with better hair, you have wasted your time and disappointed me and your mother. And please do something about that hair! I don't recommend this pattern for Unit testing, if you think this pattern should not be used for Unit testing please keep it to yourself!  And you can sleep well in the knowledge that I both agree and don't care, at the same time. Cheers!

Soy el rey de las pruebas funcionales

Tuesday, July 15, 2014

Part 2 Troubleshooting WebDriver StaleElementReferenceException

StaleElementReferenceException can be a misleading error and people can have a hard time filtering out the actual root cause. The StaleElementReferenceException means the element has been removed or changed in between the time it was referenced and the time it was used.

Let’s walk through some code that might result in a StaleElementReferenceException

  1) We create a variable initialized to a text box
var text1 = driver.FindElement(By.Id("text1"));
  2) Lets imagine some javascript removed it from DOM and then adding it back
  3) Finally the call to SendKeys() will throw "StaleElementReferenceException"
text1.SendKeys("StaleElementReferenceException");

I often see this error on SelectElement's
For example:
A form with dependent SelectElement's: a SelectElement for State that depending on it value populates a SelectElement for City.
When you populate the State SelectElement, the data is loaded into the City SelectElement by removing it from DOM and then adding it back. Lots of apps have these dependent SelectElement's
You can create a variable for the City SelectElement before populating the State but theres might be a race condition if you try t populate it right after populating the State.

Here is a link to the Selenium2 Webdriver documentation on StaleElementReferenceException.

Links to Troubleshooting:
Part 0 Troubleshooting WebDriver issues are easy and so can you!
Part 1 Troubleshooting WebDriver NoSuchElementException
Part 2 Troubleshooting WebDriver StaleElementReferenceException

Monday, May 5, 2014

Part 1 Troubleshooting WebDriver NoSuchElementException

NoSuchElementException seems obvious but it can be a misleading error and people can have a hard time filtering out the actual root cause. The NoSuchElementException means the specific element did not exist at the time the call was made plus the duration of the implicit wait.

Let’s say the following code results in a NoSuchElementException

driver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0, 0, 10)); //10 seconds
driver.Navigate().GoToUrl(url);
driver.FindElement(By.Id("text1"));


There are many reasons this might happen, lets walk over some of the common reasons:
There is no web element with an ID of "text1"
What to look for, the element may have been removed or the ID may have been removed or changed.
  • Element doesn't exist, navigate to the URL and view the source code
    • Verify that the web element exists
    • Verify that the ID = “text1”

The element did not load within 10 seconds
What to look for, the element may have been loaded after the 10 seconds
  • Never loads in less than 10 seconds, navigate to the URL and see how long it takes for the element to appear on the page    
    • Verify that it happens in less than 10 seconds, each time you go to a page the time could be different so doing this multiple times could help provide insight. My general rule is if it should take 4 seconds expect it to take 8 seconds and wait as much as 12 seconds. (Should take X,  Expected X*2, Wait X*3)
  • Intermittently loads in less than 10 seconds, Consider each Runtime Context separately, simple example: It works on my laptop but not on build machine   
    • Verify hardware, how long it takes on each machine: laptop 3 seconds, build machine 12 seconds. If this were true you might expect other operations have the same ratio.
    • Verify time of day and day of week, what time are the failures. If fails every Saturday 5:00am, what else is happening at that time, DB back up, system updates, virus scans, etc…

The element could be inside an iframe.
What to look for, the element might not be part of the parent html page it may be contained inside of an iframe.
  • The web element is present but not in the pages DOM
    • Verify if the web element is a dependent of an iframe
So these were just some of the things that could have caused this error, there are a host of other reasons the NoSuchElementException would be raised. I think the most important thing to remember is be flexible and not get locked into a specific way of thinking. Try to visualize what happened over the wire and in the browser. Sadly I have seen people lose hours and days without trying the steps above thats why I finally decided to blog it out.

Here is a link to Selenium documentation on the subject of NoSuchElementException Enjoy it I know I did!

Links to Troubleshooting:
Part 0 Troubleshooting WebDriver issues are easy and so can you!
Part 1 Troubleshooting WebDriver NoSuchElementException
Part 2 Troubleshooting WebDriver StaleElementReferenceException

Thursday, February 6, 2014

NUnit Test Data in CSV or Excel

     I'm always trying to find ways to build what I call "lightweight data driven tests" and I keep coming back to Excel or CSV as a way to accomplish this quickly.  Why? Because, from thought to execution you can quickly get a large number of tests completed while keeping them easy to maintain. Data driven testing is much like gold mining, some people spend a lot of money and only find dirt others get rich quick and everything between that.  The best case for this Pattern is when you want a third party to take some degree of ownership of the data, a PM, manual QA, customer service, Technical Account manager, Sales Engineer, or other. The example below will be in CSV in case you don't actually have Excel installed. But that won't matter because Excel and CSV are basically the same in code, Excel is just easier to work with.
     First off here is a sample test "TestCsvData" and a TestCaseSource "CSVDATA" that calls the "TestDataReader" class's "ReadCsvData" method to get its test data.
[TestCaseSource("CSVDATA")]
public void TestCsvData(string a, string b, string c)
{
    Assert.AreEqual(int.Parse(a) + int.Parse(b), int.Parse(c));
}

public IEnumerable<TestCaseData> CSVDATA
{
    get
    {
        List<TestCaseData> testCaseDataList = new TestDataReader().ReadCsvData(@"Data\CSVData.csv");
        if (testCaseDataList != null)
            foreach (TestCaseData testCaseData in testCaseDataList)
                yield return testCaseData;
    }
}

     Next we just need to make an OleDbConnection to the file and select the rows we want to use. For a CSV file the table is the file name and for a Excel file the spreadsheet is the table. The first row is treated as the column names, if you don't want to return all columns as test data then change the select statement .
class TestDataReader
{
    public List<TestCaseData> ReadCsvData(string csvFile, string cmdText = "SELECT * FROM [{0}]")
    {
        if (!File.Exists(csvFile))
            throw new Exception(string.Format("File name: {0}", csvFile), new FileNotFoundException());
        var file = Path.GetFileName(csvFile);
        var pathOnly = Path.GetFullPath(csvFile).Replace(file, "");
        var tableName = string.Format("{0}#{1}", Path.GetFileNameWithoutExtension(file), Path.GetExtension(file).Remove(0,1));

        cmdText = string.Format(cmdText, tableName);
        var connectionStr = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Text;HDR=Yes\"", pathOnly);
        var ret = new List<TestCaseData>();
        using (var connection = new OleDbConnection())
        {
            connection.ConnectionString = connectionStr;
            connection.Open();

            var cmd = connection.CreateCommand();
            cmd.CommandText = cmdText;
            var reader = cmd.ExecuteReader();

            if (reader == null)
                throw new Exception(string.Format("No data return from file, file name:{0}", csvFile));
            while (reader.Read())
            {
                var row = new List<string>();
                var feildCnt = reader.FieldCount;
                for (var i = 0; i < feildCnt; i++)
                    row.Add(reader.GetValue(i).ToString());
                ret.Add(new TestCaseData(row.ToArray()));
            }
        }
        return ret;
    }

    public List<TestCaseData> ReadExcelData(string excelFile, string cmdText = "SELECT * FROM [Sheet1$]")
    {
        if (!File.Exists(excelFile))
            throw new Exception(string.Format("File name: {0}", excelFile), new FileNotFoundException());
        string connectionStr = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0 Xml;HDR=YES\";", excelFile);
        var ret = new List<TestCaseData>();
        using (var connection = new OleDbConnection(connectionStr))
        {
            connection.Open();
            var command = new OleDbCommand(cmdText, connection);
            var reader = command.ExecuteReader();
            if (reader == null)
                throw new Exception(string.Format("No data return from file, file name:{0}", excelFile));
            while (reader.Read())
            {
                var row = new List<string>();
                var feildCnt = reader.FieldCount;
                for (var i = 0; i < feildCnt; i++)
                    row.Add(reader.GetValue(i).ToString());
                ret.Add(new TestCaseData(row.ToArray()));
            }
        }
        return ret;
    }
}

So why Excel? Its just easier than anything else.... 

  • Excel was built for this type of data manipulation 
  • Excel has lots of functionality to help you get work done quick such as pattern copy
  • Using a file instead of DB makes the test suite more accessible to non-technical people
  • SQL will require a lot more time to develop
  • SQL will require a lot more time to maintain
  • CSV not very good readability or data entry 
Generic methods like these return all the data as string parameters so its up to the test method to correctly type the data.

In the past I have had a hard time convincing people of the benefits of testing this way, I'll do my best here to allay their concerns and yours.
Concern: Putting the test data in Excel or CSV will somehow make the data become irrelevant especially over time. I think they have the perception that having the data in a file external to the test class will cause a data issue or slow down the time to find and fix issues with the data.
Answer: False, the data will become irrelevant no matter where you store it, so focus on changing/fixing it as soon as it happens regardless of data location.
Concern: By putting the test data in Excel or CSV the data will lose visibility, meaning the PM's, managers or manual testers won't have visibility to the data.
Answer: False, we are talking about moving the data from source code to Excel or CSV file and it will most likely be in an adjacent folder. That means the data is going to be in the same source control as before so no access control will change. Furthermore, you might grant write permissions to manual testers on an excel file to add more test cases where you won't want them touching source code.
Concern: Shouldn't we put this in SQL, reasons: (reporting, visibility, easy data entry, etc)
Answer: False, if you put this data in SQL you need to build a data access layer and this will make changing the data more difficult and rigid. Also, if you don't already have one, using SQL now will add a new dependency on SQL.  Rule of thumb SQL is great for enterprise software but not at all lightweight.
Concern: Excel or CSV  will not scale to meet the data needs, or will have poor performance.
Answer: If you have this problem with your test data, you have other more important problems. I believe if you could actually find enough relevant tests to make Excel or CSV perform poorly enough to bother you then you hit a testing gold mind.

Sunday, September 22, 2013

Use ExpectedCondition over Thread.Sleep() in tests, Selenium 2 Webdriver C#

So the theory is you should never use a Thread.Sleep() in automated UI testing because it will be hard to maintain, prone to failure and perform poorly.
  • Whatever you set the value of the wait to, you might have to increase it in the future. On different hardware the "web site" runs slower, when the "web site" being tested changes the waits will need to change.
  • The smaller you set the wait the bigger the chances of future issues, 1,2,3 seconds waits are your worst enemy.
  • If you set the value very high (say 30sec) so you don't need to adjust it in the future you will waste time in execution. Example in 1200 tests it adds up to about one hour of wasted execution time, and you should aim to way more than 1200 tests.
Below are two code snippets that are functionally the same except for the way each waits for the next element.

The wrong way
In this snippet we are clicking the submit button
next we wait a static 1000 milliseconds using Thread.Sleep()
Finally we assert that ByLabel1Div is present by calling ElementExists
My1Page.SubmitButton.Click();
Threading.Thread.Sleep(1000);
Assert.AreEqual(true, Driver.ElementExists(My2Page.ByLabel1Div));

The right way
In this snippet we are clicking the submit button same as above
Finally we assert that ByLabel1Div is present, by calling WaitUntilExists witch return in 0-10 seconds if the element meets the ExpectedConditions of ElementExists
My1Page.SubmitButton.Click();
Assert.AreEqual(true, Driver.WaitUntilExists(My2Page.ByLabel1Div));

In the cases where ByLabel1Div is loaded  by Javascript and Ajax, the time to load the ByLabel1Div  can be affected by many things such as workload on web server, DB server and local machine as well as the network latency.

Selenium 2 Webdriver provides the following 4 ExpectedConditions for you to use (ElementExists, ElementIsVisible, TitleContains, TitleIs). In my open source project I have created 8 additional ExpectedCondition's (ElementNotExists, ElementTextEquals, ElementTextNotEquals, ElementTextContains, ElementTextNotContains, ElementAttributeEquals, ElementAttributeNotEquals, ElementNotVisible)

Here is the magic behind the WaitUntilExists, It is basically a Extension method extending the IWebDriver interface. Its using a WebDriverWait with the ExpectedConditions ElementExists to return as soon as the condition is met.
public static bool WaitUntil(this IWebDriver iWebDriver, Func condition, int maxWaitTimeInSeconds = 10)
{
   var driver = (IWebDriver)iWebDriver;
   var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(maxWaitTimeInSeconds));
   try
   {
      wait.Until(condition);
   }
   catch (WebDriverTimeoutException)
   {
      return false;
   }
   return true;
}

public static bool WaitUntilExists(this IWebDriver iWebDriver, By locator, int maxWaitTimeInSeconds = 10)
{
   return WaitUntil(iWebDriver, ExpectedConditions.ElementExists(locator), maxWaitTimeInSeconds);
}
For more information here's the full file IWebDriverExtension.cs with more WaitUnils*.

In the snippets above you see Click, and then WaitUntilExists, or more generally stated as a pattern: perform action and then validate. This pattern (perform action and then validate) is one you'll want to use often especially for elements that are loaded by Javascript and Ajax.

Friday, September 13, 2013

Working with Selenium 2, WebDriver and .NET. Tips and tricks

This is a supplement to help a person learning Selenium 2, WebDriver and .NET. As a supplement it will be very helpful for you while you're learning and using Selenium to keep these things in mind, but it's not intended to teach you (Selenium 2, WebDriver and .NET), you can get that information elsewhere.

Get the source code
The source code is the best place to answer your questions.  If you're attempting to do Selenium 2 for the first time whether you have UI Automation experience or you're a first timer, youre going have a lot of questions. Understanding the code will not only help you answer specific questions but increase your overall understanding of the different Selenium projects and how best to use them. Also for reasons described below the online information won't always answer your questions.
Update often
Update the Selenium packages you are working with as frequently as you can. People are always making contributions to the Selenium projects and there are frequent updates available, read these change logs and download!  It's super easy to update your Selenium projects and usually it won't impact you tests in a negative way.
Read but don't believe what you see
A lot of the documentation is missing, incomplete, out of date or not in .NET on the Selenium site, try not to complain about it too much, it gets old and doesn't help you solve the problem. This problem is all over the web (including this blog) it's because Selenium is moving so fast (its a good thing!). You can solve the problem by submitting the correct answers to your favorite site. As a .NET SDET you will have a problem no Java SDET will have, that you'll be translating Java into C#, see a blog I wrote in response to this Basic problem.
Don't sleep!
Please don't sleep in your code its a false economy, bottom line if you have a Thread.Sleep(4000) in your code today Murphy's law says it will fail tomorrow. If you honestly think the only way the test will work is if you use a Sleep then you've missed the plot. What's the plot, use the pattern ExpectedCondition instead of Thread.Sleep(). For more information check out this blog article Use ExpectedCondition
Make sure your closing all the browser sessions.
Close all your browsers or call WebDriver.Quit() before you exit your code (think IDisposable). Don't abandon your RemoteWebDriver or other IWebDriver sessions! It will bring down your Selenium Grid, it will eat your hard drives, It will leak memory, and it will reduce the amount of sessions available. For details read this blog entry WebDriver.Quit() and also this StackOverflow question
Think like a web server
You should always be thinking like a user when doing testing because you are user #0, the first user! But you'll also need to think like a Web Server. When a user uses a web page it thinks it working with a synchronized application, not true. Underneath it all, all websites are using REST!  Client side javascript and AJAX are two ways the user experience is enhanced, and can give the illusion of a synchronise responsive application. Selenium will wait for the page to be loaded but it has no mechanism for telling when a random element was added to a page from javascript or AJAX except for the ExpectedConditions.  So many people get lost for hours in their own heads blaming Selenium when they simply missed the plot.
Page factory is good but it will limit you 
If you use a page factory it can be helpful, but keep in mind it will restrict you from being able to use your locators in out side of the page (which you might find very helpful) so its a trade off.
Party like it your birthday

Sunday, June 23, 2013

Best use of webDriver.Quit() Close() Dispose() or it will eat your hard drive


Recently I found that some of VM's I share with coworkers were running out of hard drive space, I was surprised to find that the reason was webDriver.Quit() wasn't being called. My cohorts were running code or NUnit tests in the debugger and forcing the code execution to stop. Stopping execution this way was ensuring that [TearDown] and [TestFixtureTearDown] weren't being called, which is where we had placed the Quit() call. To put this in perspective one of my cohorts had over 18 Gb of data in his appData folder because of this.

What to do when done with the WebDriver
It's important to make sure to free up the driver at the end of the run by calling the Quit() method. Calling the Quit method will clean up temporary files created and in the case of RemoteDriver it will also close the session on the Selenium Server.

I Did some Googleing and didn't really find an answer that I thought I could trust, so I turned to the source code and found the following:

• webDriver.Close() - Close the browser window that the driver has focus of
• webDriver.Quit() - Calls dispose
• webDriver.Dispose() Closes all browser windows and safely ends the session

In summary ensure that Quit() or Dispose() is called before exiting the program, and don't use the Close() method unless you're sure of what you're doing. I found this unanswered question on StackOverflow

What happens if you don't Call WebDriver.Quit() before exiting the program
The result will be that files are abandoned in your appData folder. And in the case of the remote driver the Selenium Server will not have ended the session properly, causing a memory leak in the Selenium Server.

I wouldn't have guessed this to be a problem for several reasons, first I would have thought those files would be cleaned up when doing a disk clean up. That assumption was incorrect even though these files are being written to a temp folder its one that Disk cleanup doesn't work on. Second as I rarely hit the "Stop" button in the debugger and therefore my appData folders aren't blowing up like the housing market. The reason I don't like to stop execution like that is when you let execution continue it ensures we get a NUnit log and all teardowns take place, in our case that means un-managed objects are cleaned up screenshots and logs are written. Having these logs and screenshots make it easier to troubleshoot and do investigation.

Monday, August 29, 2011

Selenium 2 WebDriver C# Basic Example 2.33.0.0

Learning Selenium 2 with WebDriver in C# can be much harder than it should be. When I started using it in version 2.4.0 I tried a bunch of samples pulled from other peoples blogs and none of the samples worked without having to add/edit code sometimes extensively. I quickly identified a couple reasons for this the code samples didn't work out of the box.
1) First they didn't provide the version of selenium used, this was especially a big problem at the beginning because changes and new features were being added all the time to Selenium 2
2) Second (C# != Java) a lot of the C# samples I found were written Java, also a lot of the C# questions on sites like StackOverflow are written in... Java!

So here is my C# sample that I wrote in C#, good luck.
I started with the Selenium 2.33.0.0 libraries

using OpenQA.Selenium;  
using OpenQA.Selenium.Firefox;  
using NUnit.Framework;  
using System;  
namespace BasicWebDriver  
{  
   [TestFixture]  
   public class BasicGoogleTest  
   {  
     [Test]  
     public void SearchForTest()  
     {  
       string searchText = "Selenium 2 WebDriver C# Basic Example";  
       using (IWebDriver driver = new FirefoxDriver())  
       {  
         driver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0, 0, 10));  
         driver.Navigate().GoToUrl("http://rickcasady.blogspot.com");  
         driver.FindElement(By.Id("b-query")).SendKeys(searchText);  
         driver.FindElement(By.Name("b-query-icon")).Click();   
         Assert.NotNull(driver.FindElement(By.PartialLinkText(searchText)));  
       }  
     }  
   }  
} 
and don't copy this into Eclipse it might not compile.....

Friday, July 8, 2011

Write .NET "exe" with out Visual Studio installed

Here is a quick step to start compiling C# code from the command prompt and notepad. The other day I spent an hour learning how to do something I already know how to do in C# because I didn't have access to VS.NET. Granted most of that time was spent learning how to over come the limitations of the script language. You'll need to make sure the .NET framework is installed before you start.

1. Create a text file
2. Rename it to Program.cs
3. Open it in a text editor
4. Type your program:

using System;
namespace Example.HelloWorld
{
 class Program
 {
  static void Main()
  {
   Console.WriteLine("Hello World");
  }
 }
}

Notes on compiling:
* Find the C# conpiler "csc.exe", there should be several versions my current one is "C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe"
- Type at the command-line: C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe c:\yourPath\Program.cs
* Remember to type the full path to both the compiler and the *.cs file, you can add the compiler path to the "path" enviroment variable so you dont have to type it every time.
- Type at the command-line: csc.exe c:\yourPath\Program.cs

The ability to write create a "exe" is powerful....