How to Build an E2E Testing Framework Using Design Patterns
End to End or E2E testing is about simulating the consumer’s expertise. It would not take care of features, variables, courses, or databases. Instead, it offers with buttons, clicks, anticipated messages, hyperlinks, and so forth.
You may say that E2E testing is the “ultimate” testing because it checks whether or not the product as a complete behaves as anticipated.
In normal, E2E testing is tough to automate. First of all, you want instruments that may work together with the appliance that’s being examined – fill out kinds, watch for a web page to load fully, that sort of stuff.
You additionally want to get the outcomes from the consumer interface. You do not have features returning objects however HTML parts containing the data. Mocking an actual consumer could be difficult and may require a variety of upkeep.
In this text, I’ll speak about my very own expertise constructing an E2E testing framework. I utilized some cool Design Patterns so I believe this may very well be fascinating for you even if in case you have nothing to do with E2E testing automation.
This put up is language and gear agnostic. This signifies that I will not refer to a selected programming language or a selected E2E software like Selenium, Puppeteer, or Playwright. By the best way, these are nice instruments for automatizing E2E exams. Also, this put up focuses on E2E testing for web sites.
The drawback I had to resolve
I had to design a framework to carry out totally different E2E exams on totally different web sites. More exactly, I wanted to make some exams over particular React parts inside these web sites.
Every element had the identical construction and CSS selectors regardless of the web site and simply modified barely from one website to one other. I wanted to make exams for each potential viewport (cellular, pill, and desktop), and the parts had to change their construction when the viewport modified.
In this state of affairs, I knew nothing concerning the builders. So I wanted to be ready to handle some unexpected modifications within the interface comparatively simple. In different phrases, it was vital that the framework be simple to keep.
So how was I supposed to make an E2E testing framework that did not care an excessive amount of whether or not the builders modified the id attribute of some button that was clicked in some take a look at? How might I write exams for some element that was not created but? And how might I make each take a look at simple to learn and perceive?
I used to be in a position to obtain all these objectives by making use of some abstractions and design patterns. So let’s examine how I did it.
The Page Object Model
The very first thing we want to do is to create an abstraction for a web page. This is essential for a number of causes.
First, it would improve readability. For instance, you don’t need to have a line in your take a look at that reads
software.getByCssSelector("button.btn.btn-submit").click on(). Instead you need to have a line like this one:
web page.clickSubmitLoginFormButton() or one thing related.
You additionally want to hold all of the CSS selectors and DOM-related stuff in a single place. This approach, when one thing within the interface modifications you solely want to modify one single file (or possibly two, however no more 😉 ).
That abstraction is known as the Page Object Model. You make a category that represents solely the weather that you’re inquisitive about from the web page. You put all of the DOM-related stuff in these courses.
In my case, I did it barely in another way. I created two courses for each web page, a PageModel and a Page Object.
In the primary one, I put the weather of the web page. For instance, suppose we’re testing a login web page, then my LoginPageModel can be like this:
class LoginPageModel constructor(software) this.software = software loginUsernameInput() return this.software.getById('username-input') loginPasswordInput() return this.software.getById('password-input) loginSubmitButton() return this.software.getById('submit-login-button')
If any of these parts change sooner or later we solely want to modify the corresponding PageModel class.
In the PageObject class, I add the actions you can carry out on the web page. An instance of a LoginPageObject class can be:
class LoginPageObject constructor(web pageModel) this.mannequin = web pageModel typeUsername(username) this.mannequin.loginUsernameInput().kind(username) typePassword(password) this.mannequin.loginPasswordInput().kind(password) click onLoginSubmitButton() this.mannequin.loginSubmitButton().click on()
Here we are able to make the most of a statically typed language that may get all of the strategies of the mannequin class in compilation time. That approach some IntelliSense software can remind us the title of each methodology that represents a web page factor.
We additionally get extra compilation errors and fewer runtime errors, which is excellent for us and our psychological well being.
Why do we want to separate web page parts from web page actions? A single class that comprises each the weather and the actions could be very massive.
We can say that by doing this we’re making use of the Single Responsibility Principle and that may be cool. But on this case, that does not have a lot sensible significance past readability and maintaining courses easy.
With the Page Object abstraction we are able to make exams that solely rely on web page objects as an alternative of writing some difficult CSS selectors in the course of the take a look at code.
We hold all of the DOM-related stuff in a single place and our exams could be extra expressive and simple to perceive.
Writing exams – the Facade Pattern
Now we’ve many courses that include all the weather and actions of a number of pages. What we want to do now’s to construct our exams.
These exams will present a easy interface that exposes the
run performance to the consumer. This performance returns a take a look at consequence.
The consumer would not have to fear about accessing any factor or doing any motion, it simply wants to instantiate the take a look at and run it.
When we offer a easy interface that hides a extra advanced infrastructure we’re making use of the Facade Pattern. I do know that is solely a elaborate title for one thing it is clear that we wanted to do.
Continuing with our Login Page take a look at instance, the LoginCheck can be one thing like this:
class LoginCheck constructor(loginPageObject) this.web pageObject = loginPageObject run() this.web pageObject.typeUsername("TestUser") this.web pageObject.typePassword("TestPassword") this.web pageObject.click onLoginSubmitButton() assert that the login was profitable
The final line of the
run methodology is an assertion. Depending on the complexity of the assertions you employ, you’ll be able to both outline them individually or contained in the Page Object.
By selecting the primary possibility you’ll be able to reuse and lengthen assertions. But in case your assertions are very particular for every case and easy sufficient, the primary possibility could be overkill and you’ll in all probability be good with the second.
We are additionally injecting the Page Object dependency within the take a look at. We will not be doing
this.web pageObject = new LoginPageObject() however receiving the dependency as an argument within the constructor. This is known as Dependency Injection. That approach, we are able to instantiate the identical take a look at for one more web page.
We additionally inject the Page Model in Page Object situations. Then, we are able to have the identical Page Object with one other mannequin (Example: similar LoginPageObject occasion with a LoginCellPageModel as an alternative of a daily LoginPageModel).
But now, to instantiate a take a look at, we want to instantiate a number of Page Models, then a number of Page Objects, and at last the take a look at. This looks like an excessive amount of work. That’s exactly one of many drawbacks of utilizing Dependency Injection – however the issue is solvable!
The Factory Pattern
Let’s delegate the duty to one other abstraction. In this case, we’ll make some factories.
Factories are courses which are used to instantiate different courses. Every manufacturing facility class will probably be chargeable for instantiating a selected take a look at. That’s the Factory Pattern in motion.
So we are able to create a LoginCheckFactory for our LoginCheck:
import software class LoginCheckFactory create(config) if config.viewport == 'cellular' then return new LoginCheck(new LoginPageObject(new LoginCellPageModel(software))) else return new LoginCheck(new LoginPageObject(new LoginPageModel(software)))
software we’re representing any potential expertise you would use to get the weather of a web page and work together with them.
Maybe you do not go the imported software as is, however you create some objects utilizing that software after which go these objects as parameters.
But the thought is that each one the comparatively advanced logic to make an occasion of a take a look at is encapsulated in a manufacturing facility object.
To run our take a look at we solely want to do one thing like this:
runLoginTestDesktop() manufacturing facility = new LoginCheckFactory() config = new ConfigObject(viewport = 'desktop') take a look at = manufacturing facility.create(config) take a look at.run() runLoginTestMobile() manufacturing facility = new LoginCheckFactory() config = new ConfigObject(viewport = 'cellular') take a look at = manufacturing facility.create(config) take a look at.run()
Now, within the conclusions part, we’ll test whether or not we’ve achieved our preliminary objectives
Building your testing framework like this could dramatically lower the price of modifications in a consumer interface. All the code that depends upon the consumer interface is remoted in particular courses that summary the idea of a web page.
That abstraction additionally permits you to write your exams for the subsequent week. (I imply the exams for parts that haven’t been created but.) You simply make the required new PageModels and PageObjects to mock the weather on the web page that will probably be created and you may construct the remainder of the method in the identical approach we’ve seen up to now.
When you could have particular parts on the interface you’ll be able to change the web page fashions and confirm whether or not the appliance behaves as anticipated.
You even have exams which are very simple to learn and perceive because you make expressive actions like
this.web pageObject.click onLoginSubmitButton(). Thus, your exams can describe the necessities of your software and could be simply maintained.
E2E testing automation is tough as a result of it is laborious to hold it easy. And a fancy take a look at just isn’t a take a look at.
In this put up, I’ve proven some design patterns and good practices you should utilize to make it smoother. I’ve tried to make it language and gear agnostic so you’ll be able to apply these practices in your challenge it doesn’t matter what language or expertise you might be utilizing. I solely assumed an Object-Oriented programming language.
Whether or not you are making an E2E testing framework, I believe this text can nonetheless be of use to you. Some of those methods could be utilized in a comparatively huge number of issues.