I'm unit testing classes in Python using unittest. As I understand it, unittest calls the setUp function before each test so that the state of the unit test objects are the same and the order the test are executed wouldn't matter.
Now I have this class I'm testing...
#! usr/bin/python2
class SpamTest(object):
def __init__(self, numlist = []):
self.__numlist = numlist
#property
def numlist(self):
return self.__numlist
#numlist.setter
def numlist(self, numlist):
self.__numlist = numlist
def add_num(self, num):
self.__numlist.append(num)
def incr(self, delta):
self.numlist = map(lambda x: x + 1, self.numlist)
def __eq__(self, st2):
i = 0
limit = len(self.numlist)
if limit != len(st2.numlist):
return False
while i < limit:
if self.numlist[i] != st2.numlist[i]:
return False
i += 1
return True
with the following unit tests...
#! usr/bin/python2
from test import SpamTest
import unittest
class Spammer(unittest.TestCase):
def setUp(self):
self.st = SpamTest()
#self.st.numlist = [] <--TAKE NOTE OF ME!
self.st.add_num(1)
self.st.add_num(2)
self.st.add_num(3)
self.st.add_num(4)
def test_translate(self):
eggs = SpamTest([2, 3, 4, 5])
self.st.incr(1)
self.assertTrue(self.st.__eq__(eggs))
def test_set(self):
nl = [1, 4, 1, 5, 9]
self.st.numlist = nl
self.assertEqual(self.st.numlist, nl)
if __name__ == "__main__":
tests = unittest.TestLoader().loadTestsFromTestCase(Spammer)
unittest.TextTestRunner(verbosity = 2).run(tests)
This test fails for test_translate.
I can do two things to make the tests succeed:
(1) Uncomment the second line in the setUp function. Or,
(2) Change the names of the tests such that translate occurs first. I noticed that unittest executes tests in alphabetical order. Changing translate to, say, atranslate so that it executes first makes all tests succeed.
For (1), I can't imagine how this affects the tests since at the very first line of setUp, we create a new object for self.st . As for (2), my complaint is similar since, hey, on setUp I assign a new object to self.st so whatever I do to self.st in test_set shouldn't affect the outcome of test_translate.
So, what am I missing here?
Without studying the detais of your solution, you should read the Default Parameter Values in Python by Fredrik Lundh.
It is likely that it explains your problem with your empty list as a default argument. The reason is that the list is empty only for the first time unless you make it empty explicitly later. The initialy empty default list is the single instance of the list type that is reused when no explicit argument is passed.
It is good idea to read the above article to fix your thinking about the default arguments. The reasons are logical, but may be unexpected.
The generally recommended fix is to use None as the default value of the __init__ and set the empty list inside the body if the argument is not passed, like this:
class SpamTest(object):
def __init__(self, numlist=None):
if numlist is None:
numlist = [] # this is the new instance -- the empty list
self.__numlist = numlist
This is due to the way default parameters behave in Python when using Mutable objects like lists: Default Parameter Values in Python.
In the line:
def __init__(self, numlist = []):
The default parameter for numlist is only evaluated once so you only have one instance of the list which is shared across all instance of the SpamTest class.
So even though the test setUp is called for every test it never creates a fresh empty list, and your tests which work upon that list instance end up stepping on each others toes.
The fix is to have something like this instead, using a non-mutable object like None:
def __init__(self, numlist = None):
if numlist is None:
numlist = []
self.__numlist = numlist
The reason it works when setting the property is that you provide a brand new empty list there, replacing the list created in the constructor.
Related
I created a class, something like below -
class child:
def __init__(self,lists):
self.myList = lists
def find_mean(self):
mean=np.mean(self.myList)
return mean
and when I create an onject something like below -
obj=child()
it gives the error -
TypeError: __init__() missing 1 required positional argument: 'lists'
if I create object like below then it works well -
obj=child([44,22,55)
or If I create the class like below -
class child:
def find_mean(self,myList):
mean=np.mean(myList)
return mean
and then I create the object like below -
obj=child()
then also it works well, however I need to make it in the way I explained in the very begining. Can you please help me understand this context?
In the first example, the __init__ method expects two parameters:
self is automatically filled in by Python.
lists is a parameter which you must give it. It will try to assign this value to a new variable called self.myList, and it won't know what value it is supposed to use if you don't give it one.
In the second example, you have not written an __init__ method. This means that Python creates its own default __init__ function which will not require any parameters. However, the find_mean method now requires you to give it a parameter instead.
When you say you want to create it in the way you explained at the beginning, this is actually impossible: the class requires a value, and you are not giving it one.
Therefore, it is hard for me to tell what you really want to do. However, one option might be that you want to create the class earlier, and then add a list to it later on. In this case, the code would look like this:
import numpy as np
class Child:
def __init__(self, lists=None):
self.myList = lists
def find_mean(self):
if self.myList is None:
return np.nan
mean = np.mean(self.myList)
return mean
This code allows you to create the object earlier, and add a list to it later. If you try to call find_mean without giving it a list, it will simply return nan:
child = Child()
print(child.find_mean()) # Returns `nan`
child.myList = [1, 2, 3]
print(child.find_mean()) # Returns `2`
the code you have at the top of your question defines a class called child, which has one attribute, lists, which is assigned at the time of instance creation in the __init__ method. This means that you must supply a list when creating an instance of child.
class child:
def __init__(self, lists):
self.myList = lists
def find_mean(self):
mean=np.mean(self.myList)
return mean
# works because a list is provided
obj = child([44,22,55])
# does not work because no list is given
obj = child() # TypeError
If you create the class like in your second example, __init__ is no longer being explicitly specified, and as such, the object has no attributes that must be assigned at instance creation:
class child:
def find_mean(self, myList):
mean=np.mean(myList)
return mean
# does not work because `child()` does not take any arguments
obj = child([44,22,55]) # TypeError
# works because no list is needed
obj = child()
The only way to both have the myList attribute, and not need to specify it at creation would be to assign a default value to it:
class child:
def find_mean(self,myList=None):
mean=np.mean(myList)
return mean
# now this will work
obj = child()
# as will this
obj = child([24, 35, 27])
I am relatively new to Python and love it. If I have a problem, usually I manage to find my own solutions, or relative topics that can help me understand where I messed up. But this one I can't seem to wrap my head around it.
So the issue I'm facing right now is that I give a class1.list to another class2.function, where I do some stuff there with that list. My issue is that my class1.list changes in that function and I don't understand why:
class ClassA():
def function_a(self, test):
test[0][0] = '?'
test[0][1] = '?'
return test
class ClassB():
class_b_list = [[1,2],[3,4]]
def function_b(self,value):
value[1][0] = 350
value[1][1] = 250
return value
my_class_a = ClassA()
my_class_b = ClassB()
print(my_class_b.class_b_list)
my_class_a.function_a(my_class_b.class_b_list)
my_class_b.function_b(my_class_b.class_b_list)
print(my_class_b.class_b_list)
So my first print will give me the actual value of the list: [[1,2][3,4]]
But after I give it as an attribute, my list changes to: [['?','?'][350,250]]
What am I missing? I don't want my_class_b.class_b_list to change.
Don't try to mutate the argument. Copy it first, then change and return.
from copy import deepcopy
class ClassA():
def function_a(self, test):
# use deep copy if you're using nested lists, or mutating object/dict in list
test = deepcopy(test)
# if not, slice copy is usually enough
# test = test[:]
test[0][0] = '?'
test[0][1] = '?'
return test
Also see: How to clone or copy a list?
In Pytest I'm trying to do following thing, where I need to save previous result and compare current/present result with previous for multiple iterations.
I've done as following ways:
#pytest.mark.parametrize("iterations",[1,2,3,4,5]) ------> for 5 iterations
#pytest.mark.parametrize("clsObj",[(1,2,3)],indirect = True) ---> here clsObj is the instance. (clsObj.currentVal, here clsObj gets instantiated for every iteration and it is instance of **class func1**)
presentVal = 0
assert clsObj.currentVal > presrntVal
clsObj.currentVal = presentVal
When I do as above every time I loop presentVal get's assign to 0 (expected since it is local variable). Instead above I tried to declare presentVal as global like,global presentVal and also I intialized presentVal above my test case but didn't turn well.
class func1():
def __init__(self):
pass
def currentVal(self):
cval = measure() ---------> function from where I get current values
return cval
Can someone suggest how declare global variable in pytest or other best way
Thanks in advance!
What you are looking for is called a "fixture". Have a look at the following example, it should solve your problem:
import pytest
#pytest.fixture(scope = 'module')
def global_data():
return {'presentVal': 0}
#pytest.mark.parametrize('iteration', range(1, 6))
def test_global_scope(global_data, iteration):
assert global_data['presentVal'] == iteration - 1
global_data['presentVal'] = iteration
assert global_data['presentVal'] == iteration
You can essentially share a fixture instance across tests. It's intended for more complicated stuff like a database access object, but it can be something trivial like a dictionary :)
Scope: sharing a fixture instance across tests in a class, module or session
What I would like to do is this...
x = MagicMock()
x.iter_values = [1, 2, 3]
for i in x:
i.method()
I am trying to write a unit test for this function but I am unsure about how to go about mocking all of the methods called without calling some external resource...
def wiktionary_lookup(self):
"""Looks up the word in wiktionary with urllib2, only to be used for inputting data"""
wiktionary_page = urllib2.urlopen(
"http://%s.wiktionary.org/wiki/%s" % (self.language.wiktionary_prefix, self.name))
wiktionary_page = fromstring(wiktionary_page.read())
definitions = wiktionary_page.xpath("//h3/following-sibling::ol/li")
print definitions.text_content()
defs_list = []
for i in definitions:
print i
i = i.text_content()
i = i.split('\n')
for j in i:
# Takes out an annoying "[quotations]" in the end of the string, sometimes.
j = re.sub(ur'\u2003\[quotations \u25bc\]', '', j)
if len(j) > 0:
defs_list.append(j)
return defs_list
EDIT:
I may be misusing mocks, I am not sure. I am trying to unit-test this wiktionary_lookup method without calling external services...so I mock urlopen..I mock fromstring.xpath() but as far as I can see I need to also iterate through the return value of xpath() and call a method "text_contents()" so that is what I am trying to do here.
If I have totally misunderstood how to unittest this method then please tell me where I have gone wrong...
EDIT (adding current unittest code)
#patch("lang_api.models.urllib2.urlopen")
#patch("lang_api.models.fromstring")
def test_wiktionary_lookup_2(self, fromstring, urlopen):
"""Looking up a real word in wiktionary, should return a list"""
fromstring().xpath.return_value = MagicMock(
content=["test", "test"], return_value='test\ntest2')
# A real word should give an output of definitions
output = self.things.model['word'].wiktionary_lookup()
self.assertEqual(len(output), 2)
What you actually want to do is not return a Mock with a return_value=[]. You actually want to return a list of Mock objects. Here is a snippet of your test code with the correct components and a small example to show how to test one of the iterations in your loop:
#patch('d.fromstring')
#patch('d.urlopen')
def test_wiktionary(self, urlopen_mock, fromstring_mock):
urlopen_mock.return_value = Mock()
urlopen_mock.return_value.read.return_value = "some_string_of_stuff"
mocked_xpath_results = [Mock()]
fromstring_mock.return_value.xpath.return_value = mocked_xpath_results
mocked_xpath_results[0].text_content.return_value = "some string"
So, to dissect the above code to explain what was done to correct your problem:
The first thing to help us with testing the code in the for loop is to create a list of mock objects per:
mocked_xpath_results = [Mock()]
Then, as you can see from
fromstring_mock.return_value.xpath.return_value = mocked_xpath_results
We are setting the return_value of the xpath call to our list of mocks per mocked_xpath_results.
As an example of how to do things inside your list, I added how to mock within the loop, which is shown with:
mocked_xpath_results[0].text_content.return_value = "some string"
In unittests (this might be a matter of opinion) I like to be explicit, so I'm accessing the list item explicitly and determining what should happen.
Hope this helps.
How to return a list of objects and not list here.
I want to return a list of test objects and not a list of str..
class test:
val = ""
def __init__(self,v):
self.val = v
def tolower(self,k):
k = k.val.lower()
return k
def test_run():
tests_lst = []
tests_lst.append(test("TEST-0"))
tests_lst.append(test("TEST-1"))
tests_lst.append(test("TEST-2"))
i_want_object_of_test = map(lambda x:x.val.lower(),tests_lst)
if __name__ == '__main__':
test_run()
OUTPUT:
['test-0', 'test-1', 'test-2']
i want a list of test objects where each object's val has changed to lower case.
The question is unclear. I'll answer by what I understand.
What I understand is that you are trying to create a new list of test objects, with the values as lower case.
You can do this either by changing the state of each of the objects in a for loop (changing state is usually not recommended):
for test_obj in test_lst:
test_obj.val = test_obj.val.lower()
A way to do it through a list comprehension is to create new test instances:
i_want_object_of_test = [test(test_obj.val.lower()) for test_obj in test_lst]
Besides, there are a few problems with your test class:
It is an old style class, you should always inherit from object in your classes: class test(object):
You define a class variable by putting val = ""' in your class defenition, you then override it in each instance.
Your tolower method gets another test instance (k) and returns its value as lower case. I assume you want to either return a new test object or change the current one in place. Either way the method should only use self.