第十五章 測試(二)

測試WEB服務

Flask測試客戶端還可用於測試REST式WEB服務。

tests/test_api.py:使用Flask測試客戶端測試REST式API

import unittest
import json
import re
from base64 import b64encode
from app import create_app, db
from app.models import User, Role, Post, Comment


class APITestCase(unittest.TestCase):
    def setUp(self):
        self.app = create_app('testing')
        self.app_context = self.app.app_context()
        self.app_context.push()
        db.create_all()
        Role.insert_rows()
        self.client = self.app.test_client()

    def tearDown(self):
        db.session.remove()
        db.drop_all()
        self.app_context.pop()

    @staticmethod
    def get_api_headers(username, password):
        return {
            'Authorization': 'Basic ' + b64encode((username + ":" + password).encode('utf-8')).decode('utf-8'),
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        }

    def test_404(self):
        response = self.client.get('/wrong/url', headers=self.get_api_headers('email', 'password'))
        self.assertEqual(response.status_code, 404)
        json_response = json.loads(response.get_data(as_text=True))
        self.assertEqual(json_response['error'], 'not found')

    def test_no_auth(self):
        response = self.client.get('/api/v1/posts/', content_type='application/json')
        self.assertEqual(response.status_code, 401)

    def test_bad_auth(self):
        # add a user
        r = Role.query.filter_by(name='User').first()
        self.assertIsNotNone(r)
        u = User(email='[email protected]', password='cat', confirmed=True, role=r)
        db.session.add(u)
        db.session.commit()

        # authenticated with the bad password
        response = self.client.get('/api/v1/posts/', headers=self.get_api_headers(u.email, 'dog'))
        self.assertEqual(response.status_code, 401)

    def test_token_auth(self):
        # add a user
        r = Role.query.filter_by(name='User').first()
        self.assertIsNotNone(r)
        u = User(email='[email protected]', password='cat', confirmed=True, role=r)
        db.session.add(u)
        db.session.commit()

        # issue a request with a bad token
        response = self.client.get('/api/v1/posts/', headers=self.get_api_headers('bad-token', ''))
        self.assertEqual(response.status_code, 401)

        # get a token
        response = self.client.post('/api/v1/token', headers=self.get_api_headers(u.email, 'cat'))
        self.assertEqual(response.status_code, 200)
        json_response = json.loads(response.get_data(as_text=True))
        self.assertIsNotNone(json_response.get('token'))
        token = json_response['token']

        # request with token
        response = self.client.get('/api/v1/posts/', headers=self.get_api_headers(token, ''))
        self.assertEqual(response.status_code, 200)

        # get a token with a token
        response = self.client.post('/api/v1/token', headers=self.get_api_headers(token, ''))
        self.assertEqual(response.status_code, 401)

    def test_anonymous(self):
        response = self.client.get('/api/v1/posts/', headers=self.get_api_headers('', ''))
        self.assertEqual(response.status_code, 401)

    def test_unconfirmed_account(self):
        # add an unconfirmed user
        r = Role.query.filter_by(name='User').first()
        self.assertIsNotNone(r)
        u = User(email='[email protected]', password='123', confirmed=False, role=r)
        db.session.add(u)
        db.session.commit()

        # get list of posts with unconfirmed account
        response = self.client.get('/api/v1/posts/', headers=self.get_api_headers(u.email, '123'))
        self.assertTrue(response.status_code, 403)

    def test_posts(self):
        # add user
        r = Role.query.filter_by(name='User').first()
        self.assertIsNotNone(r)
        u = User(email='[email protected]', password='123', confirmed=True, role=r)
        db.session.add(u)
        db.session.commit()

        # write an empty post
        response = self.client.post('/api/v1/posts/', headers=self.get_api_headers(u.email, '123'),
                                    data=json.dumps({'body': ''}))
        self.assertEqual(response.status_code, 400)

        # write valid post
        response = self.client.post('/api/v1/posts/', headers=self.get_api_headers(u.email, '123'),
                                    data=json.dumps({'body': 'a post from Bob.'}))
        self.assertEqual(response.status_code, 201)
        url = response.headers.get('Location')
        self.assertIsNotNone(url)

        # get the new post
        response = self.client.get(url, headers=self.get_api_headers(u.email, '123'))
        self.assertEqual(response.status_code, 200)
        json_response = json.loads(response.get_data(as_text=True))
        self.assertIsNotNone(json_response)
        self.assertEqual('http://localhost' + json_response['self_url'], url)
        self.assertEqual(json_response['body'], 'a post from Bob.')
        self.assertEqual(json_response['body_html'], '<p>a post from Bob.</p>')
        json_post = json_response

        # get the post from the user
        response = self.client.get('/api/v1/users/{}/posts/'.format(u.id),
                                   headers=self.get_api_headers(u.email, '123'))
        self.assertEqual(response.status_code, 200)
        json_response = json.loads(response.get_data(as_text=True))
        self.assertIsNotNone(json_response.get('posts'))
        self.assertEqual(json_response.get('count', 0), 1)
        self.assertEqual(json_response['posts'][0], json_post)

        # get the post from the user as a follower
        response = self.client.get('/api/v1/users/{}/followed/posts/'.format(u.id),
                                   headers=self.get_api_headers(u.email, '123'))
        self.assertEqual(response.status_code, 200)
        json_response = json.loads(response.get_data(as_text=True))
        self.assertIsNotNone(json_response.get('posts'))
        self.assertEqual(json_response.get('count', 0), 1)
        self.assertEqual(json_response['posts'][0], json_post)

        # edit post
        response = self.client.put(url, headers=self.get_api_headers(u.email, '123'),
                                   data=json.dumps({'body': 'updated body'}))
        self.assertEqual(response.status_code, 200)
        json_response = json.loads(response.get_data(as_text=True))
        self.assertEqual('http://localhost' + json_response['self_url'], url)
        self.assertEqual(json_response['body'], 'updated body')
        self.assertEqual(json_response['body_html'], '<p>updated body</p>')

       測試API時使用的setUp()和tearDown()方法與測試普通應用所用的一樣,不過API不使用cookie,無需配置cookie支持。get_api_headers()是一個輔助方法,返回多數API請求要求發送的通用首部,包括身份驗證憑據和MIME類型相關的首部。

       請求主體中發送的數據(即data參數的值)要使用json.dumps()進行編碼,因爲Flask測試客戶端不會自動編碼,因爲Flask測試客戶端不會自動編碼JSON格式數據。類似的返回的響應主體格式也是JSON,處理之前必須使用json.loads()方法解碼。

       當然了,還有一種簡便方法,使用json可直接傳遞json字典,對響應對象調用get_json()可以直接獲取解析後的字典。比如下面的測試,在對Comment新增時就用到了:

    def test_comments(self):
        # add two users
        r = Role.query.filter_by(name='User').first()
        self.assertIsNotNone(r)
        u1 = User(email='[email protected]', username='alice', password='alice', confirmed=True, role=r)
        u2 = User(email='[email protected]', username='bob', password='bob', confirmed=True, role=r)
        db.session.add_all([u1, u2])
        db.session.commit()

        # add a post
        post = Post(body='body of the post', author=u1)
        db.session.add(post)
        db.session.commit()

        # write a comment
        response = self.client.post('/api/v1/posts/{}/comments/'.format(u1.id),
                                    headers=self.get_api_headers(u2.email, 'bob'),
                                    json={'body': 'Good [post](http://example.com)!'})
        self.assertEqual(response.status_code, 201)
        # json_response = json.loads(response.get_data(as_text=True))
        json_response = response.get_json()
        url = response.headers.get('Location')
        self.assertIsNotNone(url)
        self.assertEqual(json_response['body'], 'Good [post](http://example.com)!')
        self.assertEqual(re.sub('<.*?>', '', json_response['body_html']), 'Good post!')

        # get the new comment
        response = self.client.get(url, headers=self.get_api_headers(u2.email, 'bob'))
        self.assertEqual(response.status_code, 200)
        json_response = json.loads(response.get_data(as_text=True))
        self.assertEqual('http://localhost' + json_response['self_url'], url)
        self.assertEqual(json_response['body'], 'Good [post](http://example.com)!')

        # add another comment
        comment = Comment(body='Thanks!', author=u1, post=post)
        db.session.add(comment)
        db.session.commit()

        # get the two comments form the post
        response = self.client.get('/api/v1/posts/{}/comments/'.format(post.id),
                                   headers=self.get_api_headers(u1.email, 'alice'))
        self.assertEqual(response.status_code, 200)
        json_response = json.loads(response.get_data(as_text=True))
        self.assertIsNotNone(json_response.get('comments'))
        self.assertEqual(json_response.get('total', 0), 2)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章