/** * @name Imprecise assert * @description Using 'assertTrue' or 'assertFalse' rather than a more specific assertion can give uninformative failure messages. * @kind problem * @tags maintainability * testability * @problem.severity recommendation * @sub-severity high * @precision very-high * @id py/imprecise-assert */ import python /* Helper predicate for CallToAssertOnComparison class */ predicate callToAssertOnComparison(Call call, string assertName, Cmpop op) { call.getFunc().(Attribute).getName() = assertName and (assertName = "assertTrue" or assertName = "assertFalse") and exists(Compare cmp | cmp = call.getArg(0) and /* Exclude complex comparisons like: a < b < c */ not exists(cmp.getOp(1)) and op = cmp.getOp(0) ) } class CallToAssertOnComparison extends Call { CallToAssertOnComparison() { callToAssertOnComparison(this, _, _) } Cmpop getOperator() { callToAssertOnComparison(this, _, result) } string getMethodName() { callToAssertOnComparison(this, result, _) } string getBetterName() { exists(Cmpop op | callToAssertOnComparison(this, "assertTrue", op) and ( op instanceof Eq and result = "assertEqual" or op instanceof NotEq and result = "assertNotEqual" or op instanceof Lt and result = "assertLess" or op instanceof LtE and result = "assertLessEqual" or op instanceof Gt and result = "assertGreater" or op instanceof GtE and result = "assertGreaterEqual" or op instanceof In and result = "assertIn" or op instanceof NotIn and result = "assertNotIn" or op instanceof Is and result = "assertIs" or op instanceof IsNot and result = "assertIsNot" ) or callToAssertOnComparison(this, "assertFalse", op) and ( op instanceof NotEq and result = "assertEqual" or op instanceof Eq and result = "assertNotEqual" or op instanceof GtE and result = "assertLess" or op instanceof Gt and result = "assertLessEqual" or op instanceof LtE and result = "assertGreater" or op instanceof Lt and result = "assertGreaterEqual" or op instanceof NotIn and result = "assertIn" or op instanceof In and result = "assertNotIn" or op instanceof IsNot and result = "assertIs" or op instanceof Is and result = "assertIsNot" ) ) } } from CallToAssertOnComparison call where /* Exclude cases where an explicit message is provided*/ not exists(call.getArg(1)) select call, call.getMethodName() + "(a " + call.getOperator().getSymbol() + " b) " + "cannot provide an informative message. Using " + call.getBetterName() + "(a, b) instead will give more informative messages."