title: Testing a user invitation system
body: <p>I built a user invitation system in my Flask application and want to test it with Python unittest. I'm using SendGrid to send emails. How can I stop SendGrid from actually sending an email while in a test? I can I receive the user invitation link and pull it into my test? My code is below:</p>
  
  <pre><code># 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/&lt;token&gt;', 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
  </code></pre>