Un problème évident avec l'utilisation de l'assertion

Les tests unitaires sont une partie importante de tout projet suffisamment grand. Je veux partager avec vous une petite histoire de détective liée à leur chute de masse non évidente.

Cela commence par le fait qu'à la suite d'un certain engagement inoffensif, environ 150 tests sont tombés dans le projet, alors que l'ensemble des tests de chute n'était pas stable. Les tests n'étaient pas interconnectés, les tests ont été effectués séquentiellement. La base de données h2 en mémoire est utilisée comme source de données pour les tests. La chute de la grande majorité de ces 150 tests s'est accompagnée d'une erreur dans le journal: "Impossible d'obtenir une connexion, erreur de pool Timeout en attente d'un objet inactif". Il convient de noter que la taille du pool de connexions lors de l'exécution de tests dans le projet est de 1.

Une petite digression lyrique: dans le code du projet, la transaction est déconnectée périodiquement du flux, puis le code est exécuté dans une transaction distincte et, enfin, la transaction est rétrogradée. Pour de tels cas, une classe d'assistance est écrite, dont l'utilisation ressemble à ceci:

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

À la suite de l'analyse, il a été révélé que l'erreur commence à apparaître après un test qui a échoué et qui contient un appel de code dans une transaction:

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

Jetez un œil à l'intérieur de la classe TransactionRunner, un appel de méthode conduit au code suivant:

 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); } } 

Alors quel est le problème ici? Et le problème est que l'AssertionError qui résulte de l'exécution du code de test n'est pas héritée d'Exception, ce qui signifie que la transaction imbriquée n'est ni annulée ni validée. Étant donné que la taille du pool de connexions est égale à un, nous obtenons la même erreur «Impossible d'obtenir une connexion, erreur de pool Timeout en attente d'objet inactif» lors de la tentative d'obtention de l'objet Connection par des tests ultérieurs.

Moralité: il est nécessaire de placer les assertions dans les tests avec prudence, et dans le cas de crashs non évidents et, en particulier, de masse, une solution consiste à vérifier si la gestion des exceptions prend en compte les objets qui ne sont pas hérités d'Exception.

L'affaire m'a paru digne d'être fixée, peut-être que cette expérience est utile à quelqu'un.

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


All Articles