How to mock a method on an object in a class? - python-2.7

I'm trying to figure out how to mock with one additional level of indirection - a method on an object in a class.
import serial
class MyClass(object):
def __init__(self, com_port, baudrate):
self.sp = serial.Serial(com_port, baudrate)
def do_something(self, data):
num_bytes_written = self.sp.write(data)
if num_bytes_written != len(data):
return -1
return 0
In testfile:
import mock
...
#mock.patch('serial.Serial', return_value='')
#mock.patch('serial.Serial.write', return_value=0)
def test_do_something_fails_on_bad_write(self, mock_write):
...
want = -1
dummy_data = b'123'
inst = myfile.MyClass('COM8', 115200)
got = c.do_something(dummy_data)
self.assertEqual(got, want)

Unit test solution:
serial.py:
class Serial(object):
def __init__(self, port, baudrate):
self.port = port
self.baudrate = baudrate
def write(self, data):
return 100
myclass.py:
import serial
class MyClass(object):
def __init__(self, com_port, baudrate):
self.sp = serial.Serial(com_port, baudrate)
def do_something(self, data):
num_bytes_written = self.sp.write(data)
if num_bytes_written != len(data):
return -1
return 0
test_myclass.py:
import unittest
from unittest import mock
import serial
import myclass
class TestMyClass(unittest.TestCase):
#mock.patch('myclass.serial.Serial')
def test_do_something_fails_on_bad_write(self, mock_Serial):
serial_instance = mock_Serial()
serial_instance.write.return_value = 1
want = -1
dummy_data = b'123'
c = myclass.MyClass('COM8', 115200)
got = c.do_something(dummy_data)
self.assertEqual(got, want)
serial_instance.write.assert_called_once_with(b'123')
#mock.patch('myclass.serial.Serial')
def test_do_something_success(self, mock_Serial):
serial_instance = mock_Serial()
serial_instance.write.return_value = 3
want = 0
dummy_data = b'123'
c = myclass.MyClass('COM8', 115200)
got = c.do_something(dummy_data)
self.assertEqual(got, want)
serial_instance.write.assert_called_once_with(b'123')
if __name__ == '__main__':
unittest.main()
unit test result with coverage report:
..
----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
Name Stmts Miss Cover Missing
--------------------------------------------------------------------------
src/stackoverflow/61333922/myclass.py 9 0 100%
src/stackoverflow/61333922/serial.py 6 3 50% 3-4, 7
src/stackoverflow/61333922/test_myclass.py 25 0 100%
--------------------------------------------------------------------------
TOTAL 40 3 92%

Related

I'm struggling to create a unit test, if someone could please have a look on my code to figure out the possible issue:

