Should Testers know how to code?

I was actively involved in recruitment of testers for Trainline.com in 2012 and came across an age old discussion once again which made me write about this topic. Should Testers know how to code?

Adding to this, there was also a statement made by Adam Goucher at CAST2012..

This is the last generation of testers that don’t know how to code

In my opinion, it totally depends on the situation. For example, in companies like Microsoft and Google, virtually all testers must know some form of coding. There are still companies out there which only require manual testers. If coding know how was not a requirement, I would also prefer to hire a tester who has critical thinking, analytical, investigative skills, good communication, understanding of risk and knowledge of common areas where bugs tend to hide as compared to someone who can just write C#.

 I also agree with Cem Kraner’s view below:

Testing within a business involves at least two types of knowledge: knowledge about testing and knowledge about the type of application under test. Much of the PR about the need for “software development engineers in test” has come from software companies. For them, software is the subject matter. For them, expecting someone to understand code is like a bank expecting someone to understand financial statements. It is unfortunate that the software publishers and software service providers have been speaking with a louder megaphone than the other industries: the result has been that some non-software companies are changing recruiting standards to look for people with stronger software knowledge instead of stronger industry (e.g. banking) knowledge.

Vacancies for “Developers in Test” or “QA Developers” are coming out more and more, especially in Agile environments. We need to remember, automation testing can never replace the need to do manual testing. There has to be a balance between the two. Skilled testers are adaptable people and should not be threatened by test automation. It’s a valuable skill to have and it’s not like learning how to code will make you forget manual testing. Secondly, the more the tester knows about coding, they’ll be able to do their jobs better, and the more career opportunities they will have (more on this later – I have some stats to prove this). Additionally, talking to developers in a language they can understand, writing clear bug reports which put your message across correctly and quickly has to be a good thing, right?

As a recruiter, you also need to use a common sense approach. I have seen companies that seem to use a standard list of demands, that they once created, and keep on using because they have been told it is a good list by the consultant they hired to create it, or they see other companies ask for the same skills, so they assume they are vital. You should look at the job spec, the role of a tester in your organisation and your team structure. What does a tester’s role involve, instead of what others testers in your company do/can do. Sometime you may find that the ability to write code is desirable instead of a requirement. If it is a requirement, state the level of familiarity with coding that you require. This way you will avoid putting off good candidates who will just be overwhelmed by the long list of requirements. Here at the Trainline, I have amended our requirements in job specs to be inline with the role of a QA Developer.

Lastly, what all programming skills should a tester have? This totally depends upon the job requirement and differs from one company to the other. Some positions are for automated testers and for this you require a good set of programming skills to create test frameworks. A recommendation for a manual tester or a tester thinking of improving their programming skills would be to:

  • Learn enough in any language (look at your target market or see research below) so you can write some test automation helper code, create classes, make asserts etc.
  • Learn enough of general programming concepts so that you can write code for typical easy programming exercises available on the internet.
  •  Once you have mastered this, drill deeper into object oriented design skills/concepts and create a mock test framework testing a website or an application.
  •  Also attend tester gatherings and talk to other testers/managers and see what skills they seek in a tester.

Now, let’s have a look at testing job specs taken from 2012. This research has been done by a UK based recruitment company.

Percentage of jobs that require Good/Exceptional coding skills:

Q1/Q2 2012 – 63%

Q3/Q4 2012 – 84%

Breakdown of languages required:

  Q1/Q2 2012  –  28% – Java, 26% – C#, 23% – Ruby, 16% – C, 7% – PHP

Q3/Q4 2012  –  39% – Java, 35% – C#,  8% – Ruby, 12% – C, 6% – PHP

The remaining  37% in Q1/2 2012 and 16% in Q3/4 2012 were a mixture of “we don’t need someone with any coding experience”, or “we purely need a Manual tester.”

Research also shows that salaries for testers with exceptional coding abilities have risen by £3500 per annum in the past 6 months.

What this means:

The research above suggests there are 21% more people being hired in the past 6 months alone with good to exceptional coding knowledge than without, however, there is clearly still a requirement for Manual testers.

Java is a still the main scripting requirement but Ruby has become increasingly popular.

