I started my automation career with procedural language for the test script, Now I start learning OOP's concept but after learning OOP's concept I am not feeling confident that how can I use four major OOP principles like encapsulation, inheritance, polymorphism, and abstraction for writing the test script & design. I want to know best practice to use the object-oriented design for a tester.

up vote 6 down vote accepted

Rather than taking those specific principles and try to match up to them an alternative approach might be to focus on:

  • DRY - Making sure you don't repeat code unnecessarily
  • Readable - Use real english words and avoid most abbreviations
  • Simple - Use Single Responsibility to make sure a function does only 1 thing
  • Maintainable - Aim to make the code understandable for a newbie

In order to meet the above goals you might use encapsulation, inheritance, polymorphism, and abstraction but they would be tools to achieve the above rather than goals in their own right.

  • 2
    +1 for suggesting DRY.I think if someone just focus on keeping their code DRY, they can go a long way...without knowing much technical lingo. – Vishal Aggarwal May 16 at 11:31

I do not believe there could be some special advice for testers on how to use the OOP principles since as soon as you open your IDE and start writing code you stops being a tester and starts being a developer in test. However below is what my experience shows.

Encapsulation

You are to use this principle in your tests to segregate public methods and fields from private ones. When you write some code (especially when you develop test frameworks) you might have the code that is to be called in some prescribed cases or in some prescribed order and calling that code in different cases might break the framework state. Hence you mark that code as private so that the testers which use your framework will not be able to call that piece of code and introduce inconsistency.

This also makes sense to segregate private and public parts for a kind of utility code in your test classes and even if you are the only person working with the tests because you might forget what logic you initially put to your code.

Inheritance

It is widely used in testing. This can be used (but not limited by) to extend the existing test class with additional tests, or to store the common utility code in parent class. This is also widely used in Page Object design pattern because the pieces of a real UI often extend other pieces of that UI and hence it makes sense to transfer that model to your Page Object model using inheritance mechanism.

Polymorphism

Polymorphism is more often used in test framework development rather then in tests themselves. You might want your test framework methods to support different types of incoming data so that you introduce methods having the same name but accepting different types of parameters. This can be used when you introduce new public code and try to maintain backward compatibility for old tests which use your framework or when you just introduce some flexibility to your framework. For example you provide a method that resets test data. Your framework supports test data to be stored in either a file or in database. So you introduce to methods resetTestData. One accepts reference to a file and another accepts reference to a database. The latter example also can be implemented using the conception of Abstraction.

Abstraction

Abstraction are widely used in test framework development. The example of using Abstraction principle is widely known WebDriver. When you develop a framework you delegate implementation of some piece of functionality to the people who will use your framework. This adds some flexibility and extendability to your framework. Let's look at the example shown in previous principle description. Instead of introducing to different resetTestData methods you might introduce the abstraction like TestDataSource and define what the methods are to be implemented by the classes which implement that abstraction. Say you define that the abstraction prescribes to implement reset method and you do not care how that method will be implemented (what would be the logic behind that method).

In this architecture your framework just knows that it should call reset against the object that implements TestDataSource conception. Those who use your framework know better then you in which format they store their test data, hence they are not limited by either file or database and are free to use their own storage. This gives less functionality "out-of-the-box" but makes the framework more flexible (usually the framework developers deliver some basic implementations of the abstractions so that people will less skills could start using it in some basic environment).

  • 2
    Great Answer!I wish I could upvote this, multiple times. – Vishal Aggarwal May 16 at 11:28
  • 1
    @VishalAggarwal - you always can assign a bounty for answer :-) – P.M May 16 at 14:17
  • Go ahead @VishalAggarwal think Answer of this question deserves the bounty ;-) – Nitin Rastogi May 17 at 3:47
  • @ NitinRastogi, if you guys read carefully ,I praised the answer , not the question :) – Vishal Aggarwal May 17 at 7:49
  • 2
    @VishalAggarwal bounty program is only for the great answer not for the question :) – Nitin Rastogi May 17 at 8:41

OOP concept plays an important role in developing a well-structured test automation framework. Just sharing some simple examples of common OOP application in test automation. I am using Java and Selenium Webdriver in these examples.

Encapsulation:

Encapsulate the data of the web pages. Here, data means the elements of the page like texts, buttons etc.

