How to test a transaction method using JOOQ

Viewed 355

I'm trying to do some tests to see if my transactional methods are working fine. However I do not fully understand whether or not I should mock the database or not and how JOOQ comes into this equation. Below is the Service class with the transaction of adding a role into the databse.

    @Service
    public class RoleService implements GenericRepository<Role>
    {
        @Autowired
        private DSLContext roleDSLContext;
    
        @Override
        @Transactional
        public int add(Role roleEntry)
        {
            return roleDSLContext.insertInto(Tables.ROLE,
                            Tables.ROLE.NAME,
                            Tables.ROLE.DESCRIPTION,
                            Tables.ROLE.START_DATE,
                            Tables.ROLE.END_DATE,
                            Tables.ROLE.ID_RISK,
                            Tables.ROLE.ID_TYPE,
                            Tables.ROLE.ID_CONTAINER)
                    .values(roleEntry.getName(),
                            roleEntry.getDescription(),
                            roleEntry.getStartDate(),
                            roleEntry.getEndDate(),
                            roleEntry.getIdRisk(),
                            roleEntry.getIdType(),
                            roleEntry.getIdContainer())
                    .execute();
        }
    }

I'm using MySQL and the connection to the database is made using the spring config file

    spring.datasource.url=jdbc:mysql://localhost:3306/role_managementverifyServerCertificate=false&useSSL=true
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

I'm assuming I don't have to reconnect to the database everytime I'm testing the transaction and closing the connection after it finishes. I know that there is

MockDataProvider provider = new MockDataProvider()

but I don't understand how it works.

What is the best way to test the before mentioned method?

1 Answers

Disclaimer

Have you read the big disclaimer in the jOOQ manual regarding mocking of your database?

Disclaimer: The general idea of mocking a JDBC connection with this jOOQ API is to provide quick workarounds, injection points, etc. using a very simple JDBC abstraction. It is NOT RECOMMENDED to emulate an entire database (including complex state transitions, transactions, locking, etc.) using this mock API. Once you have this requirement, please consider using an actual database product instead for integration testing, rather than implementing your test database inside of a MockDataProvider.

It is very much recommended you use something like testcontainers to integration test your application, instead of implementing your own "database product" via the mock SPI of jOOQ (or any other means of mocking).

If you must mock

To answer your actual question, you can configure your DSLContext programmatically, e.g. using:

@Bean
public DSLContext getDSLContext() {
    if (testing)
        return // the mocking context
    else
        return // the actual context
}

Now inject some Spring profile value, or whatever, to the above configuration class containing that DSLContext bean configuration, and you're all set.

Alternatively, use constructor injection instead of field injection (there are many benefits to that)

@Service
public class RoleService implements GenericRepository<Role> {
    final DSLContext ctx;

    public RoleService(DSLContext ctx) {
        this.ctx = ctx;
    }

    // ...
}

So you can manually construct your service in the test that mocks the database:

RoleService testService = new RoleService(mockingContext);
testService.add(...);

But as you can see, the mocking is completely useless. Because what you want to test is that there's a side effect in your database (a record has been inserted), and to test that side effect, you'll want to query the database again, but unless you mock that as well, or re-implement an entire RDBMS, you won't see that record in the database. So, again, why not just integration test your code, instead?

Related