Top Test Automation Technologies are:

  •   Selenium, including SeleniumRC and Webdriver
  •   QTP
  •   XUnit frameworks such as JUnit, NUnit, TestNG, etc
  •   BDD tools like Specflow/Cucumber
  •   Watir or Watin.

The above results are for the UK. A similar study was done in the US not long ago and you can read the details about that here.

Final Words:

Adam Goucher is probably right. But for now, manual testing and manual testers are here to stay. As more and more companies move towards the “Developer in Test” mentality, I see future testers taking up coding, especially if they want to work for the best companies and not to forget, these roles pay considerably more as compared to manual testing roles.

Webdriver – How to Write Automated Tests Using Page Object Design Patterns

Look at this code

Site.Click("crt1h-yth");
Site.Click("chk-formb1");
Site.Click("pay_02");

and compare it with this..

Shop.AddToCart();
Shop.Checkout();
Shop.MakePayment();

The functionality is the same, but the way to present this is so much different. This is where page objects come into the fold and this blogpost is just about that.

So what are page objects ?

Page Object is a Design Pattern which has become popular in test automation for enhancing test maintenance and reducing code duplication. Page object is a language neutral pattern for representing a complete page or a part of a page in an objected oriented manner. We use them to model the application’s user interface. Page objects expose methods that reflect things that a user can see and do on a page. It also hides the details of telling the browser how to do those things. In short, page object encapsulates behaviour of a page.

Your tests then use the methods of this page object class whenever they need to interact with that page of the UI. The benefit is that if the UI changes for the page, the tests themselves don’t need to change, only the code within the page object needs to change. Subsequently all changes to support that new UI are located in one place.

So why do page objects? The main reasons are

  • Maintenance
  • Readability of scripts
  • Reduced or eliminated duplication
  • Reusability

Consider the following example, which does not use page object design.

namespace SeleniumTests
{
[TestFixture]
public class LinkedInTest
{
private IWebDriver driver;
private string baseURL;
 
[SetUp]
public void SetupTest()
{
driver = new FirefoxDriver();
baseURL = "http://www.linkedin.com/";
 
}
 
[TearDown]
public void TeardownTest()
{
driver.Quit();
}
 
[Test]
public void LoginAndLogoutOfLinkedIn()
{
driver.Navigate().GoToUrl(baseURL + "/");
driver.FindElement(By.Id("session_key-login")).Clear();
driver.FindElement(By.Id("session_key-login")).SendKeys("test@123.com");
driver.FindElement(By.Id("session_password-login")).Clear();
driver.FindElement(By.Id("session_password-login")).SendKeys("awesomePswd");
driver.FindElement(By.Id("btn-login")).Click();
Assert.assertTrue(driver.isElementPresent("inbox button"),
                                "Login was unsuccessful");
driver.FindElement(By.LinkText("Sign Out")).Click();
}
 
}
}

There are a few problems with this approach:

  1. There is no clear separation between test methods and the locators of the application (Ids or LinkText locators that I’ve used above). They are all in a single method. If the application changes its identifiers or layout, then the tests must change.
  2. Imagine a scenario of multiple tests which require the use of this login functionality (There can be various scenarios to do with testing login functionality). The same login code will be repeated again and again in each test. Any change in UI will mean that all tests will have to be modified.
  3. The above code is not very readable, or maintainable, has duplication and is not reusable. (Exactly the issues addressed by Page Object Design).

 

By applying the Page Object design technique, I can rewrite the above test in the following way.

Firstly here is the flow that I will be automating using C#, NUnit and Webdriver:

  1. Navigate to the LinkedIn Homepage
  2. Enter Username
  3. Enter Password
  4. Click Login
  5. Assert Some Text is appearing on the HomePage after login
  6. Logout
  7. Assert that I have signed out

 

Below is the Page Object for the Login-In Page:

using System;
using OpenQA.Selenium;
 
namespace SeleniumTests
{
    public class LoginPage
    {
        protected readonly IWebDriver WebDriver;
 
