Python injecting random number into tests - python

I have written code like this :
def choice(states):
states = list(states)
rnd = random.random()
for state, p in states:
rnd -= p
if rnd <= 0:
return state
And I need to create some tests :
import unittest
class Tests(unittest.TestCase):
def test_choice(self):
assertEquals(choice(states),something_equl)
How am I supposed to inject my own random number into test? to get deterministic results?

Mock the random.random() function, example:
import random
import unittest
import mock
def choice(states):
states = list(states)
rnd = random.random()
for state, p in states:
rnd -= p
if rnd <= 0:
return state
class Tests(unittest.TestCase):
#mock.patch('random.random')
def test_first_state_fires(self, random_call):
random_call.return_value = 1
self.assertEquals(choice([(1, 1)]), 1)
#mock.patch('random.random')
def test_returns_none(self, random_call):
random_call.return_value = 2
self.assertIsNone(choice([(1, 1)]))

You can use the unittest.mock library to patch out the random() function. The library is part of Python 3.3 and up, you can install it separately as mock for older versions:
try:
from unittest import mock
except ImportError:
import mock
class Tests(unittest.TestCase):
#mock.patch('random.random')
def test_choice(self, mock_random):
mock_random.return_value = 0.42
assertEquals(choice(states),something_equl)

I would like to improve the response with a full script to better understand and be adaptable to other cases.
import random
from unittest import TestCase, mock
def get_random_words(): # Simple function using choice
l = []
for _ in range(3):
l.append(random.random(0, 10))
return "".join([str(n) for n in l])
class TestRandom(TestCase):
#mock.patch('random.random') # *(1)
def test_get_random_words(self, mock_random):
mock_random.side_effect = [1,7,3,6] # *(2)
result = get_random_words()
self.assertEqual(result, '173', 'Does not generate correct numbers')
Considerations
*(1) For this example, the function is inside the same file, but in case it is in another file you must change the path of the patch
Ex: #mock.patch('your_package.your_file.your_function.random.random')
*(2) For this case, the get_random_words function calls random.random 3 times. This is why you must put equal or more items inside mock_random.side_effect. This is because if it has fewer items it will throw the StopIteration error.

Related

freeze_time doesn't work inside invoked functions

# test.py
from freezegun import freeze_time
from actual import DummyClass
class DummyClassMock(DummyClass):
def some_function(self):
# does something
class TestClass():
def setUp(self):
self.dummy = DummyClassMock()
#freeze_time('2021-01-01')
def test_dummy_function(self):
self.assertTrue((self.dummy.dummy_function - datetime.utcnow()).total_seconds() >= 1)
# actual.py
from datetime import datetime, timedelta
class DummyClass():
def dummy_function(self):
return datetime.utcnow() + timedelta(5)
My code goes along the above structure. With this, if I am executing the test_dummy_function individually, dummy_function is returning 2021-01-01 and test case is a pass. However, when I running this along with all the other test cases in the project, it is failing. Content is not dependent either.
Not particularly a good solution but, the workaround I used was to define a function that would just return datetime.utcnow() and mock it. My test case will assign the date used in freeze_time as return value. It looks something like,
#mock.patch(actual.DummyClass.now_function)
#freeze_time('2021-01-01')
def test_dummy_function(self, mock_dt):
now = datetime.utcnow()
mock_dt.return_value = now
self.assertTrue((self.dummy.dummy_function - now).total_seconds() >= 1)
# actual.py
Class DummyClass():
def now_function():
return datetime.utcnow()
def dummy_function():
return now_function()+timedelta(days=5)

How to mock modules python, patch does not find the attribute

