📜 ⬆️ ⬇️

Testing Spring applications. Transactions in Testing

spring-overview

About the usefulness of the TDD approach ( development through testing , test driven development ) did not hear only lazy or deaf. But today we will not discuss all its usefulness and beauty, as well as problems and disadvantages. Today we will try to see how to develop unit tests for spring applications. We will also touch on the manual management of transactions in unit tests.

A small note: sometimes spring application tests are not exactly unit tests, because we can raise and use very complex environments (DB, WebService, etc.) in them. Such tests are more integration tests , but I think that now we will not raise philosophical questions.

To begin with, I propose to harmonize some terms and concepts:

All other terms and concepts are standard and have long been established or their description is uncritical here. For example, such a thing as IoC ( inversion of control , inversion of control ).
')
I completely forgot, I’m not writing the first about writing unit tests for spring applications, I’m a little here and there .

So, we have a goal: to test the behavior of the class in the spring-application, in addition, you must manually manage transactions . To do this, we will create a simple spring application and write a unit test. Our unit-test at startup will initialize the application context config of our spring application and then call methods on the class we are testing. We will also develop a separate test in which we will manage transactions manually.

To begin, create an application that we will test. Technologies in the application will be as follows:
  1. Tool assembly and compilation - Apache Maven .
  2. Database - HSQLDB .
  3. The tool for mapping classes to the database (Object relation mapping) is Hibernate .
  4. Tool for configuring the application - Spring framework .
  5. Tool for creating unit tests - JUnit .

The final file structure in the application will look like this: structure
By the way, you can download it yourself (from SVN) and view the source code of the running application in the browser .

Let's start?

First , we need to create pom.xml in which we describe the build and compilation of the application (read about maven here or there ). In this configuration file, we prescribe all dependencies on the libraries we use. Also at this step, we will create all the directories of our application.

Second , we will create a java persistente entity class - ru.intr13.example.springTransactionalTest.Data . This class will describe the data model and with the help of the hibernate library it will be displayed in the database (a table will be automatically created in the database). Also in this step, we will create the hibernate.cfg.xml file, where we will link to the class we developed.

Third , we will create an interface - ru.intr13.example.springTransactionalTest.DataDao . In which we describe the main methods for working with our database. The checkpoint and shutdown methods are designed to work with hsqldb and their presence is connected with the peculiarity of the operation of this database (see details here ).

Fourth , we will create an implementation of the interface we developed - en.intr13.example.springTransactionalTest.DataHibernateDao . Where we implement all the methods described in the DataDao interface. It is worth noting that this class is inherited from the class org.springframework.orm.hibernate3.support.HibernateDaoSupport, which in turn is part of the Spring Dao library and already has methods for convenient work with the database.

Fifth , we will create an application context config file to configure our application. In which we describe:

In the sixth , we will create a test application that initializes the application context config and works a bit with the service we developed. The results of the work are saved in our local database, which can be observed in the data / test.db.script file (this file contains the data of our database):
CREATE SCHEMA PUBLIC AUTHORIZATION DBA
CREATE MEMORY TABLE DATA (ID BIGINT NOT NULL PRIMARY KEY ,TEXT VARCHAR (255))
CREATE MEMORY TABLE HIBERNATE_SEQUENCES(SEQUENCE_NAME VARCHAR (255),SEQUENCE_NEXT_HI_VALUE INTEGER )
CREATE USER SA PASSWORD ""
GRANT DBA TO SA
SET WRITE_DELAY 10
SET SCHEMA PUBLIC
INSERT INTO DATA VALUES (1, 'one' )
INSERT INTO DATA VALUES (2, 'two' )
INSERT INTO DATA VALUES (3, 'three' )
INSERT INTO HIBERNATE_SEQUENCES VALUES ( 'Data' ,1)

So, the test application has been created and now we need to develop a unit test . To do this, we create a class ru.intr13.example.springTransactionalTest.DataDaoTest . This class is derived from the org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests class, which allows you to run the application context config when running tests. This is done using annotations for the test class:
@ContextConfiguration(locations = { "/applicationContext.xml" }).

But for full testing, we need our test to have the opportunity to get a service developed by us. To do this, we register in our test field with reference to the service and set the annotation for this field - @Autowired:
@Autowired
private DataDao dataDao;

Now when you run the tests in this field will be set a link to the previously developed service.

And in the end it remains to write the text of the unit test:
@Test
public void simpleTest() {
String text = UUID.randomUUID().toString();
dataDao.save( new Data(text));
Collection result = dataDao.find(text);
Assert.assertEquals(1, result.size());
Assert.assertEquals(text, result.iterator().next().getText());
}

This test creates a Data object, stores it in a database, and then searches for a Data object by its contents.

I think there is nothing particularly difficult in the above, and this can even be used, but sometimes in such tests it is required to organize manual management of transactions (that is, declaratively roll back or save the transaction, start a new transaction). How to do this is described here , but now we consider a small example.

To manually manage transactions in tests, we need to get the transaction manager described in the application context config (transactionManager), which is done by creating a field in our test:
@Autowired
private PlatformTransactionManager transactionManager;

Next, we simply create a new transaction:
TransactionStatus transaction = transactionManager.getTransaction( new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW));

and then we commit to it (save):
transactionManager.commit(transaction);

or rollback:
transactionManager.rollback(transaction);

Knowing all of the above, we can write the following test, which creates several transactions manually:
@Test
public void comlexTest() {
TransactionStatus transaction = transactionManager.getTransaction( new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW));
String text = UUID.randomUUID().toString();
dataDao.save( new Data(text));
transactionManager.commit(transaction);
transaction = transactionManager.getTransaction( new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW));
Collection result = dataDao.find(text);
Assert.assertEquals(1, result.size());
Assert.assertEquals(text, result.iterator().next().getText());
transactionManager.rollback(transaction);
}

The final version of the test can be viewed here .

Total: we created a test application based on the spring framework (the source code lies here ). A way of testing individual services in the spring framework was demonstrated. It also showed how to manually manage transactions in unit tests. As a result, we saw that creating simple unit tests for the spring framework is a fairly simple task. The question of the appropriateness and the need to develop such tests was not considered, this is a topic for a separate conversation.

p / s
This is an edited version of a post from my personal blog . Do not count for advertising :)

p / s / s
Picture found here . By the way, an attentive person will notice one funny thing :)

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


All Articles