Ein offensichtliches Problem bei der Verwendung von assert

Unit-Tests sind ein wichtiger Bestandteil jedes ausreichend großen Projekts. Ich möchte Ihnen eine kleine Detektivgeschichte erzählen, die sich auf ihren nicht offensichtlichen Massensturz bezieht.

Es beginnt mit der Tatsache, dass infolge eines bestimmten harmlosen Commits etwa 150 Tests in das Projekt fielen, während die Anzahl der fallenden Tests nicht stabil war. Die Tests waren nicht miteinander verbunden, die Tests wurden nacheinander durchgeführt. Die speicherinterne h2-Datenbank wird als Datenquelle für Tests verwendet. Der Sturz der überwiegenden Mehrheit dieser 150 Tests ging mit einem Fehler im Protokoll einher: „Verbindung kann nicht hergestellt werden, Poolfehler Timeout wartet auf inaktives Objekt“. Es sollte gesagt werden, dass die Größe des Verbindungspools bei der Durchführung von Tests im Projekt 1 beträgt.

Ein kleiner lyrischer Exkurs: Im Projektcode wird die Transaktion regelmäßig vom Ablauf getrennt, dann wird der Code in einer separaten Transaktion ausgeführt und schließlich wird die Transaktion rückverknüpft. Für solche Fälle wurde eine Hilfsklasse geschrieben, deren Verwendung ungefähr so ​​aussieht:

TransactionRunner.run(dbDataManager(), new MethodTransaction() { @Override public ExecutionResult runInTransaction() throws Exception { // ,       return result; } ); 

Als Ergebnis der Analyse wurde festgestellt, dass der Fehler nach einem fehlgeschlagenen Test auftritt, der einen Codeaufruf in einer Transaktion enthält:

 TransactionRunner.run(dbDataManager(), new MethodTransaction() { @Override public ExecutionResult runInTransaction() throws Exception { // ...   // assert   assert( 1, result.getSomeNotEqualOneIntValue() ); return result; } ); 

Werfen Sie einen Blick in die TransactionRunner-Klasse. Ein Methodenaufruf führt zu folgendem 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); } } 

Was ist hier das Problem? Das Problem besteht darin, dass der AssertionError, der als Ergebnis der Testcodeausführung auftritt, nicht von Exception geerbt wird. Dies bedeutet, dass die verschachtelte Transaktion weder zurückgesetzt noch festgeschrieben wird. Da die Größe des Verbindungspools gleich eins ist, wird der gleiche Fehler "Verbindung kann nicht hergestellt werden, Poolfehler Timeout wartet auf inaktives Objekt" angezeigt, wenn versucht wird, das Verbindungsobjekt durch nachfolgende Tests abzurufen.

Moral: Es ist notwendig, Assertions mit Vorsicht in Tests zu platzieren. Bei nicht offensichtlichen und insbesondere Massenabstürzen besteht eine Lösung darin, zu überprüfen, ob bei der Ausnahmebehandlung Objekte berücksichtigt werden, die nicht von Exception geerbt wurden.

Der Fall schien mir der Fixierung wert zu sein, vielleicht ist diese Erfahrung für jemanden nützlich.

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


All Articles