Showing posts with label Testing. Show all posts
Showing posts with label Testing. Show all posts

Sunday, May 18, 2014

Creating a simple test framework in plain English, easy to use and easy to understand

Let's start off by creating our framework and then we are gonna create a Unit Testing project and that's gonna be our tests layer.

First we start by creating a new Class Library project and call it "TestFramework".


Now that we have this project, we can go ahead and create another project inside our solution and this one will contain our tests. In this case we are gonna create a "Unit Test Project" and call it "Tests".


The idea here is to separate the Test Framework from our Tests. Remember in our Architecture Diagram we said this was really important. We don't want our tests to depend on Selenium. We want the Test Framework to depend on Selenium. And the Tests to depend on our Test Framework.

So we'll never gonna add Selenium to our Tests project. We're only gonna add to our Test Framework. And we'll make our Tests depend on our Test Framework.

So let's go ahead and add that dependency. We will go to our references in Tests and add a new reference to TestFramework.


The next thing we are gonna do is to add Selenium to our TestFramework. To do that we go to references in the TestFramework project and select the option "Manage NuGet Packages..."

We'll search for "selenium". Select the packages "Selenium WebDriver" and "Selenium WebDriver Support Classes".


So now that we have our projects set up, let's go ahead and create a very simple test.
We are just gonna open up our website "http://plan2finish.net" and log in with a valid credentials (username and password) and verify that the user logged in successfully.

So we go to our UnitTest1.cs class and create a test named Can_Login_User_With_Valid_Credentials


We are gonna write this test first and then we are gonna implement the framework to make this test succeed.
This is a little bit backwards from how you might think about writing an automation framework but I found that this is the best way to do it for two reasons:


  • It makes sure your API is simple and understandable.
  • It make sure you don't build more framework than you need.


So we can start writing the API we want to use inside of our test. Remember we can't depend on Selenium here. We should try to write it in plain English as possible and we are not gonna ever import Selenium.


We start by creating a method Pages.LoginPage.Goto that it's gonna go to LoginPage of plan2finish.net website.
After that we are gonna create another method Pages.LoginPage.LoginUser that will receive a username and password and will insert those values in the username and password fields on the Login Page and push the Login Button.
At last we are gonna Assert that the credentials are valid by checking if we are in the MainPage and the user logged in successfully using the method Pages.MainPage.HasUserLoggedIn that receives the username.

We go ahead and add a new class Pages inside the TestFramework project.



We are gonna make this public and static. You'll find that as we are working on the framework, the way that I like to build the framework is not the way that you should build traditional code. What I mean by this, is that you typically wouldn't make everything static. But you'll find that there is this balance between simplifying the API and making the framework code itself subscribed to use some of the best practices that you would use in a regular application. A framework for testing it's a different type of application than any other type of applications. So you don't want try and build it as a standard application.

So now that we have the Pages class, we need a LoginPage class. We'll make this one static as well.

After creating the LoginPage class we need to create a Goto method inside the LoginPage class. We'll leave the implementation for later. Let's make sure we have our test class flashed out.


So we need to add a LoginUser method to LoginPage class and create a MainPage class with a HasUserLoggedIn method inside. All static.


We can now run our test. It will fail because we don't have any implementation in our methods. So next, we'll add the implementation to our methods.

Let's start by implement the Goto method and imagine a general implementation of a Browser. A general purpose browser. We could create a browser for each page, but if we did that we would be mixing the concepts a little bit. So again, I'm gonna drive this time from the framework to the deeper layers of the framework.

The simplest thing to do would be something like:



Which url would be a constant in our class. Now we need the Browser object implementation.
We'll make a static class called Browser with a Goto method. And the implementation of this Goto method is going to use Selenium. So let's give to our Browser class an instance of our webdriver and we'll set it to a new FirefoxDriver. This implementation could be more flexible to use different types of browsers, but in this case to make things simple let's use firefox only.


Now that we have the Goto implemented we need to implement the LoginUser method.
We should inspect login page in plan2finish.net and think about our page object model as we've mentioned before.


We want to have the login page model as it's own object and it should mirror the functionality that exists when the user is using this page. So for example, you can see two fields here, one for Email and other for Password. And there's also a button Login that we'll need.

So we're gonna start off by modeling the parts we need. So we can insert a email and a password and press the login button.

In order to do that we're gonna use the Page Object Model that's built in Selenium. We can't have a static page class anymore. We have to create an instantiation of the page where we can inject the information from the page and bind the elements we want to use. This will make a little bit more sense in a minute. But first we need to refactor our code a little bit.

So we need to change our LoginPage class so it can be instantiated instead of making it static.
The same with the methods inside the class.

The next thing we need to do is to implement the LoginPage in Pages as a property. Using only the getter, we don't need the setter here.


Going back to our LoginUser method, in order to login a user in the login page, we need to get those fields and button. We are gonna inspect the page and look for the better way to retrieve them.



We can get the email field, the password field and the login button by the tags ids.


  • Email: id="UserName"
  • Password: id="Password"
  • Login Button: id="LoginButton"


We can use the Page Object Model to do this. We will create a set of properties to bind to those elements in the page.


Then we will annotate those properties with FindsBy, and we are gonna say "How" = How.Id, it will find the element by id and "Using" the id name "UserName" for the Email field. We'll do the same for the password field and login button.

