Python unittest a search function - python

I am trying to write unit tests for my search function which searches through a directory and returns a list of matching files which matches the query. While writing unit tests I realized that if I give it test data, the test data might just take up too much space. How can i get over this problem ? I thought of mocking test data but I am not familiar on how to do this. This is currently how one of my unit tests looks like
import search_function
import unittest
class searchDirTest(unittest.TestCase):
def setUp(self):
pass
def test_file_name_case_sensitive(self):
res = search_function.search_function('./testData', 'allsmall')
self.assertEquals(res, ['./testData/allsmall.txt', './testData/testData_subdir/allsmall.txt'])
self.assertNotEquals(res, ['./testData/ALLSMALL.txt'])
if __name__ == "__main__":
unittest.main()
Also the search_function takes 2 arguments. The Path and the search query. Any pointers on how to achieve this ?

I would suggest refactoring your search_function so that it accepts a list of file names and returns all file names that match, something like case_insensitive_file_match(filenames). This is easily testable and the test data is simple to produce
Testing things external to your program should preferably be kept out of unit tests. This includes reading files/calling APIs etc

Related

How to list all keywords from a .robot file which have no test cases

Problem statement:
I have a .robot file which contains a lot of keywords. this is a higher level robot file which does not contains any test cases.
I want to list all the keywords name.
What i tried so far ?
from robot.parsing.model import TestData
suite = TestData(parent=None,source="Track2_Keywords.robot")
it gives error
raise NoTestsFound('File has no tests or tasks.')
robot.parsing.populators.NoTestsFound: File has no tests or tasks.
also i tried:
from robot.parsing.model import KeywordTable
suite = KeywordTable("Track2_Keywords.robot")
for item in suite:
... print (item.name)
but its empty.
First, you need to use the ResourceFile model rather than TestData or KeywordTable. Second, you must call the populate method to get the keywords to be visible. It is the populate method that actually reads the file and imports the keywords.
from robot.parsing.model import ResourceFile
rf = ResourceFile("Track2_Keywords.robot")
rf.populate()
for kw in rf.keywords:
print(kw.name)
I don't understand what is your goal. If the file does not contain tests, then it is a Resource file. In this case you can add a dummy test case like for example:
*** Test Cases ***
Dummy Test
No Operation
This way, your code should not complaint about missing tests.
--
If you want to find non used keywords in a test suite, you can use that existing feature in RIDE (https://github.com/robotframework/RIDE/).

Import all methods to a unittest file

I'm trying to figure out the best structure to write unittests for a file parser. My parser.py file looks like this:
import re
import sys
import glob
from datetime import datetime
import csv
def main():
# does something
def normalizeGender(gender):
# does something
def normalizeDate(date):
# does something
def parseLine(record, delimiter):
# does something
def formatRecords(record_list):
# does something
etc...
I have tests dir, and inside a parser_test.py file that looks like this:
import unittest
class ParserTest(unittest.TestCase):
# Returns True or False.
def test(self):
self.assertTrue(True)
if __name__ == '__main__':
unittest.main()
Now, if I want to test all the methods from my parser, should import them all at once? Something tells me they should be wrapped as a module of some sort.
The following answer is based on my tool preferences, and the practices that I usually follow:
I would use pytest to implement the tests
I would implement at least 1 test for every function: test_normalizeGender, test_normalizeDate, test_ normalizeDate, test_parseLine, test_formatRecords. However, in case you have if statement with multiple branching inside your code, make sure you try to cover the possible cases of those branches. In addition, if you have for loops, I will implement a test for no elements, one element, and multiple elements.
I will put all the test in the same file since they are related.
Keep in mind that when it comes to implementing unit tests, you are testing a unit, so you don't need to verify the functionality of all your parser in the same test. In addition, you should mock/patch objects, methods, or functions to facilitate testing.
I hope this helps.

Python: know function is called from a unittest?

Is there a way to know in Python if a function is called from the context of a unittest execution or a debugging run?
For the context, I am trying to unittest a code where I use functions that perform a database call. In order to avoid database calls during the test of that function (DB calls are tested separately), I am trying to make the DB IO functions aware of their environement and to mock when they are called within a unittest and log additional variables during a debug run.
My current aproach is to read/write environment variables, but it seems a little bit of an overkill and I think Python must have a better mechanism for that.
Edit:
Here is the example of a function I am trying to unittest:
from Database_IO import Database_read
def some_function(significance_level, time_range)
data = Database_read(time_range)
significant_data = data > significance_level
return significant_data
In my opinion, if you write your function to behave in a different way when tested, you are not really testing it.
To test the function I'd mock.patch() the database object, and then check it has used correctly in your function.
The most difficult thing when you start using the mock library is to find the correct object to replace.
In your example, if in your_module you import the Database_read object from the Database_IO module, you can test it by using a code similar to the following
with mock.patch('your_module.Database_read') as dbread_mock:
# prepare the dbread_mock
dbread_mock.return_value = 10
# execute a test call
retval = some_function(3, 'some range')
# check the result
dbread_mock.assert_called_with('some range')

A DRY way of writing similar unit tests in python

I have some similar unit tests in python.
There are so similar that only one argument is changing.
class TestFoo(TestCase):
def test_typeA(self):
self.assertTrue(foo(bar=TYPE_A))
def test_typeB(self):
self.assertTrue(foo(bar=TYPE_B))
def test_typeC(self):
self.assertTrue(foo(bar=TYPE_C))
...
Obviously this is not very DRY, and if you have even 4-5 different options the code is going to be very repetitive
Now I could do something like this
class TestFoo(TestCase):
BAR_TYPES = (
TYPE_A,
TYPE_B,
TYPE_C,
...
)
def _foo_test(self, bar_type):
self.assertTrue(foo(bar=bar_type))
def test_foo_bar_type(self):
for bar_type in BAR_TYPES:
_foo_test(bar=bar_type))
Which works, however when an exception gets raised, how will I know whether _foo_test failed with argument TYPE_A, TYPE_B or TYPE_C ?
Perhaps there is a better way of structuring these very similar tests?
What are you trying to do is essentially a parameterized test. This feature isn't included in standard django or python unittest modules, but a number of libs provide it: nose-parameterized, py.test, ddt
My favorite so far is ddt: it resembles NUnit-JUnit style parameterized tests most, pretty lightweight, don't get in your way and does not require dedicated test runner (like nose-parameterized do). The way it can help you is that it modifies test name to include all parameters, so you would clearly see which test case failed by looking at a test name.
With ddt your example would look like this:
import ddt
#ddt.ddt
class TestProcessCreateAgencyOfferAndDispatch(TestCase):
#ddt.data(TYPE_A, TYPE_B, TYPE_C)
def test_foo_bar_type(self, type):
self.assertTrue(foo(bar=type))
In such case names will look like test_foo_bar_type__TYPE_A (technically, it constructs it something like [test_name]__[repr(parameter_1)]__[repr(parameter_2)]).
As a bonus, it is much cleaner (no helper method), and you get three methods instead of one. The advantage here is that you can test various code paths in a method and get one test case per each path (but a certain amount of thinking is needed, sometimes it's better to have a dedicated test for some of code paths)
Most TestCase assertion methods, including assertTrue, take an optional msg argument.
If you change your BAR_TYPES tuple to include the variable names, then you can include this in the message that is shown when the assertion fails.
class TestProcessCreateAgencyOfferAndDispatch(TestCase):
BAR_TYPES = (
('TYPE_A', TYPE_A),
('TYPE_B', TYPE_B),
('TYPE_C', TYPE_C),
...
)
def _foo_test(self, var_name, bar_type):
self.assertTrue(foo(bar=bar_type), var_name)
def test_foo_bar_type(self):
for (var_name, bar_type) in BAR_TYPES:
_foo_test(bar=bar_type), var_name=var_name)

