How do we write unit tests for methods related to connecting to the database?

I had a question about writing unit tests for web methods that actually communicate with the database and return some value.

Say, for example, I have a web service called "StudentInfoService". This web serialization provides the API "getStudentInfo (studentid)"

Here is an example fragment

public class StudentInfoService { public StudentInfo getStudentInfo(long studentId) { //Communicates with DB and creates // StudentInfo object with necessary information // and returns it to the caller. } } 

How do we actually write unit tests for this getStudentInfo method? As a rule, we write unit tests for methods that are associated with connecting to a resource (Database, Files, JNDI, etc.)?

+6
source share
4 answers

First, the StudentInfoService class in your example is not amenable to testing, or at least not easy. This is for a very simple reason - there is no way to pass a database connection object to a class, at least not in the method you specify.

To make the class testable, you need to create your own class as follows:

 public class StudentInfoService { private Connection conn; public StudentInfoService(Connection conn) { this.conn = conn; } public StudentInfo getStudentInfo(long studentId) { //Uses the conn object to communicate with DB and creates // StudentInfo object with necessary information // and returns it to the caller. } } 

The above code allows dependency injection through the constructor. You can use installer injection instead of constructor injection if it is more suitable, but usually this is not for DAO / Repository classes, since the class cannot be considered fully formed without a connection.

Dependency injection will allow your test cases to create a connection to the database (which is an employee of your tested class / system) instead of the class / system itself creating collaborator objects. In simpler words, you untie the database connection mechanism from your class. If your class previously looked at the JNDI data source and then created the connection, then it would be unstable if you hadn't deployed it to the container using Apache Cactus or a similar structure like Arquillian , or if you used the built-in container. Having identified the problem of creating a connection from a class, you can now create connections in your unit tests outside the class and provide them to the class as needed, which allows you to run tests inside the Java SE environment.

This will allow you to use a database-oriented database such as DbUnit , which will allow you to configure the database to a known state before each test, then it is passed in conjunction with the StudentInfoService class, and then assert the class state (as well as co-author, etc. e. database) after the test.

It should be emphasized that when you unit test your classes, your classes should be the only systems tested. Elements, such as connections and data sources, are simply employees who can and should be bullied. Some unit tests would use databases such as H2 , HSQL , or Derby for unit tests and use production equivalent database setups for integration and functional testing.

+4
source

Try using http://www.dbunit.org/intro.html .

The main idea is to create a stub database with a known dataset to run your tests and validate the results. You will need to reload the dataset before starting to restore it to its original state.

+2
source

We use the HSQL database in memory. It is very fast and compatible with SQL-92. To make our PostgreSQL queries run in HSQL, we rewrite the queries using the SessionFactory (Hibernate) self-recording test. Benefits for a real database:

  • much faster, which is important for unit tests
  • no configuration required
  • works everywhere including our continuous integration server
+1
source

When working with "legacy code", it can be difficult to write unit tests without some level of refactoring. When recording objects, I try to stick with SOLID . As part of SOLID, ā€œDā€ means inverse dependency.

The problem with legacy code is that you can have many clients that use the no arg StudentInfoService , which can make it difficult to add a constructor that accepts the Connection conn parameter.

What I would like to offer is usually not the best practice, since you are exposing test code in your production system, but sometimes it is optimal for working with outdated code.

 public class StudentInfoService { private final Connection conn; /** * This no arg constructor will automatically establish a connection for you. This * will remain around to support legacy code that depends on a no arg constructor. */ public StudentInfoService() throws Exception { conn = new ConcreteConnectionObject( ... ); } /** * This constructor may be used by your unit tests (or new code). */ public StudentInfoService( Connection conn ) { this.conn = conn; } public StudentInfo getStudentInfo() { // this method will need to be slightly refactored to use // the class variable "conn" instead of establishing its own connection inline. } } 
0
source

Source: https://habr.com/ru/post/892933/


All Articles