本文介绍了如何在Python中模拟SendGrid方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 我试图在我的Flask视图函数中模拟SendGrid方法,以便在测试期间不发送电子邮件。当我运行下面的代码时,我得到一个错误'ImportError:No module named sg'。如何正确配置'sg'方法以便在测试中找到? #test_helpers.py 从unittest import TestCase from views import app $ b $ class PhotogTestCase(TestCase): $ b def setUp(self): app.config ['WTF_CSRF_ENABLED' ] = False app.config ['TESTING'] = True self.app = app self.client = app.test_client() $ b $ test_views。 py 从test_helpers导入模拟导入PhotogTestCase 导入sendgrid $ b $ 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没有发送 resp = self.client.post('/ add_user',data = {'email':'[email protected]'},follow_redirects =真正)在resp.data中声明'Wow' #views.py 导入sendgrid $ b $从其危险导入URLSafeTimedSerializer $ b $从烧瓶导入烧瓶,重定向, render_template,\ 请求,url_for,flash,current_app,从flask.ext.stormpath中止从表单导入login_required RegistrationForm,AddContactForm,\ AddUserForm @ app.route('/ add_user',methods = ['GET','POST']) @login_required def add_user():发送带有令牌的邀请电子邮件给被邀请的用户 form = AddUserForm() $ b $如果form.validate_on_submit(): #token serializer ts = URLSafeTimedSerializer(app.config ['SECRET_KEY']) email = request.form ['email'] tenant_id = user.custom_data ['tenant_id '] #创建包含电子邮件和tenant_id的令牌 token = ts.dumps([email,tenant_id]) #使用令牌创建网址,例如/ add_user_confirm / asdf-asd-fasdf confirm_url = url_for('add_user_confirm',令牌=令牌, _external = True) 尝试: #sendgrid setup sg = sendgrid.SendGridClient( app.config ['SENDGRID_API_KEY'], raise_errors = True ) #电子邮件安装 message = sendgrid.Mail( to = request.form ['email'], subject ='账户邀请', html ='您已被邀请点击这里:'+ confirm_url, from_email='[email protected]') #发送电子邮件状态, 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) 解释 嘲笑必须针对您正在测试的位置执行,而不是您实施方法的位置。或者,在你的情况下,嘲笑unittest中的 sg 对象也是行不通的。 确切地确定你的项目的结构是什么。但希望这个例子有所帮助。 您需要确保您也正在引用您想要模拟的类的适当位置,以便正确地剔除其方法。 解决方案 所以,让我们假设您正在从test.py运行测试: test.py your_app / views.py 测试/ all_your_tests.py 里面的views.py,你正在导入发送像这样: from module_holding_your_class import SendGridClient 所以,看看你的mock.patch,它应该看起来像这样: $ $ p $ $ $ $ c $ @ mock.patch('your_app.views.SendGridClient.send') def test_add_user_page_loads(self,mocked_send): 正如您所看到的,您正在运行测试.py,所以你的导入是从那里引用的。这是我建议在你真正运行你的真实代码的地方运行你的测试的地方,这样你就不必乱搞你的导入。 另外,你正在嘲笑你正在调用views.py的 send 。 这应该是有效的。让我知道这是怎么回事。 额外信息:类的模仿实例 所以,根据你的代码,可能会更多如果你真的嘲笑你的班级实例,对你有利。这样,你可以非常容易地在 SendGridClient ,甚至 Mail 的实例模拟中测试所有的方法。这样你就可以专注于你的方法的显式行为,而不用担心外部的功能。 为了完成一个类的实例(或者在你的情况下是两个) ,你将不得不做这样的事情(解释内联) *这个具体的例子是未经测试,可能不完整。我们的目标是让您了解如何操纵模拟和数据来帮助您进行测试。 更进一步,下面我有一个经过充分测试的例子。 * $ $ p $ @ mock.patch('your_app.views.Mail') @ mock.patch('your_app。获取Mock()的一个实例 mock_sgc_obj = mock.Mock() mock_mail_obj = mock。 Mock() 你的模拟SendGridClient的返回现在是一个Mock() m_sendgridclient.return_value = mock_sgc_obj #你的模拟邮件的返回现在将是一个模拟() m_mail.return_value = mock_mail_obj #进行实际的通话 resp = self.client.post('/ add_user',data = {'email ':'[email protected]'},follow_redirects = True) #执行所有测试#示例 self.assertEqual(mo ck_sgc_obj.send.call_count,1)#确保发送也是用一个Mail实例调用的。 mock_sgc_obj.assert_called_once_with(mock_mail_obj) 根据您提供的代码,我不是确定 Mail 正在返回。我假设它是 Mail 的对象。如果是这样,那么上面的测试案例就足够了。但是,如果您正在测试 message 本身的内容,并确保每个对象属性中的数据都是正确的,我强烈建议分离您的unittests以处理它 Mail 类,并确保数据按预期行事。 这个想法是你的 add_user 方法不应该在意验证数据。就是打电话给对象。此外,在你的发送方法本身,你可以在那里进一步进行单元测试,以确保输入到该方法的数据得到相应的处理。这样可以让你的生活变得更轻松。 示例 下面是一个例子,我希望能进一步澄清这一点。你可以复制粘贴到你的编辑器并运行它。注意我使用 __ main __ ,这是表示我从哪里嘲笑。在这种情况下,它是 __ main __ 。 另外,我会玩弄 side_effect 和 return_value (查看我的示例)来查看两者之间的不同行为。 side_effect 会返回执行的内容。在你的情况下,你想看看当你执行发送方法时会发生什么。 每个单元测试都是以不同的方式进行模拟,并展示可以应用的不同用例。 / p> import unittest $ b $ from unittest import mock class Doo(object) : def __init __(self,stuff =,other_stuff =): pass $ b $ class Boo(object): def d (self): return'the d' def e(self): return'the e' class Foo (object): data =一些数据 other_data =其他数据 def t(self):b = Boo res = bd() be()返回res $ b $ 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) 类材料(对象): def __init __(self,winner): self.winner =赢家 def did_it(self,a_var):返回'a_var' class TestIt(unittest.TestCase): def setUp(self): self.f = Foo() @ mock.patch('__ main__ .Boo.d') def test_it(self,m_d):'''注意在这个测试中,其中一个方法没有被模拟。 '''#m_d.return_value =bob m_d.side_effect = lambda:bob res = self.ft() 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.ft() 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.dat a =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() I'm trying to mock the SendGrid method within my Flask view function, so that it does not send an email during testing. When I run the below code I get an error 'ImportError: No module named sg'. How can I properly configure the 'sg' method so it is found in testing?# test_helpers.pyfrom unittest import TestCasefrom views import appclass 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.pyimport mockfrom test_helpers import PhotogTestCaseimport sendgridclass 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': '[email protected]' }, follow_redirects=True) assert 'Wow' in resp.data# views.pyimport sendgridfrom itsdangerous import URLSafeTimedSerializerfrom flask import Flask, redirect, render_template, \ request, url_for, flash, current_app, abortfrom flask.ext.stormpath import login_requiredfrom forms import RegistrationForm, AddContactForm, \ [email protected]('/add_user', methods=['GET', 'POST'])@login_requireddef 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='[email protected]' ) # 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) 解决方案 ExplanationMocking has to be implemented with respect to where you are testing, and not where you have implemented the method. Or, also in your case, mocking the sg object from unittest will not work.So, I am not exactly sure what the structure of your project is. But hopefully this example helps.You need to make sure that you are also referencing the appropriate location of where that class is that you want to mock out, to properly mock out its methods.SolutionSo, let us assume you are running your tests from test.py: test.py your_app/ views.py tests/ all_your_tests.pyInside views.py, you are importing send like this: from module_holding_your_class import SendGridClientSo, to look at your mock.patch, it should look like this: @mock.patch('your_app.views.SendGridClient.send')def test_add_user_page_loads(self, mocked_send):As you can see, you are running from test.py, so your imports are with reference from there. This is where I suggest running your tests with respect to where you actually run your real code, so that you don't have to mess around with your imports.Furthermore, you are mocking the send that you are calling in views.py.That should work. Let me know how that goes.Extra Info: Mocking instance of a ClassSo, based on your code, it would probably be more beneficial for you if you actually mocked out an instance of your class. This way you can very easily test all your methods within that single mock of the instance of SendGridClient, or even Mail. This way you can focus on the explicit behaviour of your method without worrying about functionality from externals.To accomplish mocking out an instance of a Class (or in your case two), you will have to do something like this (explanation inline)*This specific example is untested and probably not complete. The goal is to get you to understand how to manipulate the mock and the data to help your testing.Further down below I have a fully tested example to play around with.*@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': '[email protected]' }, 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)Based on the code that you provided, I am not sure exactly what Mail is returning. I am assuming it is an object of Mail. If that is the case, then the above test case would suffice. However, if you are looking to test the content of message itself and make sure the data inside each of those object properties is correct, I strongly recommend separating your unittests to handle it in the Mail class and ensure that the data is behaving as expected.The idea is that your add_user method should not care about validating that data yet. Just that a call was made with the object.Furthermore, inside your send method itself, you can further unittest in there to make sure that the data you are inputting to the method is treated accordingly. This would make your life much easier.ExampleHere is a example I put together that I tested that I hope will help clarify this further. You can copy paste this in to your editor and run it. Pay attention to my use of __main__, it is to indicate where I am mocking from. In this case it is __main__.Also, I would play around with side_effect and return_value (look at my examples) to see the different behaviour between the two. side_effect will return something that gets executed. In your case you are wanting to see what happens when you execute the method send.Each unittest is mocking in different ways and showcasing the different use cases you can apply.import unittestfrom unittest import mockclass Doo(object): def __init__(self, stuff="", other_stuff=""): passclass 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() 这篇关于如何在Python中模拟SendGrid方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
08-20 04:44