Better manage the Explicit Waits with Selenium WebDriver
August 21, 2020Avoid the retry strategy to make your tests reliable
September 21, 2020This post belongs to the How to Create Lean Test Automation Architecture for Web using Java series. If you didn’t see the previous posts, please check them out!
What is the Base Test Class?
It is a simple approach to set up common initialization and cleanup in your tests. We commonly categorize it as a Testing pattern.
The problem we are trying to solve here is the reuse of common behaviors across test classes avoiding coding duplication and centralizing these actions at one point.
The application of a Base test class, in OO programming languages, is applied by the use of inheritance.
Example without a Base Test Class
Let’s say you need to open the browser as a pre-condition and close it as a postcondition. Any test you create will have it, creating a code duplication.
public class FirstTest {
private static WebDriver driver;
@BeforeAll
static void webdrivermanagerSetup() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.get("https://eliasnogueira.com");
}
@AfterAll
static void quitBrowser() {
driver.quit();
}
@Test
void firstTest() {
// the test goes here
}
}
public class SecondTest {
private static WebDriver driver;
@BeforeAll
static void webdrivermanagerSetup() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.get("https://eliasnogueira.com");
}
@AfterAll
static void quitBrowser() {
driver.quit();
}
@Test
void secondTest() {
// the test goes here
}
}
Can you see in both test classes we have:
- On lines 5 to 10 a pre-condition creating a new Google Chrome browser instance
- On line 14 a postcondition closing the Google Chrome browser instance
Both annotations were provided by JUnit 5.
The @BeforeAll
execute the code inside the method before all the @Test
methods before being executed.
The @AfterAll
execute the code inside the method after all the @Test
methods being executed.
As you can see we are duplication code, and it is a bad practice that neither scale your test nor add less maintenance to it.
Example with a Base Test Class
Class Implementation
The class below is the Base test class implementation. You can notice the following:
- On line 1, you can see that the class is abstract, so we won’t be able to instantiate it, only extend it.
- On lines 5 to 10, you can see the test pre-condition we have used in the previous tests.
- On lines 12 to 15, you can see the test postcondition we have used in the previous tests.
public abstract class BaseWeb {
protected static WebDriver driver;
@BeforeAll
static void webdrivermanagerSetup() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.get("https://eliasnogueira.com");
}
@AfterAll
static void quitBrowser() {
driver.quit();
}
}
As you can see we are avoiding code duplication and adding the pre and post-condition in a single class, so it will be a single point of change.
Test implementation
I have removed, in both tests, the pre and post-condition, but they will continue to run because I’m extending the test class by the BaseWeb
(the Base Test Class).
How it will be executed automatically?
Because we are using an annotation provided by JUnit 5. So, before the @Test
can run the pre-condition will be executed because it’s inherited from the BaseWeb
class. The same will happen with the postcondition.
public class FirstTest extends BaseWeb {
@Test
void firstTest() {
// the test goes here
}
}
public class SecondTest extends BaseWeb {
@Test
void secondTest() {
// the test goes here
}
}
Tips and more explanations
Why the class should be abstract?
You cannot instantiate an abstract class. So, we can declare methods with or without implementation and define fields that are not static and final. So abstract classes will provide:
- A way to share code among classes that need the same behavior
- Need to use the same objects in the parent classes
- Need to modify the state of a shared object
We will not use abstract methods as, per definition, we must implement them in the parent class. We are going to write abstract methods with implementation to be able to override them if you want, giving you more flexibility.
Take advantage of initialization and cleanup methods from unit test frameworks
As you can see I used the pre and postcondition approach from JUnit 5. You can take advantage of these features to write a clean and maintainable test.
You can learn the pre and post-conditions implementations from:
- JUnit 5: https://junit.org/junit5/docs/current/user-guide/#writing-tests-classes-and-methods
- TestNG: https://testng.org/doc/documentation-main.html#annotations
I don’t need to use pre or postcondition (one or another)
If you need, by any chance, not to execute the pre or postcondition (in the scenario you need to use the Base Test Class) you can simply override the method removing the super reference, making it empty, or adding your custom implementation just for that test.
public class FirstTest extends BaseWeb {
@AfterAll
@Override
public void quitBrowser() {
// empty or add your implementation
}
@Test
void firstTest() {
// the test goes here
}
}
But bear in mind if you need to do it in many test classes you might end up with a new Base Test Class, and it’s ok. I never saw different ones for a web test approach, but I’m using different ones for the API tests.
3 Comments
Hi Elias
I am begineer in Automation testing .i really loved your article.Thanks for sharing such valuable information.
Thanks
Hi Elias, thank you for the post. Just a qq: What if I need additional pre and post conditions in the test classes (that extend the WebBase class)?
Hi!
If you are using a BaseTest class and need a pre/post condition, additionally, in the test, you can have it.
The order of execution will be the precondition from the BaseTest class first, then the precondition from the test class, then postcondition from the test class and finally the postcondition from the BaseTest class.
Thank you for this question. I will create an extra blog post about it 🙂