The code below is supposed to simulate a ATM, I have to create 5 unit tests for the code, and to be honest I have no idea why it is not working! :(
It should show on the Terminal that ran 5 tests in x s, however it keeps saying that Ran 0 tests and OK. Should I import a library? Any suggestion?
import unittest
def withdraw(wdra):
balanace_account = 100
if wdra < balanace_account:
balanace_account -= wdra
return balanace_account
class AtmTest(unittest.TestCase):
def correct_amount(self):
expected = 29.50 #withdraw de 70.50
result = withdraw(70.5)
self.assertEqual(expected,result)
def invalid_error(self):
expected = 1
result = withdraw(1/0)
self.assertEqual(expected, result)
def incorrect_amount(self):
expected = 50.00
result = withdraw(60)
self.assertEqual(expected,result)
def greater_withdraw(self):
expected = -10
result = withdraw(110)
self.assertEqual(expected,result)
def invalid_data_type(selfS):
expected = 100
result = withdraw('0')
self.assertEqual(expected, result)
if __name__ == '__main__':
unittest.main()
pass
You should name your test methods with test prefix so that the test runner can identify test methods. See here for example.

my inherited class(boss) won't accept the 3 parameters the main class(employee) accepts

What is wrong with my code? when i create a new Boss class i can't seem to get all the parameters from employe to work.
months = 6
class Employee(object):
balance = 0
company_income = 10000000000000000
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.pay = pay
self.tasks = {}
self.fullname = "{} {}".format(self.first, self.last)
self.email = "{}.{}#Otterstad.com".format(self.first, self.last)
def finishTask(self, task):
del self.tasks[task]
def getMonths(self):
print self.pay * months
class Manager(Employee):
def __init__(self, first, last, pay):
super().__init__(first, last, pay)
self.employees = []
self.pay += 10000
def addTask(self, task):
if len(self.tasks) == 0:
self.tasks[1] = task
else:
self.tasks[len(self.tasks)+1] = task
class Boss(Employee):
def __init__(self, first, last, pay):
super().__init__(first, last, pay)
self.employees = []
def changeSalary(self, new_salary):
self.pay = new_salary
def giveBonus(self, bonus, employee):
employee.balance += bonus
def addTask(self, task):
if len(self.tasks) == 0:
self.tasks[1] = task
else:
self.tasks[len(self.tasks)+1] = task
st1 = Boss("test", "test", 2000000000)
You are calling super in way that is correct for Python 3, not 2.
In Boss.__init__, change
super().__init__(first, last, pay)
to
super(Boss, self).__init__(first, last, pay).
Similarly, change the other calls to super throughout your code.

How I can invoke importing class in other class Python

#!/usr/bin/env python
from __future__ import print_function
import sys
import time
import getopt
import alsaaudio
import numpy
from time import sleep
class A_weight():
def __init__(self):
skaler = 2.361E-14
fix_cur = 0.20565360419770495
A = []
hPa = 4e-11
card = 'default'
array_float = numpy.dtype(float)
stream = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, alsaaudio.PCM_NORMAL, card)
stream.setchannels(1)
stream.setrate(48000)
stream.setformat(alsaaudio.PCM_FORMAT_S16_LE)
stream.setperiodsize(128)
def A(f):
return (12200**2*f**4/((f**2+20.6**2)*(f**2+12200**2)*numpy.sqrt(f**2+107.7**2)*numpy.sqrt(f**2+737.9**2)))+fix_cur
def listen(self):
glob_leq = 0
liczba_ramek = 0
index_ramek = 0
while True:
try:
l, data = stream.read()
except IOError, e:
error_count += 1
print(" (%d) Error recording: %s" % (error_count, e))
else:
if l==128:
decoded_block = numpy.frombuffer(data, dtype='int16' )
else:
continue
Y = numpy.fft.fft(decoded_block) # fft computing and normalization
Aw = A(numpy.arange(20.,20000,(19980./len(Y))))
Na = Aw*Y
inverse = numpy.fft.ifft(Y)
maks = 32768
array_float = numpy.divide(inverse.real ,float( maks))
array_float = array_float**2
sum_array = numpy.sum(array_float, dtype=float)
glob_leq = glob_leq + sum_array
liczba_ramek += 1
index_ramek += 1
if index_ramek == 375:
index_ramek=0
cis_chwil = numpy.divide(glob_leq, liczba_ramek * 128)
leq =10*numpy.log10(numpy.divide(cis_chwil, hPa))
print (leq)
#A.append(leq)
#print(max(A))
A_weight().listen()
So i trying writing program compute sound pressure level with weighting A.
All work correct but when i want close may code in class I have problem. Because something wrong with invoke to importing class in this case is it alsaaudio.
I get this feedback:
Traceback (most recent call last):
File "rec_A.py", line 64, in <module>
A_weight().listen()
File "rec_A.py", line 37, in listen
l, data = stream.read()
NameError: global name 'stream' is not defined
Do you have any idea
Change each occurrence of stream to self.stream:
class A_weight():
def __init__(self):
skaler = 2.361E-14
fix_cur = 0.20565360419770495
A = []
hPa = 4e-11
card = 'default'
array_float = numpy.dtype(float)
self.stream = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, alsaaudio.PCM_NORMAL, card)
self.stream.setchannels(1)
self.stream.setrate(48000)
self.stream.setformat(alsaaudio.PCM_FORMAT_S16_LE)
self.stream.setperiodsize(128)
...
def listen(self):
glob_leq = 0
liczba_ramek = 0
index_ramek = 0
while True:
try:
l, data = self.stream.read()
...
This will make it an instance variable, and all other methods of that class (as long as they are passed the self argument) will have access to it through self.stream. See this bit of documentation for more details on instance variables.
Also, this is merely an aesthetic point, but the convention in Python is to use upper camel case for class names, i.e., AWeight instead of A_weight - but this will not affect how your code runs.