Now that we've done this, if we use the page factory that's built into the Selenium support, we can automatically go to the page and select the element according to this criteria and populate the properties with references to those elements. And we can use those binded properties in our tests.

This simplifies things quite a bit here. But in order to make this to work, we have to do something special in our LoginPage instantiation.



What we are gonna parse our page through this page factory. And we need the driver from our browser, so let's make sure we can access our driver from our Browser object.


We are initialising our LoginPage and passing it through this factory that would look for the attributes we annotated in our properties.

So now we can implement our LoginUser method in our LoginPage object.


We'll do something similar to our Main Page, the page where the user would be redirected if the credentials are correct. In the same way we'll send the main page to the page factory to be parsed.


And in the MainPage object we just have to implement the HasUserLoggedIn method:

In the Main Page, there would be a hidden field with the user name, and we'll check if our user is logged in by checking this field. But first we need to create a property to bind this hidden field to our class.


And that's it, we are ready to run our test now.



So let's take a minute now to think what we've done so far and where we might go from here. If we look at our unit test, we can see that we've made them in plain English, very easy to use. Notice we are not instantiating any objects here. It's all very simple. That's why we are using static methods so much. Even dough static methods might not be something that you would use in a traditional application.
We've basically driven the framework from the test. If you read this test, you should be able to understand exactly what's going on. It should be clear as possible.
The framework might be more difficult to build because we had to figure out ways to make the framework obey this API. Because the API came first.

Now if we look at the framework. You can see that we've divided things up by the different pages. This is the Page Object Model concept. We have a LoginPage and a MainPage.
Doing this really makes it easy to understand how your tests should be written. Because it's very intuitive. You can figure out that if there's a page in the system, there's probably gonna be a class in the framework that you can use. This is really important because in many instances the developers working in the Test Framework are not the same people that are writing the tests. You may have your QA team or someone that's not skilled in development skills writing tests. And if it's very simple and straight forward they can easily maintain those tests and it makes it a lot easier for them to figure out what they need to do or what kind of functionality they need in the framework. They can basically write their tests and then a developer that's working the Test Framework can implement the functionality that they want their test to be able to do.

That's the way I recommend working and that's why we should make our tests so simple.

Friday, May 16, 2014

Divide et impera - How to set up your Automation Framework

Let's look at a basic architecture example that you can use to create your Automation Framework.
If you look at this diagram here, looks fairly simple. But there're some very key things going on here.




The biggest thing that's happening is that your Test Scripts are depending on an Automation Framework and they're not depending directly on Selenium.

We are gonna have our Tests Scripts only depending on our Automation Framework that we are gonna create. And then the Automation Framework is gonna depend on Selenium.

Technically we could swap out the Selenium layer and replace it with a different type of Automation Tool besides Selenium. And just modify the connecting pieces from our Automation Framework and our Tests Scripts should still be able to run.



The goal of our Automation Framework is to support our Tests Scripts. You can also think about what concepts are related to this different layers here:

So, the Tests Scripts are gonna be focused on things like, pages in your applications, workflows. Things that are very domain specific to your application. It's actually going to share the same domain knowledge with your application.

At the Automation Framework layer we are gonna be focused on smaller pieces like the DOM (Document Object Model), things that exist in the browser. This is where we're going to be getting elements from the pages and we're going to be focused on how to manipulate pages and how to best navigate through the application.

Than at the Selenium layer we are at a very low level. It's focused on the actual HTML, the elements. It's parsing the pages. It's using the Browser API. It's at the very lowest level.

Thursday, May 15, 2014

Why do we need an Automation Framework?

Why should we create an automation framework?
Why all this work?
Why we just don't record our tests with Selenium IDE? This seems like an easy way to go!
Why do we have to write all this code?

There's a few reasons why recording doesn't work, but the main reason is:


Test Fragility


The major reason is Test Fragility. If we look at this simple diagram we can see that if you decided that you are just gonna record your tests and you won't gonna use some kind of a framework. Than your tests will directly depend on your application.



So what happen now, if you application changes in some way? Let say that the structure of the application changes significantly. Or even just minor little changes.

If you have a large number of tests and all those tests depend on a particular element on a page being named a certain way and that changes. All of those tests are suddenly going to break. And you will have to modify all that code in all those tests. That's really the big disadvantage of recording. Because recording is going to grab the elements by the most efficient way that it can find to grab the element instead of doing it in an abstracted way of grabbing that element. So if the element changes, as those tests break. You are copying a lot of code, when you are recording.


How can we reduce this fragility?


Now let's think about what happens, if we put an Automation Framework in place. The only modification I've made to this diagram was adding an abstraction layer. And that's what the Automation Framework is doing for you. It's primarily giving you an abstraction from the actually application.


The idea here, is that your test now depends on the Automation Framework. You are using the Automation Framework as an internal language for writing your tests. So since your tests depend on that Automation Framework. We run away from that scenario, if something changes in your application now, then you have just to update the method in your Automation Framework that manipulates that object in your application.

So let's imagine an id change, or a name of some field that you are using on an element change. Well hopefully, you've only just created one reference to it in your Automation Framework. You've abstracted the idea of how to grab that element or use that element. And so all those tests are dependent on the framework and so you've only have to change in one place. Because there's not that duplication of the dependencies on your application.

So that's the major reason why we don't want to record.