Suppose, you are writing test automations for StackOverflow's User Profile page (Ex: https://sqa.stackexchange.com/users/20681/shahid?tab=profile). You want to get the user name from the profile page. So you can encapsulate the element and write a public method "getUserName()" to access the data.

public class ProfilePage {

    private WebDriver driver;
    private By userName = By.xpath("//h2[@class='user-card-name']"); // we are hiding the element so that it won't be directly accessible

    public ProfilePage(WebDriver driver) {
        this.driver = driver;
    }

    // public method to access the private data
    public String getUserName() {
        return driver.findElement(userName).getText();
    }
}

Inheritance:

In a website that contains multiple pages, there can be some elements those will be common in every pages. For example: header, menu, footer etc. exists in every pages. So, declaring those elements in every page or class will make much redundancy. What we can do is to create a separate class for those those common elements. And then the classes for other pages will inherit this class. Giving a simple example to make this clear:

In sqa.stackexchange.com site, there are some navigation links like Questions, Tags, Users etc. on the top. Wherever you go in this site, these links are shown in every page. Since these elements are common in every page, let's put these into a single class.

public class BasicPage {
    private WebDriver driver;

    private By questionNavLink = By.xpath("//a[@id='nav-questions']");
    private By tagNavLink = By.xpath("//a[@id='nav-tags']");
    private By userNavLink = By.xpath("//a[@id='nav-users']");

    public BasicPage(WebDriver driver) {
        this.driver = driver;
    }

    // For simplification, we are returning nothing from those navigation methods. Otherwise, we would have returned the new web pages got after navigation.

    public void goToQuestions() {
        driver.findElement(questionNavLink).click();
    }

    public void goToTags() {
        driver.findElement(tagNavLink).click();
    }

    public void goToUsers() {
        driver.findElement(userNavLink).click();
    }
}

Now, any other page can get access to all these navigation methods just by inheriting the BasicPage class.

Polymorphism:

This is another common concept we use often. One basic example is the most basic code in Selenium WebDriver Scripting. To initialize webdriver for Chrome browser in Selenium, we use:

WebDriver driver = new ChromeDriver();

Again, we often need to override and overload methods which is a part of polymorphism.

I have been in the test automation space for a few years. On one hand I would say a test automation project should be treated like any other Java project in terms of practices - you should use the available Java features so that you have a high quality software project at the end of the day. Here are aspects which I think are most important.

Maintenance is Key

As the application under test will regularly change (assuming agile), you need to write your test automation project such that you can quickly and cleanly change it to cater for the changes in the product. This means things like:

  • Avoid God Patterns. Have many small classes instead of several large god classes (that's an anti pattern).
  • Don't have the same strings hardcoded in multiple places. Ideally have them as public static final Strings at the top of the relevant class so that others can easily find it.
  • If you have things like username, status, payloads, or other objects getting passed around, each might have defaults, checkValid() etc. then create a dedicated class for each of them - even if it is just a wrapper for a String. This has a few benefits.
  • Use abstraction and the other OOP concepts where appropriate. If you're a beginner I'd recommend reading a few books especially Clean Code.
  • I find the builder pattern is often useful for test automation, for building up a user, a request body, or other objects. It's flexible enough to cater for future changes while also being strict to force people to abide by how you want it to be used. I've also seen the factory pattern used several times in test automation. The strategy pattern is also work a look.
  • The basics can't be overstated. Name things accurately, have code self documenting (people rarely keep comments up to date)

I would say the main downfall of test automation is that people with very limited Java experience end up working on it (e.g. a manual tester who wants to transition to an automation role). For that reason I personally focus on the things listed above and just

  • Keeping things small and simple as per Clean Code.
  • Have the classes map to the real world as best as possible.

If you know what you're testing, like whether it's web / api / embedded etc. You might get better answers.

On top of other excellent answers, I would give one simple piece of advice, just focus to keep your code DRY on every level.This simple rule ,if followed religiously can take one tester so far in his journey of clean code Nirvana.

If one think deeply, he will find that many concepts themselves are just an application of DRY concept like inheritance,abstraction etc.

Just make super simple few liner(mostly one or two liner ) hundreds of generic functions which can be glued together to achieve different things in different contexts.This will give you the ultimate reusability and flexibility in your code.

Whenever you catch yourself copy/ pasting code , think how you can refactor code to define it in one place only and just call it at all other places.

Whenever you touch a code , make it better.

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.