Skip to content

Python UnitTest Example #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ project/plugins/project/
.worksheet
.idea

# pycaret_example
# pycaret_example & python_test_example
*.csv
pycaret_example/notebook/.ipynb_checkpoints
pycaret_example/notebook/catboost_info
pycaret_example/notebook/catboost_info
__pycache__
4 changes: 4 additions & 0 deletions python_test_example/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM python:3.7-slim

RUN pip install --no-cache-dir --upgrade pip setuptools wheel \
&& pip install --no-cache-dir requests flask
15 changes: 15 additions & 0 deletions python_test_example/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
image = unittest
tag = 0.1.0
curdir := `pwd`

.PHONY: dbuild
dbuild:
docker build -t $(image):$(tag) .

.PHONY: drun
drun:
docker run --rm -it -v $(curdir):/opt -w /opt $(image):$(tag) bash

.PHONY: all_test
all_test:
python3 -m unittest discover --verbose --pattern "*_test.py"
Empty file.
24 changes: 24 additions & 0 deletions python_test_example/flask_app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import json

from flask import Flask, jsonify, make_response

from .service import Service

app = Flask(__name__)

print('Instantiate Service in app')
service = Service()

@app.route('/', methods=['GET'])
def index():
message = {'message': 'OK'}
return make_response(jsonify(message), 200)

@app.route('/predict', methods=['POST'])
def predict():
print('Call predict in app')
target = 'A'
if not service.check_model():
return make_response(jsonify({'message': 'Service Unavailable'}), 503)
result = {'result': service.predict(target)}
return make_response(jsonify(result), 200)
47 changes: 47 additions & 0 deletions python_test_example/flask_app/app_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import json
from unittest import TestCase, main
from unittest.mock import MagicMock, patch

print('In app_test')

class AppTestCase(TestCase):
def setUp(self):
print('Call setUp in AppTestCase')

self.load_model_patcher = patch('flask_app.model.load_model')
self.load_model_m = self.load_model_patcher.start()
self.load_model_m.return_value = 'test'

from flask_app.app import app
self.client = app.test_client()

def tearDown(self):
self.load_model_patcher.stop()

def test_index(self):
response = self.client.get('/')
self.assertEqual(response.status_code, 200)

def test_index_content(self):
response = self.client.get('/')
self.assertEqual(response.content_type, 'application/json')

def test_predict(self):
response = self.client.post('/predict')
self.assertEqual(response.status_code, 200)

def test_predict_content(self):
response = self.client.post('/predict')
self.assertEqual(response.content_type, 'application/json')

def test_predict_data(self):
response = self.client.post('/predict')
self.assertIsInstance(json.loads(response.data)['result'], float)

@patch('flask_app.service.Service.check_model', return_value=False)
def test_predict_503(self, mock: MagicMock):
response = self.client.post('/predict')
self.assertEqual(response.status_code, 503)

if __name__ == '__main__':
main()
3 changes: 3 additions & 0 deletions python_test_example/flask_app/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def load_model():
print('Call load_model in model')
return 'model'
20 changes: 20 additions & 0 deletions python_test_example/flask_app/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from random import randint

from .model import load_model

print('In service')

class Service:
def __init__(self):
print('Init Service')
self.model = load_model()
print(f'self.model: {self.model}')

def predict(self, target: str) -> float:
print('Call predict in service')
return randint(0, 1000) / 1000.0

def check_model(self) -> bool:
if self.model:
return True
return False
Empty file.
5 changes: 5 additions & 0 deletions python_test_example/i76_testcase_subclass/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
def concat(str1: str, str2: str) -> str:
if not isinstance(str1, str) and isinstance(str2, str):
raise TypeError

return str1 + str2
26 changes: 26 additions & 0 deletions python_test_example/i76_testcase_subclass/utils_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from unittest import TestCase, main

from i76_testcase_subclass.utils import concat

class UtilsTestCase(TestCase):
def test_good_for_concat(self):
test_cases = [
(('a', 'b'), 'ab'),
(('test', 'case'), 'testcase'),
]
for value, expected in test_cases:
with self.subTest(value):
self.assertEqual(expected, concat(value[0], value[1]))

def test_bad_for_concat(self):
test_cases = [
(('a', 2), TypeError),
((1, 'b'), TypeError),
]
for value, exception in test_cases:
with self.subTest(value):
with self.assertRaises(exception):
concat(value[0], value[1])

if __name__ == '__main__':
main()
Empty file.
25 changes: 25 additions & 0 deletions python_test_example/i77_setup_and_teardown/integration2_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from unittest import TestCase, main

class Integration2Test(TestCase):
@classmethod
def setUpClass(cls):
print('* Class setup')

@classmethod
def tearDownClass(cls):
print('* Class clean-up')

def setUp(self):
print('** Test setup')

def tearDown(self):
print('** Test clean-up')

def test_1(self):
print('** Test 1')

def test_2(self):
print('** Test 2')

if __name__ == '__main__':
main()
23 changes: 23 additions & 0 deletions python_test_example/i77_setup_and_teardown/integration_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from unittest import TestCase, main

def setUpModule():
print('* Module setup')

def tearDownModule():
print('* Module clean-up')

class IntegrationTest(TestCase):
def setUp(self):
print('** Test setup')

def tearDown(self):
print('** Test clean-up')

def test_1(self):
print('** Test 1')

def test_2(self):
print('** Test 2')

if __name__ == '__main__':
main()
Empty file.
6 changes: 6 additions & 0 deletions python_test_example/mock/my_class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import requests

class MyClass:
def fetch_json(self, url: str) -> dict:
response = requests.get(url)
return response.json()
44 changes: 44 additions & 0 deletions python_test_example/mock/my_class_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from unittest import TestCase, main
from unittest.mock import MagicMock, call, patch

from mock.my_class import MyClass

class MyClassTestCase(TestCase):
def mocked_requests_get(*args, **kwargs):
class MockResponse:
def __init__(self, json_data, status_code):
self.json_data = json_data
self.status_code = status_code

def json(self):
return self.json_data

if args[0] == 'http://example.com/test.json':
return MockResponse({'key1': 'value1'}, 200)
elif args[0] == 'http://example.com/another_test.json':
return MockResponse({'key2': 'value2'}, 200)

return MockResponse(None, 404)

@patch('requests.get', side_effect=mocked_requests_get)
def test_fetch_json(self, mock_get: MagicMock):
my_class = MyClass()

json_data = my_class.fetch_json('http://example.com/test.json')
self.assertEqual(json_data, {'key1': 'value1'})
json_data = my_class.fetch_json('http://example.com/another_test.json')
self.assertEqual(json_data, {'key2': 'value2'})
json_data = my_class.fetch_json('http://no_example.com/test.json')
self.assertIsNone(json_data)

self.assertIn(
call('http://example.com/test.json'), mock_get.call_args_list
)
self.assertIn(
call('http://example.com/another_test.json'), mock_get.call_args_list
)

self.assertEqual(len(mock_get.call_args_list), 3)

if __name__ == '__main__':
main()