        public LoginPage(IWebDriver webdriver)
        {
            this.WebDriver = webdriver;
 
            string title = WebDriver.Title;
 
            if (!title.Equals("World's Largest Professional Network | LinkedIn"))
            {
                throw new InvalidOperationException("This is not the Login Page. Current page is: "
                                                    + title);
            }
        }
 
        /*
        A property to enter username on login page
     */
        public string EnterUsername
        {
            set
            {
                IWebElement usernameField = WebDriver.FindElement(By.Id("session_key-login"));
                usernameField.Clear();
                usernameField.SendKeys(value);
            }
        }
 
        /*
        A property to enter password on login page
     */
        public string EnterPassword
        {
            set
            {
                IWebElement passwordField = WebDriver.FindElement(By.Id("session_password-login"));
                passwordField.Clear();
                passwordField.SendKeys(value);
            }
        }
 
        /*
        A method to click the login button on the page
     */
        public void ClickLogin()
        {
            IWebElement loginButton = WebDriver.FindElement(By.Id("btn-login")); // find the login button
            loginButton.Click(); // click on the login button
        }
    }
}

Here is how the Page Object for the HomePage looks like:

using System;
using OpenQA.Selenium;
 
namespace SeleniumTests
{
    public class HomePage
    {
        protected readonly IWebDriver WebDriver;
 
        public HomePage(IWebDriver webdriver)
        {
            this.WebDriver = webdriver;
 
            string title = WebDriver.Title;
 
            if (!title.Equals("Welcome, Hasan! | LinkedIn"))
            {
                throw new InvalidOperationException("This is not the HomePage. Current page is: "
                                                    + title);
            }
        }
 
        /*
A method to logout of LinkedIn
*/
 public void Logout()
        {
            driver.FindElement(By.LinkText("Sign Out")).Click();
        }
    }
}

Finally, here is my LinkedIn login Test that consumes the two page objects:

using NUnit.Framework;
using OpenQA.Selenium;
 
namespace SeleniumTests
{
    [TestFixture]
    public class LinkedInTest
    {
        private IWebDriver _driver;
 
        [SetUp]
        public void Setup()
        {
            _driver = SeleniumHelper.GetSelenium();
        }
 
        [TearDown]
        public void TearDown()
        {
            SeleniumHelper.TearDownCurrentSelenium();
        }
 
        [Test]
        public void VerifyThatYouCanLoginAndLogoutOfLinkedIn()
        {
           // Create an instance of the Login Page
            LoginPage login = new LoginPage(_driver);
 
            //Enter Username
            login.EnterUsername = "HasanAziz@test.com";
 
            //Enter Password
            login.EnterPassword = "XYZ123";
 
            // Click Login
            login.ClickLogin();
 
            //Create an instance of the HomePage
            HomePage home = new HomePage(_driver);
 
            //Assert that some text is displayed on the Home Page
            Assert.IsTrue(_driver.PageSource.Contains("LinkedIn Today:"));
 
            //LogOut from LinkedIn
            home.Logout();
 
            // Assert
            Assert.IsTrue(_driver.Title.Equals("Signed Out | LinkedIn"));
        }
    }
}