multiprocessing - pyodbc IOError: bad message length

I am unexpectedly getting IOError: bad message length error when trying to share pyodbc connection across multiple processes, especially when N is more than 4 (no. of cores). Sometimes I also get cPickle.UnpicklingError: invalid load key, '#'., pyodbc.ProgrammingError: ('24000', '[24000] [FreeTDS][SQL Server]Invalid cursor state (0) (SQLExecDirectW)') as errors.
# Import custom python packages
import multiprocessing
import multiprocessing.managers as mm
import pathos.multiprocessing as mp
import pyodbc, datetime, time
class MyConn(object):
def __init__(self):
self.conn = None
self.cursor = None
def connect_to_db(self):
self.conn = pyodbc.connect("DSN=cpmeast;UID=dntcore;PWD=dntcorevs2")
self.cursor = self.conn.cursor()
def run_qry(self, data):
print 'Running query', data
self.cursor.execute("WAITFOR DELAY '00:00:01';select GETDATE(), '"+str(data)+"';")
l = self.cursor.fetchall()
_l = []
for i in l:
_l.append(list(i))
print 'Result for query', data, _l
return _l
class MyManagerClass(object):
def __init__(self):
self.result = multiprocessing.Manager().list()
def read_data(self, *args):
conn = args[0][0]
data = args[0][1]
l = conn.run_qry(data)
self.result.append(l)
class MyManager(mm.BaseManager):
pass # Pass is really enough. Nothing needs to be done here.
def main():
time_start = time.time()
MyManager.register("MyConn", MyConn)
manager = MyManager()
manager.start()
a = manager.MyConn()
a.connect_to_db()
dbm = MyManagerClass()
pool = mp.ProcessingPool(4)
jobs = []
N = 5
for i in range(N):
jobs.append((a, str(i)))
for i in pool.imap(dbm.read_data, jobs):
print 'result'
pool.close()
pool.join()
print 'Result', dbm.result
print 'Closed'
time_stop = time.time()
msg = 'runtime: {0}'.format(str(datetime.timedelta
(seconds=time_stop-time_start)))
print msg
if __name__ == '__main__':
main()

Cannot Pool.map() function because of UnpickleableError?