Giving parameters into TestCase from Suite in python

From python documentation(http://docs.python.org/library/unittest.html):
import unittest
class WidgetTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget('The widget')
def tearDown(self):
self.widget.dispose()
self.widget = None
def test_default_size(self):
self.assertEqual(self.widget.size(), (50,50),
'incorrect default size')
def test_resize(self):
self.widget.resize(100,150)
self.assertEqual(self.widget.size(), (100,150),
'wrong size after resize')
Here is, how invoke those testcase:
def suite():
suite = unittest.TestSuite()
suite.addTest(WidgetTestCase('test_default_size'))
suite.addTest(WidgetTestCase('test_resize'))
return suite
Is it possible to insert parameter custom_parameter into WidgetTestCase like:
class WidgetTestCase(unittest.TestCase):
def setUp(self,custom_parameter):
self.widget = Widget('The widget')
self.custom_parameter=custom_parameter
?
What I've done is in test_suite module just added
WidgetTestCase.CustomParameter="some_address"
The simplest solutions are the best :)
I've found a way to do this, but it's a bit of a cludge.
Basically, what I do is add, to the TestCase, an __init__ method which defines a 'default' parameter and a __str__ so that we can distinguish cases:
class WidgetTestCase(unittest.TestCase):
def __init__(self, methodName='runTest'):
self.parameter = default_parameter
unittest.TestCase.__init__(self, methodName)
def __str__(self):
''' Override this so that we know which instance it is '''
return "%s(%s) (%s)" % (self._testMethodName, self.currentTest, unittest._strclass(self.__class__))
Then in suite(), I iterate over my test parameters, replacing the default parameter with one specific to each test:
def suite():
suite = unittest.TestSuite()
for test_parameter in test_parameters:
loadedtests = unittest.TestLoader().loadTestsFromTestCase(WidgetTestCase)
for t in loadedtests:
t.parameter = test_parameter
suite.addTests(loadedtests)
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(OtherWidgetTestCases))
return suite
where OtherWidgetTestCases are tests which don't need to be parameterised.
For instance I have a bunch of tests on real data for which a suite of tests need to be applied to each, but I also have some synthetic data sets, designed to test certain edge cases not normally present in the data, and I only need to apply certain tests to those, so they get their own tests in OtherWidgetTestCases.
This is something that has been on my mind recently. Yes it is very possible to do. I called it scenario testing, but I think parameterized may be more accurate. I put a proof of concept up as a gist here. In short it is a meta class that allows you to define a scenario and run the tests against it a bunch. With it your example can be something like this:
class WidgetTestCase(unittest.TestCase):
__metaclass__ = ScenarioMeta
class widget_width(ScenerioTest):
scenarios = [
dict(widget_in=Widget("One Way"), expected_tuple=(50, 50)),
dict(widget_in=Widget("Another Way"), expected_tuple=(100, 150))
]
def __test__(self, widget_in, expected_tuple):
self.assertEqual(widget_in.size, expected_tuple)
When run, the meta class writes 2 seperate tests out so the output would be something like:
$ python myscerariotest.py -v
test_widget_width_0 (__main__.widget_width) ... ok
test_widget_width_1 (__main__.widget_width) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
As you can see the scenarios are converted to tests at runtime.
Now I am not yet sure if this is even a good idea. I use it in tests where I have a lot of text centric cases that repeat the same assertions on slightly different data, which helps me to catch the little edge cases. But the classes in that gist do work and I believe it accomplishes what you are after.
Note that the with some trickery the test cases can be given names and even pulled from an external source like a text file or database. Its not documented yet but some digging around in the meta class should get you started. There is also some more info and examples on my post here.
Edit
This is an ugly hack that I do not support anymore. The implementation should have been done as a subclass of TestCase, not as a hacked meta class. Live and learn. An even better solution would be to use nose generators.
I don't believe so, the signature for setUp needs to be what unittest is expecting, afaik, setUp is automagically called within the testcase's run method as setUp()... you're not going to be able to pass it unless you override run to pass in the var you want. But I think what you want defeats the purpose of unit testing. Don't try to use a DRY philosophy with this, each unit you're testing should be a part of a class or even part of a function/method.
I don't think this is a good idea. Unit tests should be thorough enough that you test all functionality in your cases so passing in different parameteres shouldn't be required.
You mention you're passing in a www address - this is almost certainly not a good idea. What happens if you try and run the tests on a machine where the 'net connection is down? Your tests should be:
Automatic - they will run on all machines and platforms where your app is supported, without user intervention. They shouldn't rely on external environment to pass. This means (amongst other things) that relying on a properly set up connection to the Internet is a bad idea. You can get around this by providing dummy data. Instead of passing in a URL to a resource, abstract away the data source and pass in a data-stream or whatever. This is especially easy in python since you can make use of python's duck-typing to present a stream-like object (python frequently uses a "file-like" object for this very reason!).
Thorough - your unit tests should have 100% code coverage, and cover all possible situations. You want to test your code with multiple sites? Instead, test your code with all the possible features that a site may include. Without knowing more about what your application does, I can't offer much advice in this point.
Now, it looks like you're tests are going to be heavily data-driven. There are many tools that allow you to define data-sets for unit tests and load them in the tests. Check out python test fixtures, for example.
I realise that this isn't the answer you're looking for, but I think you'll have more joy in the long-run if you follow these principles.

Categories