As you can see from above, this is much more clearer now. The comments in the tests are really not needed as they are just repeating what the code is doing (Something I mentioned in my blogpost “Comments & Bad Comments” earlier, but I have added these here just so you could understand whats going on.

There is alot of flexibility when it comes to designing page objects. I could also use techniques like inheritance, function overloading or any other Object Oriented programming concept, and  instead of having the browser name and LinkedIn URL set as hardcoded values, I could have picked them up from a config file etc. Lets take another example of the eBay homepage. You can automate it using 1 Page Object if you wish, but ideally you can have 5-6 Page objects for the home page only.

Although the flexibility is present, there are a few basic rules that you should adhere to for mainiting your code:

  1. Do not create page objects at once, do only what you need at this given time. You can spend weeks in trying to create page objects for your whole application and this would be a waste of time. Your page objects will grow when new requirements come in which inturn will require new test scripts.
  2. Asserts do not belong in page objects, they belong in test scripts. Page objects only interact with the page, they do not make the decision of whether something is passing or failing.
  3. An exception to the above rule is that there should be one single verification within the page object and that is to verify that the page and any important elements within the page were loaded correctly. This verification should be done while instantiating the page object. In my example above, both the LogInPage and HomePage constructors check that the expected page is available and ready for requests from the test.

Further Reading:

  • There are other Design Patterns, some use Page Factory for instantiating objects. More on this here 
  • Some Information on Page objects can be found on code.google.com
  • Very Useful test design considerations can be found on the Selenium HQ site

Books on this topic:

  • I recommend xUnit Test Patterns: Refactoring Test Code by Gerard Meszaros – Almost 900 pages of patterns that can be used for automation and unit testing.
  • Refactoring: Improving the Design of Existing Code by Martin Fowler and Kent Beck. Its developer centric but a very good read.
  • Design Patterns: Elements of Reusable Object-Oriented Software – This is on my reading list but I have heard very good reviews about it.

Webdriver – Drop Down boxes, Check Boxes & Filling in forms

Here is how you can select values in a drop down box: The drop Down has an ID called “Domain” and values Yahoo, Virgin, AOL.

SelectElement select = new SelectElement(_webdriver.FindElement(By.Id(“Domain”)));

Select.DeselectAll();

select.SelectByText(“Yahoo”);

This will deselect all options, and then select the option withthe displayed text of “Yahoo”.

You can select Radio Boxes and Check Boxes by the following command:

driver.FindElement(By.Id(“SingleJourneyCheckBox”)).Click();

You can find out whether this checkbox/radiobox is alrady selected by this command.

IWebElement checkbox = driver.FindElement(By.Id(“SingleJourneyCheckBox”));

if(checkbox.Selected)

{

//do something here.

}

If you were filling out a form and  once you’ve finished, you probably want to submit it. One way to do this would be to find the “submit” button and click it:

driver.findElement(By.id(“submit”)).Click(); // Assume the button has the ID “submit”

Alternatively, WebDriver has the convenience method “submit” on every element. If you call this on an element within a form, WebDriver will crawl up the DOM until it finds the enclosing form and then calls submit on that. If the element isn’t in a form, then the NoSuchElementException will be thrown:

element.submit();

Webdriver Commands & Operations

 1. Opening a New page:

 driver.get(“http://www.google.com”);

WebDriver will wait until the page has fully loaded (that is, the “onload” event has fired) before returningcontrol to your test or script. It’s worth noting that if your page uses a lot of AJAX on load then WebDriver may not know when it has completely loaded. If you need to ensure such pages are fullyloaded then you can use “waits”. More on this later.

2. Locating UI Elements & Interactions with the page:

Imagine that you have an element displayed on a page with the following code

<input type=”text” name=”Password” id=”Password-ID”/>

You can find this element by using Any of the following commands:

Locate By ID:

This is the most efficient and preferred way to locate an element.

IWebElement element;

element=driver.findElement(By.id(“Password-ID”));

Locate By Name:

Find the input element with matching name attribute.

IWebElement element;

element=driver.findElement(By.name(“Password”));

Locate By XPath:

WebDriver uses a browser’s native XPath capabilities wherever possible. On browsers that don’t have native XPath support, webdriver provides its own implementation. This can lead to some unexpected behaviour unless you are aware of the differences in the various xpath engines.

 IWebElement element;

element=driver.findElement(By.xpath(“//input[@id=’Password-ID’]”));

 Locate By Class Name:

“Class” in this case refers to the attribute on the DOM element. Often in practical use there are many DOM elements with the same class name, so be very carefull when finding elements using this option. For example, lets say that you have the following html code in your browser:

<div class="cheese"><span>Cheddar</span></div><div class="cheese"><span>Brie
</span></div>

If you use the below code, it will only target the first element it finds, i.e Cheddar.

IWebElement element;

element=driver.findElement(By.ClassName(“cheese”));

But if you want to find multiple elements, you can use the command below:

IList<IWebElement> cheeses = driver.FindElements(By.ClassName("cheese"));

 Locate by Tag Name:

You can also locate an element by its DOM tag name.

For example, say you want to find an iframe which is displayed on the page with the following code:

<iframe src="..."></iframe>

The command you will need is this:

IWebElement frame = driver.FindElement(By.TagName(“iframe”));

 Locate By Link Text:

You can find an element by matching its visible text.

Say you have a link displayed with the following code.

<a href="http://www.thetrainline.com/Hotels">Hotels</a>>

The command you will need is this:

IWebElement cheese = driver.FindElement(By.LinkText(“Hotels”));

 Locate By Partial Link Text:

You can find an element by matching its partial visible text.

Say you have a link displayed with the following code.

<a href="http://www.thetrainline.com/BestFareFinder">Best Fare Finder</a>> 

The command you will need is this:

IWebElement cheese = driver.FindElement(By.LinkText(“Fare”));

When using all of the above commands to find elements, if no element can be found then a NoSuchElementException will be thrown.

3. What to do after finding an element:

So you have found an element, what would you like to do with it?

You can enter some text in the text field:

element.SendKeys(“some text”);

 Keep in mind that typing something into a text field will not clear it. Instead, what you type will be appended to what’s already there.  You can easily clear the contents of the text field or text area:

element.Clear();

You can click an element:

After finding the element using its link text or partial link text, you can click on it.

element.Click();

Comments & Bad Comments

You should always strive to write your code so that it does what it looks like it does.  Have you heard of the phenomenon called the “write-only code”? This is code that made some kind of sense to whoever wrote it at some point in time, but it is difficult to understand for anyone trying to read it a later date, even if the person reading is the author. This kind of problem can be resolved by comments in the code.

Although comments can be very useful but sometimes it can also go wrong. I saw a common mistake today, which is this:

//Updating all records

Sanitiser.Update(“All”);

//Updating 20 records

Sanitiser.Update(20);

As you can see, these comments are just repeating what the code already said. This is just a waste but surprisingly, this is very common. One of the reason for this is mainly because these developers have been told that comments are good, but they have no idea as to what makes a good comment. A comment should say something that is not obvious from the code and which is likely to be useful to anyone trying to understand the code.

Another common fault is this:

//Updating 30 records

Sanitiser.Update(50);

Here, the comment contradicts the code which is not very helpful. This kind of thing usually happens when someone modified/refactored the code without updating the comment. A quick review of the comments after a code change is always worth doing.

Getting Started with Webdriver and CSharp (C#)

Tools you will need:

  1. An IDE – I’m using Visual Studio 2010 but you can use any IDE. For example Visual Express, which is free
  2. Nunit – Download it from here
  3. Webdriver C# Clent Drivers – Download them from here

 

Open Visual Studio and create a New Project (Class Library):

 

In your project references, add the references for Nunit.Framework.dll, Webdriver.dll & Webdriver.Support.dll

 

 

You are now ready to write some code. Here is what we will be doing.

  1. We will run our tests in firefox browser
  2. The test will navigate to www.yahoo.co.uk
  3. Type “selenium hq” into the search box
  4. Clink the “Search” button
  5. On the results page we will Assert on the title of the page.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;

namespace GettingStartedWithWebdriver
{
[TestFixture]
public class WebDriverDemo
{
private IWebDriver _driver;
private string _baseUrl;

[SetUp]
public void SetupTest()
{
_driver = new FirefoxDriver(); // we want our tests to run in Firefox
_baseUrl = “http://www.yahoo.co.uk”;

}

[TearDown]
public void TeardownTest()
{

_driver.Quit(); // quits webdriver and closes any associated windows
}

[Test]
public void NavigateToTrainLineHomePageAndAssertOnTitle()
{
_driver.Navigate().GoToUrl(_baseUrl);
_driver.FindElement(By.Id(“p_13838465-p”)).Clear(); // clear the text field before entering text
_driver.FindElement(By.Id(“p_13838465-p”)).SendKeys(“selenium hq”); // type into the text field
_driver.FindElement(By.Id(“search-submit”)).Click(); // click on the search button
Assert.AreEqual(“selenium hq – Yahoo! Search Results”, _driver.Title); // Assert using nunit on the window title
}

}
}

Incase you are wondering where did I get the ID of the fields from, you can use a tool like firebug in firefox to inspect elements on a broweser. See below

 

 And there you have it, your first test in Webdriver and CSharp.