So I am trying to multi process function F. Which is accessed by a button press with tkinter.
def f(x):
global doom,results,info
doom = doom + 1
if check(x) == True:
results.add(x)
info.append(get_column_number(x))
j.step(1)
texx = "1/"+doom
s.configure(text=texx)
root.update()
The function is called within a function like so:
def dojob():
index = ['URLS'...]
pool = Pool(processes=4)
s.configure(text="Shifting Workload to cores..")
root.update()
pool.map(f, index)
The button is inside root window.
I get the following error:
Exception in thread Thread-2:
Traceback (most recent call last):
File "C:\Python27\lib\threading.py", line 808, in __bootstrap_inner
self.run()
File "C:\Python27\lib\threading.py", line 761, in run
self.__target(*self.__args, **self.__kwargs)
File "C:\Python27\lib\multiprocessing\pool.py", line 342, in _handle_tasks
put(task)
UnpickleableError: Cannot pickle <type 'tkapp'> objects
I do not even know what a pickle does? Help?
Here is the complete code:
from Tkinter import *
from ttk import *
from tkMessageBox import showinfo
from multiprocessing import Pool
import random
emails = set()
import urllib2
import urllib2 as urllib
########
CONSTANT_PAGECOUNT = 20
######
def f(x):
global doom,emails,info
doom = doom + 1
if check(x) == True:
print "",
emails.add(x)
info.append(get_column_number(x))
j.step(1)
texx = "Sk1nn1n "+str(doom)+'/'+str(CONSTANT_PAGECOUNT)+""
s.configure(text=texx)
root.update()
return 0
def f(x):
print ""
def showFile(site,info):
top = Toplevel()
top.title('Sites')
x = Text(top)
x.pack()
i=0
for site_url in site:
x.insert(END,site_url)
i=i+1
def get_column_number(url):
return True
def check(url):
return True
def getgoogleurl(search,siteurl=False,startr=0):
if siteurl==False:
return 'http://www.google.com/search?q='+urllib2.quote(search)+'&start='+str(startr)+'&oq='+urllib2.quote(search)
else:
return 'http://www.google.com/search?q=site:'+urllib2.quote(siteurl)+'%20'+urllib2.quote(search)+'&oq=site:'+urllib2.quote(siteurl)+'%20'+urllib2.quote(search)
def getgooglelinks(search,siteurl=False,startr=0):
#google returns 403 without user agent
headers = {'User-agent':'Mozilla/11.0'}
req = urllib2.Request(getgoogleurl(search,siteurl,startr),None,headers)
site = urllib2.urlopen(req)
data = site.read()
site.close()
#no beatifulsoup because google html is generated with javascript
start = data.find('<div id="res">')
end = data.find('<div id="foot">')
if data[start:end]=='':
#error, no links to find
return False
else:
links =[]
data = data[start:end]
start = 0
end = 0
while start>-1 and end>-1:
#get only results of the provided site
if siteurl==False:
start = data.find('<a href="/url?q=')
else:
start = data.find('<a href="/url?q='+str(siteurl))
data = data[start+len('<a href="/url?q='):]
end = data.find('&sa=U&ei=')
if start>-1 and end>-1:
link = urllib2.unquote(data[0:end])
data = data[end:len(data)]
if link.find('http')==0:
links.append(link)
return links
def rip(results=15,accuracy=16):
global e
keyword = ''+str(e.get())
if keyword.strip()=="":
s.configure(text="Please enter a keyword")
root.update()
return 0
linklist = []
counter = 0
doom = 0
while counter < results:
links = getgooglelinks(keyword,startr=counter)
for link in links:
if len(linklist) > CONSTANT_PAGECOUNT:
s.configure(text="Proccessing..")
root.update()
return linklist
else:
doom = doom + 1
linklist.append(link)
texx = str(doom)+"/"+str(CONSTANT_PAGECOUNT)
s.configure(text=texx)
root.update()
root.update()
counter = counter+accuracy
return linklist
def flip():
global e
emails = set()
info = []
keyword = ''+str(e.get())
if keyword.strip()=="":
s.configure(text="Please enter a keyword")
root.update()
return 0
s.configure(text="Generating index..")
root.update()
doom = -1
index = rip(CONSTANT_PAGECOUNT,10)
if 1:
try:
pool = Pool(processes=4)
#s.configure(text="Shifting Workload to cores..")
#root.update()
pool.map(f, index)
pool.close()
except:
print "The errors there.."
j.config(value=CONSTANT_PAGECOUNT)
if len(emails) > 0:
filepath='relavant_list_'+str(random.randint(1,9999))+'.emList.txt'
#print len(emails),
#print "emails found."
ggg = open(filepath,'a+')
for x in emails:
ggg.write(x+"\n")
showinfo(
str(len(emails))+" key word related sites found!",
" sites are saved in "+str(filepath)
)
showFile(emails,info)
s.configure(text=filepath)
else:
s.configure(text='No related sites found : (')
if __name__ == '__main__':
### CONSTANTS
version = '1.0'
### END CONSTANTS
root = Tk()
root.title('Program v'+version)
s = Style()
s.theme_use('default')
#print s.theme_names()
s.configure("black.Horizontal.TProgressbar", foreground='blue', background='blue')
j = Progressbar(root, style="black.Horizontal.TProgressbar", orient="vertical", length=200, mode="determinate", maximum=CONSTANT_PAGECOUNT, value=0)
j.pack(side='right',fill='y')
f = Frame(root)
x = Frame(f)
e = Entry(x,width=51)
s = Label(x,width=50,anchor='center',text='Waiting for task..')
Button(f,text='Generate List!',width=50,command=flip).pack(fill='both',expand=True)
s.pack(side='bottom',fill='y',expand=True)
e.pack(side='top',fill='both',expand=True)
x.pack(side='top',fill='y',expand=True)
f.pack(side='left',expand=True,fill="both")
root.mainloop()
You are leaking a tkinter object. Most likely because you are trying to update the interface from another process with the last line of f()
Update based on code
You have a name collision between your function f() and a variable f in your __main__ which gets assigned to your main window and causes the tkapp pickle error. Rename the function to def myfunc() or something. Also need to call pool.join() after pool.close()