Unobvious problem of using assert

Unit tests are an important part of any large enough project. I want to share with you a small detective story related to their unobvious massive fall.

It begins with the fact that in the project, as a result of a certain innocuous commit, about 150 tests fell, while the set of falling tests was not stable. The tests were not interconnected, the tests were performed sequentially. The in-memory h2 database serves as a data source for tests. The fall of the overwhelming majority of these 150 tests was accompanied by an error in the log: “Cannot get a connection, pool timeout waiting for idle object”. It should be noted that the connection pool size when running tests in a project is 1.

A small lyrical digression: in the project code, the transaction is always interchanged from the flow, then the code is executed in a separate transaction, and finally, the transaction is back coupled. For such cases, a helper class is written, the use of which looks like this:

TransactionRunner.run(dbDataManager(), new MethodTransaction() {
           @Overridepublic ExecutionResult runInTransaction()throws Exception {
                // код, который необходимо выполнить в отдельной транзакцииreturn result;
          } 
);

As a result of the analysis, it was revealed that the error begins to manifest itself after a failed test that contains the invocation of code in the transaction:

TransactionRunner.run(dbDataManager(), new MethodTransaction() {
           @Overridepublic ExecutionResult runInTransaction()throws Exception {
                // ... рабочий код// assert который валитсяassert( 1, result.getSomeNotEqualOneIntValue() );
               return result;
          } 
);

Let's look inside the TransactionRunner class, calling the method results in the following code:

protected ExecutionResult run()throws CommonException {
        Transaction outerTr = getThreadTransaction();
        bindThreadTransaction(null);
        try {
            beginTransaction();
            try {
                setResult(transactionCode.runInTransaction());
            } catch (Exception e) {
                dbDataManager().rollbackTransaction();
                if (transaction.onException(this, e))
                    throw e;
            } 
            dbDataManager().commitTransaction();
            return getResult();
        } catch (Exception e) {
            throw ExceptionUtil.createCommonException(e);
        } finally {
            bindThreadTransaction(outerTr);
        }
    }

So what is the problem here? And the problem is that the AssertionError resulting from the execution of the test code is not inherited from Exception, which means that the nested transaction is neither rolled back nor committed. Since the size of the connection pool is one, we get that same error “Cannot get a connection,” when trying to get a Connection object with subsequent tests.

Moral: it is necessary to place assertions in tests with caution, and in the case of unobvious and, especially, massive crashes, one of the solutions is to check whether objects not inherited from Exception take into account exception handling.

The case seemed to me worthy of fixation, perhaps this experience will be useful to someone.

Also popular now: