iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 6
1
自我挑戰組

DevOps學習之旅系列 第 6

Day 6 Mock 系統測試程式

簡介

上一篇介紹了 Pyramid 開發 API 並使用 Postman 來測試功能是否正常,接下來會寫 unitest、function test,後面會導入自動化測試,測試自動化也是 DevOps 很重要的一環,以下是我們碰到導入測試程式的困難點,提出來讓大家一起思考.

阻礙:

  1. 很多時候開發系統並不會有多餘的時間來寫測試程式,尤其是專案時程很趕的時候,更不可能!
  2. 系統開發中,常常會去修改程式,測試程式也需要一併修改,會多花時間來維護測試程式的正確性

解決思路:

  1. 我們覺得在時程在趕的時候,先以功能完成度為主,只做最基本的測試,有寫總比沒有寫好.
  2. 讓有時間的非主要開發人員也可以幫忙寫測試程式(Test Case).
  3. 專案到一定的進度之後再補上.
  4. 在規劃專案時程的時候,還是希望有規劃寫測試的時間,會比較好.

Model 測試

test_initdb.py : 測試初始化 DB script

import os
import unittest
from ithome_pellok_2018.scripts.initializedb import main

class TestInitializeDB(unittest.TestCase):

    def test_usage(self):

        with self.assertRaises(SystemExit):
            main(argv=['ithome_pellok_2018'])

    def test_run(self):
        path = os.path.abspath(os.path.join(os.path.dirname("__file__")))
        main(argv=['ithome_pellok_2018', 
                   '{}/development.ini'.format(path)])
        self.assertTrue(os.path.exists('{}/ithome_pellok_2018.sqlite'.format(path)))
        os.remove('{}/ithome_pellok_2018.sqlite'.format(path))

test_mymodel_model.py : 測試對應資料表

import unittest
import transaction

from pyramid import testing

from ithome_pellok_2018.models import (
            get_tm_session,
            )
from ithome_pellok_2018.models.meta import Base
from ithome_pellok_2018.models import MyModel


class BaseTest(unittest.TestCase):

    def setUp(self):
        self.config = testing.setUp(settings={
            'sqlalchemy.url': 'sqlite:///:memory:'
        })
        self.config.include('ithome_pellok_2018.models')

        session_factory = self.config.registry['dbsession_factory']
        self.session = get_tm_session(session_factory, transaction.manager)

        self.init_database()

    def init_database(self):
        session_factory = self.config.registry['dbsession_factory']
        engine = session_factory.kw['bind']
        Base.metadata.create_all(engine)

    def tearDown(self):
        testing.tearDown()
        transaction.abort()

    def create_mymodel(self, name, value):
        return MyModel(name=name, value=value)

class TestMymodel(BaseTest):

    def test__saved(self):
        name = 'foo'
        value = 'bar'
        mymodel = self.create_mymodel(name=name, value=value)

        self.assertEqual(mymodel.name, name)
        self.assertEqual(mymodel.value, value)

API Unitest 測試

test_api_unitest.py : 測試 API 輸入輸出格式功能是否正常

import unittest
import transaction

from pyramid import testing
from ithome_pellok_2018.models import (
            get_engine,
            get_session_factory,
            get_tm_session,
            )
from ithome_pellok_2018.models.meta import Base
from ithome_pellok_2018.models import MyModel
from ithome_pellok_2018.views.api import ApiView

def dummy_request(dbsession):
    return testing.DummyRequest(dbsession=dbsession)


class BaseTest(unittest.TestCase):
    def setUp(self):
        self.config = testing.setUp(settings={
            'sqlalchemy.url': 'sqlite:///:memory:'
        })
        self.config.include('ithome_pellok_2018.models')
        settings = self.config.get_settings()
        self.engine = get_engine(settings)
        session_factory = get_session_factory(self.engine)
        self.session = get_tm_session(session_factory, transaction.manager)

    def init_database(self):
        Base.metadata.create_all(self.engine)

    def tearDown(self):
        testing.tearDown()
        transaction.abort()
        Base.metadata.drop_all(self.engine)


class TestApiView(BaseTest):

    def setUp(self):
        super(TestApiView, self).setUp()
        self.init_database()

    def create_mymodel(self, name='one', value='123'):
        """
        創建 Table 資料
        """
        mymodel = MyModel(name=name, value=value)
        self.session.add(mymodel)
        mymodel = self.session.query(MyModel)\
                      .filter(MyModel.name == name).first()
        return mymodel

    def test_create(self):
        """
        測試 創建 API 資料
        """
        request = dummy_request(self.session)
        name = 'one'
        value = 'test'
        request.params = {'name': name, 'value': value}

        api_view = ApiView(request)
        info = api_view.create()
        self.assertEqual(info.get('code', 500), 200)
        self.assertEqual(info.get('status', False), True)
        self.assertEqual(info.get('response', {}).get('name'), name)
        self.assertEqual(info.get('response', {}).get('value'), value)

        return info.get('response', {})

    def test_search(self):
        """
        測試 搜尋 API 資料
        """
        request = dummy_request(self.session)
        api_view = ApiView(request)
        info = api_view.search()
        self.assertEqual(info.get('code', 500), 200)
        self.assertEqual(info.get('status', False), True)
        self.assertEqual(len(info.get('response')), 0)


    def test_get(self):
        """
        測試 讀取 API 資料
        """
        mymodel = self.create_mymodel()

        request = dummy_request(self.session)
        request.matchdict['id'] = mymodel.id
        api_view = ApiView(request)
        info = api_view.get()
        self.assertEqual(info.get('code', 500), 200)
        self.assertEqual(info.get('status', False), True)
        self.assertEqual(info.get('response', {}).get('name'), mymodel.name)
        self.assertEqual(info.get('response', {}).get('value'), mymodel.value)

    def test_update(self):
        """
        測試 更新 API 資料
        """
        mymodel = self.create_mymodel()

        request = dummy_request(self.session)
        request.matchdict['id'] = mymodel.id
        name = 'one'
        value = 'test'
        request.params = {'name': name, 'value': value}
        api_view = ApiView(request)
        info = api_view.edit()
        self.assertEqual(info.get('code', 500), 200)
        self.assertEqual(info.get('status', False), True)
        self.assertEqual(info.get('response', {}).get('name'), name)
        self.assertEqual(info.get('response', {}).get('value'), value)


    def test_delete(self):
        """
        測試 刪除 API 資料
        """
        mymodel = self.create_mymodel()

        request = dummy_request(self.session)
        request.matchdict['id'] = mymodel.id
        api_view = ApiView(request)
        info = api_view.delete()
        self.assertEqual(info.get('code', 500), 200)
        self.assertEqual(info.get('status', False), True)
        self.assertEqual(info.get('response'), 'OK')

安裝測試插件

會安裝 setup.py 的 test_require

tests_require = [
    'WebTest >= 1.3.1',  # py3 compat
    'pytest',  # includes virtualenv
    'pytest-cov',
    ]

安裝指令

cd ithome_pellok_2018
env/bin/pip install -e .[testing]

執行 py.test

指令執行

source env/bin/activate
py.test

env/bin/py.test

source env/bin/activate
python setup.py test

結論

以上是透過 py.test 來實作測試程式

參考

bitbucket Repostory
pyramid tests


上一篇
Day 5 Mock 系統開發
下一篇
Day 7 Mock 系統頁面開發
系列文
DevOps學習之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言