When a test fails, the most important thing is knowing what went wrong so you can easily come up with a fix. The best way to know what went wrong is to keep three words in mind when designing your tests: Small, Atomic, and Autonomous.
Small refers to the idea that your tests should be short and succinct. If you have a test suite of 100 tests running concurrently on 100 VMs, then the time it will take to run the entire suite will be determined by the longest/slowest test case. Keeping your tests small ensures that your suite will run efficiently and provide you with results faster.
An atomic test is one that focuses on testing a single feature, and which makes clear exactly what it is that you're testing. If the test fails, then you should also have a very clear idea of what needs to be fixed.
An autonomous test is one that runs completely independently of other tests, and is not dependent on the results of one test to run successfully. In addition, an autonomous test should use its own data to test against, and not create potential conflicts with other tests over the same data.
Text
Problem - You want to avoid duplicated code when several tests share the same initialization and cleanup code.
When a setUp() method is defined, the test runner will run that method prior to each test. Likewise, if a tearDown() method is defined, the test runner will invoke that method after each test.
"Prerequisite" tasks that need to be taken care of before your test runs, you should include a setup section in your script that executes them before the actual testing begins. For example, you may need to to log in to the application, or dismiss an introductory dialog that pops up before getting into the application functionality that you want to test.
Benefit - avoids code duplication, makes code more readable and maintainable
| Feature | JUnit | TestNg |
|---|---|---|
| run before each @Test method | @Before | @BeforeMethod |
| run after each @Test method | @After | @AfterMethod |
| run before the first test method in the current class is invoked | @BeforeClass | @BeforeClass |
| run after all the test methods in the current class have been run | @AfterClass | @AfterClass |
| run before all tests in this suite have run | - | @BeforeSuite |
| run after all tests in this suite have run | - | @AfterSuite |
...this way test class only has testing code without any preparation and teardown code
...and Test class should extend the BaseTest class...
Incorrect Approach
Correct Approach
Dependencies between tests prevent tests from being able to run in parallel. And running tests in parallel is by far the best way to speed up the execution of your entire test suite. It's much easier to add a virtual machine than to try to figure out how to squeeze out another second of performance from a single test.
NOTE - If you still want to use implicit waiting, then do not mix implicit and explicit waits. Doing so can cause unpredictable wait times. For example setting an implicit wait of 10 seconds and an explicit wait of 15 seconds, could cause a timeout to occur after 20 seconds.
Explicit wait:
documented and defined behaviour.
runs in the local part of selenium (in the language of your code).
works on any condition you can think of.
returns either success or timeout error.
can define absence of element as success condition.
can customize delay between retries and exceptions to ignore
undocumented and practically undefined behaviour.
runs in the remote part of selenium (the part controlling the browser).
only works on find element(s) methods.
returns either element found or (after timeout) not found.
if checking for absence of element must always wait until timeout.
cannot be customized other than global timeout.
Instead of:
By.xpath("//tr/td[3]/div/div[text()='Some Text']"
Just use :
By.xpath("//*[text()='Some Text']"
Benefits of using Page Object pattern
Clear separation between test code and navigation code in code base.
Makes tests more readable
Ease of maintenance