When i look at a test class, what i want to see is clean code. What i mean by that is, well a few things, but the most important one: i want the test class to hold the code for the tests, not the code for everything but the kitchen sink.
When we write tests we have a lot of data to prepare for them. Whether this is the ‘expected’ or the ‘actual’ data used in the tests, or some auxiliary code that we need, there always is some processing that needs to be done, apart from the actual asserts that a test should do. What the test class should contain is only the checking / asserting part, while having specialized classes generate all the data that is required in the test. A test class should only check the actual data against the expected data. This is the separation of concerns principle.
A concern can be seen as a task that needs to be done. In this case, a concern is that you need to check the correctness of data. Another concern can be the data generation for the actual tests. You can break the latter down into, for example: the work needed to generate database related data, then the work for the ui related data, and so on.
This principle, applied to your automation testing, indicates that you need to have dedicated classes for specific kinds of tasks. One very good example in this case is the Page Object class you write for your Selenium tests. This class is dedicated to storing your webElement definitions, and some operations you will commonly use to interact with them. These classes are not meant to hold things like: string processing or database processing. Their sole purpose is to handle the webElement concern of your tests.
Consider an example of a test: you will write a Selenium test that requires to compare some data that is shown to the user to some data that the DB stores for that user. In this case, you will need to process the data from the DB.
The first step would be for you to connect to the DB (therefore define the credentials needed for connecting), and then performing the actual connection. Once this is done, you need to define the queries you will run against the DB. The results of the queries might need processing, so that is an extra step you will need to perform.
Now, think about all the steps you need for interacting with the database. And consider that you have several test classes that need to interact with the same database, maybe extract and process the same data. By not using the separation of concerns principle, you might end up performing the same steps in several classes (basically duplicating the code throughout your classes).
You should consider moving all the DB related code to a specialized class, that only works with the DB. In this class you will define all the setup part (for connecting), and then you will have methods that are specific for the kind of data processing you need. You can even think about this further: suppose you have several types of data queries, that are quite specific. You can have several classes that deal with the database processing, each dealing with a clearly defined type of processing. Maybe it makes sense to group the classes based on the tables they are using queries against, or maybe on the type of data they are processing.
Another example would be if you would need to store a bunch load of strings to use as the expected data for your tests. Maybe even in different languages. You don’t want to add all the strings directly into the test. You don’t want to read hundreds of lines of code until you reach the actual test part. Therefore, you can just move the strings to their own class. In this case, either just the one class, or one for each language you are interested in.
To visualize a bit the different approaches (one with all the code in the test class, and one with separation of concerns), here are two simple diagrams to consider.
The first diagram shows the test class with all the work done within its code:
The second diagram shows an example of how to break up the single class into classes focused on different concerns:
The benefits for this approach:
- no duplication of code
- you will know exactly where to find code that deals with specific tasks
- your tests will be shorter – a class should not have over 2000 lines of code
- your tests will be cleaner, easier to understand