Python 测试用户邀请系统
我在Flask应用程序中构建了一个用户邀请系统,并希望使用Python unittest对其进行测试。我正在使用SendGrid发送电子邮件。如何阻止SendGrid在测试中实际发送电子邮件?我可以接收用户邀请链接并将其拉入测试吗?我的代码如下:Python 测试用户邀请系统,python,unit-testing,flask,Python,Unit Testing,Flask,我在Flask应用程序中构建了一个用户邀请系统,并希望使用Python unittest对其进行测试。我正在使用SendGrid发送电子邮件。如何阻止SendGrid在测试中实际发送电子邮件?我可以接收用户邀请链接并将其拉入测试吗?我的代码如下: # views.py @app.route('/add_user', methods=['GET', 'POST']) @login_required @groups_required(['site_admin']) def add_user():
# views.py
@app.route('/add_user', methods=['GET', 'POST'])
@login_required
@groups_required(['site_admin'])
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')
# catch and display SendGrid errors
except SendGridClientError as err:
flash(err.message.get('message'))
except SendGridServerError as err:
flash(err.message.get('message'))
return render_template('dashboard/add_user.html', form=form)
@app.route('/add_user_confirm/<token>', methods=['GET', 'POST'])
def add_user_confirm(token):
"""
Decode invite token and create new user account
"""
form = RegistrationForm()
decoded = None
try:
ts = URLSafeTimedSerializer(app.config['SECRET_KEY'])
decoded = ts.loads(token, max_age=86400)
email = decoded[0]
except:
abort(404)
if form.validate_on_submit():
try:
tenant_id = decoded[1]
data = {}
data['email'] = email
data['password'] = request.form['password']
# given_name and surname are required fields
data['given_name'] = 'Anonymous'
data['surname'] = 'Anonymous'
# set tenant id and site_admin status
data['custom_data'] = {
'tenant_id': tenant_id,
'site_admin': 'False'
}
# create account
account = User.create(**data)
# add user to tenant group
account.add_group(tenant_id)
# login user
login_user(account, remember=True)
# success redirect
return render_template('account/add_user_complete.html')
except StormpathError as err:
flash(err.message.get('message'))
elif request.method == 'POST':
flash("Passwords don't match.")
return render_template('account/add_user_setpassword.html',
form=form,
email=email)
# tests.py
def test_add_user(self):
resp = self.client.post('/add_user', data={
'email': self.test_email
}, follow_redirects=True)
assert 'User invitation sent' in resp.data
#views.py
@app.route('/add_user',methods=['GET','POST'])
@需要登录
@需要组(['site\u admin'])
def add_user():
"""
向受邀请用户发送带有令牌的邀请电子邮件
"""
form=AddUserForm()
if form.validate_on_submit():
#令牌序列化程序
ts=URLSafeTimedSerializer(app.config['SECRET\u KEY'])
电子邮件=请求。表格['email']
租户id=user.custom\u数据['tenant\u id']
#创建包含电子邮件和租户id的令牌
令牌=ts.dumps([电子邮件,租户id])
#使用令牌创建url,例如/add\u user\u confirm/asdf asd fasdf
确认的url=url(
“添加用户确认”,
令牌=令牌,
_外部=真)
尝试:
#sendgrid设置
sg=sendgrid.SendGridClient(
app.config['SENDGRID\u API\u KEY'],
raise_errors=True
)
#电子邮件设置
message=sendgrid.Mail(
to=请求。表单['email'],
主题为“帐户邀请”,
html='您已被邀请在PhotogApp上设置帐户。单击此处:'+确认\u url,
来自support@photogapp.com'
)
#发送电子邮件
状态,msg=sg.send(消息)
flash('邀请已成功发送')
返回呈现模板('dashboard/add_user_complete.html')
#捕获并显示SendGrid错误
除SendGridClientError作为错误外:
flash(err.message.get('message'))
除SendGridServerError作为错误外:
flash(err.message.get('message'))
返回呈现模板('dashboard/add_user.html',form=form)
@app.route('/add\u user\u confirm/',方法=['GET','POST'])
def添加用户确认(令牌):
"""
解码邀请令牌并创建新用户帐户
"""
表单=注册表单()
已解码=无
尝试:
ts=URLSafeTimedSerializer(app.config['SECRET\u KEY'])
解码=ts.loads(令牌,最大年龄=86400)
电子邮件=已解码[0]
除:
中止(404)
if form.validate_on_submit():
尝试:
租户id=已解码[1]
数据={}
数据['email']=电子邮件
数据['password']=request.form['password']
#给定的名称和姓氏是必填字段
数据['given_name']=“匿名”
数据[‘姓氏’]=“匿名”
#设置租户id和站点管理员状态
数据['custom_data']={
“租户id”:租户id,
“站点管理”:“错误”
}
#创建帐户
帐户=用户。创建(**数据)
#将用户添加到租户组
帐户。添加组(租户id)
#登录用户
登录用户(帐户,记住=True)
#成功重定向
返回呈现模板('account/add\u user\u complete.html')
除StormpathError作为错误外:
flash(err.message.get('message'))
elif request.method==“POST”:
闪存(“密码不匹配”)
返回呈现模板('account/add_user_setpassword.html',
形式=形式,
电子邮件=电子邮件)
#tests.py
def测试添加用户(自身):
resp=self.client.post('/add_user',data={
“电子邮件”:self.test\u电子邮件
},follow_redirects=True)
在响应数据中断言“已发送用户邀请”
所以这是一个非常适合模仿的案例。模拟背后的基本思想是用普通的通过、失败和副作用替换当前的功能(在本例中是发送电子邮件)
否则你只能说
from unittest import mock
这样,当调用sg.send
时,它将调用模拟函数而不是实际函数。由于mock默认为空,因此没有可怕的电子邮件发送
注意:
SendGrid
需要导入,并且在test.py
文件的范围内。这很好。我以前从未听说过mock。我正在尝试让它工作,但遇到了“ImportTerror:这里是我设置“sg”的地方”:今晚我没有时间查看您的代码,但我将尝试明天运行它并向您报告。如果我忘了,戳我一下。我最后把问题作为一个单独的问题问了出来,得到了很好的回答。谢谢
# tests.py
from unittest import mock
@mock.patch("sg.send")
def test_add_user(self, mocked_send):
mocked_send.return_value = None # Do nothing on send
resp = self.client.post('/add_user', data={
'email': self.test_email
}, follow_redirects=True)
assert 'User invitation sent' in resp.data