@ -14,6 +14,7 @@
# under the License.
import ast
import os
import re
import pep8
@ -469,6 +470,77 @@ class CheckForTransAdd(BaseASTChecker):
super ( CheckForTransAdd , self ) . generic_visit ( node )
class _FindVariableReferences ( ast . NodeVisitor ) :
def __init__ ( self ) :
super ( _FindVariableReferences , self ) . __init__ ( )
self . _references = [ ]
def visit_Name ( self , node ) :
if isinstance ( node . ctx , ast . Load ) :
# This means the value of a variable was loaded. For example a
# variable 'foo' was used like:
# mocked_thing.bar = foo
# foo()
# self.assertRaises(excepion, foo)
self . _references . append ( node . id )
super ( _FindVariableReferences , self ) . generic_visit ( node )
class CheckForUncalledTestClosure ( BaseASTChecker ) :
""" Look for closures that are never called in tests.
A recurring pattern when using multiple mocks is to create a closure
decorated with mocks like :
def test_thing ( self ) :
@mock.patch.object ( self . compute , ' foo ' )
@mock.patch.object ( self . compute , ' bar ' )
def _do_test ( mock_bar , mock_foo ) :
# Test things
_do_test ( )
However it is easy to leave off the _do_test ( ) and have the test pass
because nothing runs . This check looks for methods defined within a test
method and ensures that there is a reference to them . Only methods defined
one level deep are checked . Something like :
def test_thing ( self ) :
class FakeThing :
def foo ( self ) :
would not ensure that foo is referenced .
N349
"""
def __init__ ( self , tree , filename ) :
super ( CheckForUncalledTestClosure , self ) . __init__ ( tree , filename )
self . _filename = filename
def visit_FunctionDef ( self , node ) :
# self._filename is 'stdin' in the unit test for this check.
if ( not os . path . basename ( self . _filename ) . startswith ( ' test_ ' ) and
not ' stdin ' ) :
return
closures = [ ]
references = [ ]
# Walk just the direct nodes of the test method
for child_node in ast . iter_child_nodes ( node ) :
if isinstance ( child_node , ast . FunctionDef ) :
closures . append ( child_node . name )
# Walk all nodes to find references
find_references = _FindVariableReferences ( )
find_references . generic_visit ( node )
references = find_references . _references
missed = set ( closures ) - set ( references )
if missed :
self . add_error ( node , ' N349: Test closures not called: %s '
% ' , ' . join ( missed ) )
def assert_true_or_false_with_in ( logical_line ) :
""" Check for assertTrue/False(A in B), assertTrue/False(A not in B),
assertTrue / False ( A in B , message ) or assertTrue / False ( A not in B , message )
@ -732,3 +804,4 @@ def factory(register):
register ( check_python3_no_itervalues )
register ( cfg_help_with_enough_text )
register ( no_os_popen )
register ( CheckForUncalledTestClosure )