我试图在我的SendGrid视图函数中模拟这个方法,这样它就不会在测试期间发送电子邮件了。当我运行下面的代码时,我得到一个错误'ImportError: No模块名为sg‘。如何正确配置'sg‘方法,以便在测试中找到它?
# test_helpers.py
from unittest import TestCase
from views import app
class PhotogTestCase(TestCase):
def setUp(self):
app.config['WTF_CSRF_ENABLED'] = False
app.config['TESTING'] = True
self.app = app
self.client = app.test_client()
# test_views.py
import mock
from test_helpers import PhotogTestCase
import sendgrid
class TestAddUser(PhotogTestCase):
sg = sendgrid.SendGridClient(app.config['SENDGRID_API_KEY'])
@mock.patch('sg.send')
def test_add_user_page_loads(self, mocked_send):
mocked_send.return_value = None # Do nothing on send
resp = self.client.post('/add_user', data={
'email': 'joe@hotmail.com'
}, follow_redirects=True)
assert 'Wow' in resp.data
# views.py
import sendgrid
from itsdangerous import URLSafeTimedSerializer
from flask import Flask, redirect, render_template, \
request, url_for, flash, current_app, abort
from flask.ext.stormpath import login_required
from forms import RegistrationForm, AddContactForm, \
AddUserForm
@app.route('/add_user', methods=['GET', 'POST'])
@login_required
def add_user():
"""
Send invite email with token to invited user
"""
form = AddUserForm()
if form.validate_on_submit():
# token serializer
ts = URLSafeTimedSerializer(app.config['SECRET_KEY'])
email = request.form['email']
tenant_id = user.custom_data['tenant_id']
# create token containing email and tenant_id
token = ts.dumps([email, tenant_id])
# create url with token, e.g. /add_user_confirm/asdf-asd-fasdf
confirm_url = url_for(
'add_user_confirm',
token=token,
_external=True)
try:
# sendgrid setup
sg = sendgrid.SendGridClient(
app.config['SENDGRID_API_KEY'],
raise_errors=True
)
# email setup
message = sendgrid.Mail(
to=request.form['email'],
subject='Account Invitation',
html='You have been invited to set up an account on PhotogApp. Click here: ' + confirm_url,
from_email='support@photogapp.com'
)
# send email
status, msg = sg.send(message)
flash('Invite sent successfully.')
return render_template('dashboard/add_user_complete.html')
return render_template('dashboard/add_user.html', form=form)发布于 2015-10-23 20:56:31
解释
模拟必须根据您正在测试的位置而不是实现方法的位置来实现。或者,在您的示例中,从unittest中模拟sg对象将无法工作。
所以,我不太清楚你的项目的结构是什么。但希望这个例子有帮助。
您需要确保您还在引用该类要模拟的位置的适当位置,以正确模拟其方法。
解决方案
因此,让我们假设您是从test.py运行测试的:
test.py
your_app/
views.py
tests/
all_your_tests.py在views.py内部,您将像这样导入send:
from module_holding_your_class import SendGridClient因此,要查看您的mock.patch,应该如下所示:
@mock.patch('your_app.views.SendGridClient.send')
def test_add_user_page_loads(self, mocked_send):正如您所看到的,您是从test.py运行的,因此您的导入是从那里引用的。这就是我建议在实际运行代码的地方运行您的测试的地方,这样您就不必处理您的导入了。
此外,您还在模拟您在views.py中调用的views.py。
那应该管用。告诉我事情进展如何。
额外信息:模拟类的实例
因此,根据您的代码,如果您实际上模拟出了类的一个实例,那么它可能会对您更有好处。通过这种方式,您可以很容易地在SendGridClient实例的单个模拟中,甚至在Mail中测试所有方法。这样,您就可以专注于方法的显式行为,而不必担心外部的功能。
要完成类实例的模拟(或者在您的示例2中),您必须执行类似的操作(内联解释)
*这个具体的例子是未经检验的,很可能不完整。目标是让您了解如何操作模拟和数据以帮助您的测试。
在下面,我有一个经过充分测试的示例可供使用。
@mock.patch('your_app.views.Mail')
@mock.patch('your_app.views.SendGridClient')
def test_add_user_page_loads(self, m_sendgridclient, m_mail):
# get an instance of Mock()
mock_sgc_obj = mock.Mock()
mock_mail_obj = mock.Mock()
# the return of your mocked SendGridClient will now be a Mock()
m_sendgridclient.return_value = mock_sgc_obj
# the return of your mocked Mail will now be a Mock()
m_mail.return_value = mock_mail_obj
# Make your actual call
resp = self.client.post('/add_user', data={
'email': 'joe@hotmail.com'
}, follow_redirects=True)
# perform all your tests
# example
self.assertEqual(mock_sgc_obj.send.call_count, 1)
# make sure that send was also called with an instance of Mail.
mock_sgc_obj.assert_called_once_with(mock_mail_obj)根据您提供的代码,我不确定Mail到底返回了什么。我假设它是Mail的一个对象。如果是这样的话,那么上面的测试用例就足够了。但是,如果您希望测试message本身的内容,并确保每个对象属性中的数据都是正确的,那么我强烈建议将您的单元测试分开,以便在Mail类中处理它,并确保数据按预期运行。
这样做的想法是,您的add_user方法不应该关心验证该数据。只是那个对象打了个电话。
此外,在send方法本身中,您可以在其中进行进一步的单元测试,以确保您输入到该方法的数据得到相应的处理。这样你的生活就容易多了。
示例
下面是我所测试的一个例子,我希望能帮助进一步澄清这一点。您可以将其复制到编辑器中并运行。注意我对__main__的使用,它是指我从哪里取笑。在这种情况下,它是__main__。
此外,我还会使用side_effect和return_value (看看我的例子)来了解两者之间的不同行为。side_effect将返回被执行的内容。在您的示例中,您希望看到在执行方法send时发生了什么。
每个单元测试都以不同的方式进行嘲弄,并展示您可以应用的不同用例。
import unittest
from unittest import mock
class Doo(object):
def __init__(self, stuff="", other_stuff=""):
pass
class Boo(object):
def d(self):
return 'the d'
def e(self):
return 'the e'
class Foo(object):
data = "some data"
other_data = "other data"
def t(self):
b = Boo()
res = b.d()
b.e()
return res
def do_it(self):
s = Stuff('winner')
s.did_it(s)
def make_a_doo(self):
Doo(stuff=self.data, other_stuff=self.other_data)
class Stuff(object):
def __init__(self, winner):
self.winner = winner
def did_it(self, a_var):
return 'a_var'
class TestIt(unittest.TestCase):
def setUp(self):
self.f = Foo()
@mock.patch('__main__.Boo.d')
def test_it(self, m_d):
'''
note in this test, one of the methods is not mocked.
'''
#m_d.return_value = "bob"
m_d.side_effect = lambda: "bob"
res = self.f.t()
self.assertEqual(res, "bob")
@mock.patch('__main__.Boo')
def test_them(self, m_boo):
mock_boo_obj = mock.Mock()
m_boo.return_value = mock_boo_obj
self.f.t()
self.assertEqual(mock_boo_obj.d.call_count, 1)
self.assertEqual(mock_boo_obj.e.call_count, 1)
@mock.patch('__main__.Stuff')
def test_them_again(self, m_stuff):
mock_stuff_obj = mock.Mock()
m_stuff.return_value = mock_stuff_obj
self.f.do_it()
mock_stuff_obj.did_it.assert_called_once_with(mock_stuff_obj)
self.assertEqual(mock_stuff_obj.did_it.call_count, 1)
@mock.patch('__main__.Doo')
def test_them(self, m_doo):
self.f.data = "fake_data"
self.f.other_data = "some_other_fake_data"
self.f.make_a_doo()
m_doo.assert_called_once_with(
stuff="fake_data", other_stuff="some_other_fake_data"
)
if __name__ == '__main__':
unittest.main()https://stackoverflow.com/questions/33311001
复制相似问题