In a function I'm using uuid1 that I want to patch.
def myFunction():
my_value = uuid4.int
smth else..
I want to be able to mock my_value so it always returns the same number in my unit test, because I need it for further use.
I tried doing:
#patch('folder.myFunction.uuid4')
def test_myFunction(self, mock_value):
mock_value.return_value = 22222
But it throws an error saying myFunction does not have uuid4 as an attribute.
How do I mock its value?
The error you get is correct. Your function does not have a uuid4 attribute.
I'm reading between the lines assuming uuid4 is a method of the uuid module that normally generates a random uuid.
When testing you said you want it to always return the same value. To do that you can substitute a unittest.mock.Mock for uuid.uuid4.
In [36]: uuid_mock = Mock(return_value=uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b9f3e'))
In [37]: uuid_mock()
Out[37]: UUID('77f1df52-4b43-11e9-910f-b8ca3a9b9f3e')
Something like this for testing the following function (f)
import uuid, unittest
from unittest.mock import Mock, patch
def f():
z = uuid.uuid4()
return z.int
The target for the patch is the uuid method - uuid.uuid4. Specify a unittest.mock.Mock with a fixed return value for the new parameter of the patch. During the test, the Mock will be substituted for uuid.uuid4
class TestF(unittest.TestCase):
uuid_mock = Mock(return_value=uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b9f3e'))
good_uuid = uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b9f3e').int
bad_uuid = uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b5a31').int
#patch(target='uuid.uuid4', new=TestF.uuid_mock)
def test_myFunction_True(self):
self.assertEqual(f(), self.good_uuid)
#patch(target='uuid.uuid4', new=TestF.uuid_mock)
def test_myFunction_False(self):
self.assertNotEqual(f(), self.bad_uuid)
if __name__ == '__main__':
unittest.main()
Result:
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
If you want to test a function that relies on f's return value and you want f to always return the same value during testing then make f the target for the patch.
def g():
return f() == uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b9f3e').int
class TestG(unittest.TestCase):
good_uuid_mock = Mock(return_value=uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b9f3e').int)
bad_uuid_mock = Mock(return_value=uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b5a31').int)
#patch(target='__main__.f', new=TestG.good_uuid_mock)
def test_myFunction_True(self):
self.assertTrue(g())
#patch(target='__main__.f', new=TestG.bad_uuid_mock)
def test_myFunction_False(self):
self.assertFalse(g())
It depends on your import. Let's say you have a module called module.py, and you have an import like this:
from uuid import uuid4
This means that in this module we now have a variable called uuid4. This is the thing to mock.
#patch('path.to.module.uuid4.int')

Good way to count number of functions of a python file, given path

I do not want to import my module. I have to count the number of functions given the .py file path. What is the best way to do so?
One thing I thought about was to count the number of "def" in my code, but it does not seem like the best way to go about this. Is there any better way to count the number of functions?
To count the top level definition, use ast module like this:
import ast
with open(filename) as f:
tree = ast.parse(f.read())
sum(isinstance(exp, ast.FunctionDef) for exp in tree.body)
You can use ast.NodeVisitor:
import inspect
import importlib
import ast
class CountFunc(ast.NodeVisitor):
func_count = 0
def visit_FunctionDef(self, node):
self.func_count += 1
mod = "/path/to/some.py"
p = ast.parse(open(mod).read())
f = CountFunc()
f.visit(p)
print(f.func_count)
If you wanted to include lambdas you would need to add a visit_Lambda:
def visit_Lambda(self, node):
self.func_count += 1
That will find all defs including methods of any classes, we could add more restrictions to disallow that:
class CountFunc(ast.NodeVisitor):
func_count = 0
def visit_ClassDef(self, node):
return
def visit_FunctionDef(self, node):
self.func_count += 1
def visit_Lambda(self, node):
self.func_count += 1
You can tailor the code however you like, all the nodes and their attributes are described in the greentreesnakes docs
Padraic beats me to it, but here is my code, which works with Python 2 and Python 3.
from __future__ import print_function
import ast
class FunctionCounter(ast.NodeVisitor):
def __init__(self, filename):
self.function_count = 0
with open(filename) as f:
module = ast.parse(f.read())
self.visit(module)
def visit_FunctionDef(self, node):
print('function: {}'.format(node.name))
self.function_count += 1
# Uncomment this to disable counting methods, properties within a
# class
# def visit_ClassDef(self, node):
# pass
if __name__ == '__main__':
counter = FunctionCounter('simple.py')
print('Number of functions: {}'.format(counter.function_count))
Discussion
This code not only count the function at the module level, it also count functions (method and properties) nested within class definitions.
To disable counting functions in the class definitions, uncomment the 2 lines for visit_ClassDef
You can use
len(dir(module))
Hope it helps
you can use the pyclbr module to get results of the module, the only catch is that you need to use the name of the module as you would import it instead of the file path, this benefits from also recognizing from X import Y for python source based modules (not builtin ones like math)
from pyclbr import readmodule_ex, Function
#readmodule_ex is function 1
def test(): #2
pass
def other_func(): #3
pass
class Thing:
def method(self):
pass
result = readmodule_ex("test") #this would be it's own file if it is test.py
funcs = sum(isinstance(v,Function) for v in result.values())
print(funcs)
My answer is an edit to the answer of # Naveen Kumar because I can not edit his answer at the moment.
You can use
import module
len(dir(module))
i.e:
import math
print(len(dir(math)))
output:
63

Search for recursive functions in a Python project

I need to find all functions in a Python project which are recursive (i.e. call themselves).
Any ideas how to approach this?
It's hard to say whether function recursive or not before it runs. I would personally use this one with inspect.getclosurevars (added in Python 3.3):
import sys
if sys.version_info >= (3, 3, 0):
from inspect import getclosurevars
def is_recursive(func):
if sys.version_info >= (3, 3, 0):
return getclosurevars(func).globals.get(func.__name__) is func
else:
# We can implement part of it if it's not in our standard library
def global_vars_in_closure(func):
vars = {x: func.__globals__.get(x) for x in func.__code__.co_names}
return vars
return global_vars_in_closure(func).get(func.__name__) is func
It will work correctly in most use cases, just remember to use func_X instead of __X__ as function methods on Python 2. It will fail only if a function contain a reference to itself without call:
def false_recursive():
false_recursive
def true_recursive():
true_recursive()
assert is_recursive(true_recursive), 'Must not fail'
assert not is_recursive(false_recursive), 'See? It fails' # AssertionError: See? It fails
You can parse the source code with ast:
code = """
def f(x):
f(x)
def g(x):
pass
"""
import ast
class FindRecursiveFunctions(ast.NodeVisitor):
def __init__(self):
self._current_func = None
self.recursive_funcs = set()
def generic_visit(self, node):
if node.__class__ is ast.FunctionDef:
self._current_func = node.name
if node.__class__ is ast.Call and node.func.id == self._current_func:
self.recursive_funcs.add(self._current_func)
super(FindRecursiveFunctions, self).generic_visit(node)
>>> tree = ast.parse(code)
>>> finder = FindRecursiveFunctions()
>>> finder.visit(tree)
>>> finder.recursive_funcs
set(['f'])

Timed nose tests not failing properly

I have the following test that does not fail when running an especially long fib assert.
Tests that don't fail properly
#!/usr/env/bin python2.7
import unittest
from fib import fib
from nose.tools import timed
def test_gen(expected, actual):
#timed(.001)
def test_method(self):
return self.assertEqual(expected, actual)
return test_method
if __name__ == '__main__':
all_cases = {
'user': ((fib(40), 102334155), (fib(2), 1), (fib(5), 5)),
}
fails = {}
for username, cases in all_cases.items():
class FibTests(unittest.TestCase):
pass
for index, case in enumerate(cases):
test_name = 'test_{0}_{1}'.format(username, index)
test = test_gen(case[1], case[0])
setattr(FibTests, test_name, test)
suite = unittest.TestLoader().loadTestsFromTestCase(FibTests)
result = unittest.TextTestRunner(verbosity=2).run(suite)
fails[username] = len(result.failures)
print fails
(Slow) Fib.py Implementation
def fib(x):
if x == 0:
return 0
elif x == 1:
return 1
return fib(x - 2) + fib(x - 1)
Tests that fail properly
import unittest
from fib import fib
from nose.tools import timed
def test_gen(expected, actual):
#timed(.001)
def test_method(self):
time.sleep(.2)
return self.assertEqual(expected, actual)
return test_method
You are timing the wrong thing, and never actually calling your test method. You are also going to an awful lot of effort to dynamically create and add your cases to your class that does nothing but act as a container for tests when nose supports generator test cases, which would be much easier to read and follow than what you have here. Also, is this a test file or a piece of product code? If it's a test file, then having all of that code in if __name__ == '__main__' is kind of odd, and if it is a product code file, then having the test_gen function and the unittest and nose import statements in the uncoditionally run part doesn't make much sense. I'd recommend doing it the following way, and not trying to make the test script self-runnable; just launch it with nose.
from fib import fib
from nose.tools import timed
fib = timed(.001)(fib)
def execute(username, fib_arg, expected_output):
result = fib(fib_arg)
assert result == expected_output, ('%s fib(%d) got %d, expected %d'
% (username, fib_arg, result, expected_output))
def test_fib():
for name, datasets in (('user', ((40, 102334155), (2, 1), (5, 5))),):
for arg, expected in datasets:
yield execute, name, arg, expected

Categories