Test automation framework implementation using Selenide and JUnit
Probably the most popular architecture pattern used for test automation frameworks is Layered (also known as N-Tier or Tiered) architecture. This pattern is so well known that on job interviews for some companies when they ask you about Test automation framework architecture you are supposed to describe this one. If you don’t — they think you know nothing about the architecture altogether.
I suggest first reading brilliant description of the pattern at the O.REILLY web page, cause in this post I am going to describe the pattern in a way it is usually applied to build test automation solution.

Let’s consider an example of what Layered (Tiered) architecture may look like for a test automation framework. As a system under test we’re going to use this simple and neat “TODO list” application:

First let’s do a brief analysis of the application. From my brief exploration I came up with the following check list:
Please notice, that I did checklist first — thus I make test scenarios to drive my framework implementation, not vice versa. I think it is important premise as our final product are tests. Let us call it “intent driven test automation design” (or choose/suggest any other meaningful name).
Typical anti-pattern would be to start working on PageObjects first, then on Service/Step layers and only after that on test scenarios. Thus you usually get ugly and unreadable scenarios.
Typical layered framework is divided into three layers — Web (contains Page objects ), Business (contains business abstractions and exposing “steps” abstraction above) and Tests themselves (check this short post for additional info). For a record I don’t like thinking about Tests as a layer of framework (rather it is a layer of a solution, please check this) but common vocabulary differs from mine.
Traditional three layered framework/solution
Most test automation frameworks I worked with were “plain code” frameworks, where test scenarios are done using some XUnit library as pure test methods, resembling typical unit tests. The example I have created uses this approach as well, with major difference — I inversed dependency so my lower level layers depend on a higher layer (this is inspired by Clean Architecture by Uncle Bob and Hexagonal Architecture pattern).

Mapping between layers and code itself:
Test layer
/src/main/java/gmail/alexspush/test
/src/test/java/gmail/alexspush/test
Business (Steps/Service) Layer
/src/main/java/gmail/alexspush/service
Core Layer
/src/main/java/gmail/alexspush/utils
/src/main/java/gmail/alexspush/driver
/src/main/java/gmail/alexspush/page
All in all, this is a good enough solution. Tests are descriptive and tell the reader what they’re testing (instead of how they do it). Of course as you add test scenarios one should be very careful to preserve this communication, but still good enough already. The weakest point of this solution is a standard reporting, but it can be improved using external libraries (or ignored if you run tests often enough so you know source of failure without reporting)
Here’s how a test method would look like:
Github link: https://github.com/senpay/layered-test-framework-example
PS
Did you enjoy this post? I think you might also find this video useful: https://www.youtube.com/watch?v=RVel7MZaSA0
It describes: 3 rules of test-driven development, TDD benefits, TDD drawbacks