Slide 1

Slide 1 text

౵੉ॆীࢲ ಞೞѱ పझ౟ ா੉झ ੘ࢿೞӝ pytest, Travis CI, Docker ߅ઙഅ, (઱)ې࠶স

Slide 2

Slide 2 text

ߊ಴੗ • (઱) ې࠶স (~2.2֙) • HTML, CSS, JavaScript, Polymer, Django, Go • ನ೦ Ѣ઱ (ਗѺӔޖ) • Ӕ୊ ࢎदח ࠙ झఠ٣ ݽ੐ э਷ Ѫ ജ৔ ƕƕ

Slide 3

Slide 3 text

݈ೞ۰Ҋ ೞח Ѫ • పझ౟ ੘ࢿਸ ಞೞѱ: pytest • పझ౟ प೯ਸ ಞೞѱ: Travis CI • ౵੉ॆ unittest ݽٕ • unittest ؀࠺ pytest੄ ੢੼ • Travis CIܳ ాೠ పझ౟ ੗زച • docker containerܳ ాೠ పझ౟ प೯

Slide 4

Slide 4 text

୒઺ • ౵੉ॆਵ۽ పझ౟ா੉झ ੘ࢿਸ ೧ࠄ ҃೷੉ যו ੿ب ੓ח ࠙ • pytest ੜ ॳҊ ҅दݶ Ҷ੉ উ ٜਵ࣊ب… • ੉޷ పझ౟ ੜ ೞदח ࠙਷ ׮ܲ ࣁ࣌ਸ…

Slide 5

Slide 5 text

పझ౟ పझ౟ ೤द׮

Slide 6

Slide 6 text

పझ౟ ா੉झ • ௏٘о ਗೞח ؀۽ ز੘ೞח૑ పझ౟ೞח ௏٘ • ա ؀न ௏٘ܳ పझ౟೧ ઴ ޖ঱оо ೙ਃ • ঱ઁө૑ա ࣻز పझ౟ܳ ೡ ࣻח হ਺ • ܻಂష݂ೡ ٸ ୭ࣗೠ੄ ߡ౱ݾ def add_one(n): return n + 1 def test_add_one(): assert add_one(1) == 2 assert add_one(8) == 9 ௏٘ పझ౟ ா੉झ

Slide 7

Slide 7 text

పझ౟ܳ ೤द׮ • పझ౟ח ѐߊ੄ ೙ࣻ੸ੋ җ੿ • పझ౟ হ੉ Ҋಿ૕ ௏٘ܳ য়ۖزউ ਬ૑ೞӝ য۰਑ • (ޛۿ పझ౟о ੓׮Ҋ ௏٘੄ ૕੉ ࠁ੢غ૑ח ঋ਺) • ੗ӝо ੘ࢿೠ ௏٘ী ؀ೠ ୭ࣗೠ੄ పझ౟ۄب • ҳӖ੄ ҃਋ • Software engineer (SWE) • Software engineer in test (SET) • Test engineer (TE)

Slide 8

Slide 8 text

Test-Driven Development (TDD) • ௏٘о оઉঠ ೡ झಖਸ ੿੄ೞҊ Ӓী ݏח పझ౟ ݢ੷ ੘ࢿ • पઁ۽ ೧ࠁݶ ցޖ ൨ٝ https://goo.gl/IPwERJ

Slide 9

Slide 9 text

ઑӘ औѱ ੽Ӕ (>> None) • ௏٘ܳ ੘ࢿೡ ٸ పझ౟ܳ ੸ӓ ഝਊ • ex) Selenium పझ౟ ࢲߡܳ ڸਕࢲ ௏٘ ੘ࢿ • ߡӒܳ ଺ਵݶ Ӓী ೧׼ೞח పझ౟ ੘ࢿ (য়׹֢౟?) • పझ౟ܳ ੘ࢿೞӝ য۰਍ ௏٘ • Ӓ ௏٘ী ؀ೠ పझ౟ ੘ࢿਸ ನӝೠ׮ ನӝೞݶ ಞ೧ • Mocking / fakes (੉Ѫب औ૑݅਷ ঋणפ׮݅) • ௏٘ ܻಂష݂ਸ Ҋ۰ (పझ౟ ೞӝ ए਍ ௏٘ ੘ࢿ) • పझ౟ח ௏٘ܳ о੢ ੸ӓ੸ਵ۽ ഝਊೞח ੌઙ੄ ࢎਊ੗

Slide 10

Slide 10 text

౵੉ॆ੄ unittest ౵੉ॆ੄ పझ౴ ೐ۨ੐ਕ௼

Slide 11

Slide 11 text

unittest ݽٕ • ౵੉ॆ੄ పझ౟ ೐ۨ੐ਕ௼ • ݔࣚਵ۽ పझ౟ ੘ࢿ೧ب غ૑݅ ب਑ਸ ߉ਵݶ ಞೣ • పझ౟ ா੉झ ੘ࢿ ߂ प೯ ੹߈ী Ѧ୛ ਬਊೠ بҳ ઁҕ • పझ౟ ா੉झ Ѩ࢝ • పझ౟ী ೙ਃೠ ࢎ੹/ࢎറ ੘স (e.g. fixture) • పझ౟ ா੉झ प೯ • పझ౟ Ѿҗ ࣻ૘ • Ѿҗ ࠁҊ • ١١

Slide 12

Slide 12 text

unittest (ױਤ పझ౟) • ױਤ పझ౟ (unit test) • ୭ࣗ ӝמ ױਤী ؀ೠ పझ౟ (ex. add_one ੗୓ ӝמ పझ౟) • ా೤ పझ౟ (integration test) • ݻ ѐ੄ ӝמ ױਤܳ ޘযࢲ పझ౟ (ex. add_one ࢎਊೞח ੄ઓ ӝמ పझ౟) • ӝמ పझ౟ (functional test) • ࢎਊ੗ झషܻ పझ౟ • ਢ੄ ҃਋ Selenium పझ౟ ١ • … Selenium పझ౟੄ ҃਋ PyCon APAC 2016 ߊ಴ ଵҊ - ௿ۄ਋٘ ࢚ীࢲ Seleniumਸ ੉ਊೠ Django ӝמ పझ౟ ੗زച - https://www.pycon.kr/2016apac/program/37 Where do our flaky tests come from? https://testing.googleblog.com/2017/04/where-do-our-flaky-tests-come-from.html

Slide 13

Slide 13 text

unittest ݽٕਸ ഝਊೠ పझ౟ ா੉झ • పझ౟ Ӓܛ : unittest.TestCase ࢚ࣘ • పझ౟ ா੉झ : test۽ द੘ೞח ݫࣗ٘ import unittest from code import myabs class TestMyAbs(unittest.TestCase): def test_return_itself_with_positive_param(self): self.assertEqual(myabs(5), 5) def test_return_positive_with_negative_param(self): self.assertEqual(myabs(-7), 7) def myabs(n): if n >= 0: return n else: return -n code.py test_code_unittest.py self.assertEqual(a, b) : పझ౟ ী۞ assert a == b : ী۞ పझ౟ Ӓܛ పझ౟ ா੉झ పझ౟ ா੉झ

Slide 14

Slide 14 text

ޖࣗध੉ ൞ࣗध • ೞ૑݅ పझ౟ח पಁ೧ঠ ઁ ݍ! pycon17) ~/D/pycon17 ››› python -m unittest test_code_unittest.py .. ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK ݽٚ పझ౟о ੜ ࣻ೯ؽ పझ౟ प೯: • python -m unittest • python -m unittest test_code_unittest.py • python -m unittest test_code_unittest.TestMyAbs • python -m unittest test_code_unittest.TestMyAbs.test_return_itself_with_positive_param

Slide 15

Slide 15 text

పझ౟ ী۞ vs ী۞ def test_this_will_fail(self): self.assertEqual(myabs(5), 6) FAIL: test_this_will_fail (test_code_unittest.TestMyAbs) -------------------------------------------------------- Traceback (most recent call last): File "/Users/adrysn/Develop/pycon17/test_code_unittest.py", line 7, in test_this_will_fail self.assertEqual(myabs(5), 6) AssertionError: 5 != 6 -------------------------------------------------------- Ran 1 test in 0.000s FAILED (failures=1) def test_this_will_fail(self): assert myabs(5) == 6 FAIL: test_this_will_fail (test_code_unittest.TestMyAbs) -------------------------------------------------------- Traceback (most recent call last): File "/Users/adrysn/Develop/pycon17/test_code_unittest.py", line 7, in test_this_will_fail assert myabs(5) == 6 AssertionError -------------------------------------------------------- Ran 1 test in 0.000s FAILED (failures=1) Ӓېࢲ unittest ݽٕ ੉ਊೡ ҃਋ assert*() ݫࣗ٘ ࢎਊਸ ӂ੢ test exception python exception

Slide 16

Slide 16 text

다양한 assertion ݫࣗ٘ .FUIPE $IFDLT UIBU assertRaises(exc, fun, *args, **kwds) fun(*args, **kwds) raises exc assertRaisesRegex(exc, r, fun, *args, **kwds) fun(*args, **kwds) raises exc and the message matches regex r assertWarns(warn, fun, *args, **kwds) fun(*args, **kwds) raises warn assertWarnsRegex(warn, r, fun, *args, **kwds) fun(*args, **kwds) raises warn and the message matches regex r assertLogs(logger, level) The with block logs on logger with minimum level .FUIPE $IFDLT UIBU assertEqual(a, b) a == b assertNotEqual(a, b) a != b assertTrue(x) bool(x) is True assertFalse(x) bool(x) is False assertIs(a, b) a is b assertIsNot(a, b) a is not b assertIsNone(x) x is None assertIsNotNone(x) x is not None assertIn(a, b) a in b assertNotIn(a, b) a not in b assertIsInstance(a, b) isinstance(a, b) assertNotIsInstance(a, b) not isinstance(a, b) .FUIPE $IFDLT UIBU assertAlmostEqual(a, b) round(a-b, 7) == 0 assertNotAlmostEqual(a, b) round(a-b, 7) != 0 assertGreater(a, b) a > b assertGreaterEqual(a, b) a >= b assertLess(a, b) a < b assertLessEqual(a, b) a <= b assertRegex(s, r) r.search(s) assertNotRegex(s, r) not r.search(s) assertCountEqual(a, b) a and b have the same elements in the same number, regardless of their order .FUIPE 6TFE UP DPNQBSF assertMultiLineEqual(a, b) strings assertSequenceEqual(a, b) sequences assertListEqual(a, b) lists assertTupleEqual(a, b) tuples assertSetEqual(a, b) sets or frozensets assertDictEqual(a, b) dicts PEP8 ٮਤח ޖद೤פ׮…

Slide 17

Slide 17 text

੉ۧѱ పझ౟ܳ ੘ࢿೞ׮ ࠁݶ ೠ о૑ ޙઁо… • п పझ౟ח ׮ܲ పझ౟ী ة݀੸੉যঠ ೣ • ৈ۞ పझ౟ী ࢎਊغח ҕా పझ౟ ܻࣗझ੄ ೙ਃࢿ • పझ౟ীࢲب Don’t Repeat Yourself (DRY) class TestSomething(unittest.TestCase): def test_one (self): # ҕా ੗ਗ 1 ੿੄ (ex. ࢎਊ੗) # ҕా ੗ਗ 2 ੿੄ (ex. Mock obj) self.assertEqual(...) def test_two(self): # ҕా ੗ਗ 1 ژ ੿੄ # ҕా ੗ਗ 2 ژ ੿੄ self.assertEqual(...)

Slide 18

Slide 18 text

unittest੄ fixture class Test(unittest.TestCase): @classmethod def setUpClass(cls): # executed before all test cases in Test cls._conn = createExpensiveObject() @classmethod def tearDownClass(cls): # executed after all test cases in Test cls._conn.destroy() def setUp(self): # executed before each test case self.common_widget = createWidget('the widget') def tearDown(self): # executed after each test case self.common_widget.dispose() ௿ېझܳ ഝਊೞৈ ҕా ੗ਗ ҕਬ/੤ࢎਊ п పझ౟ ா੉झ प೯ ੹റ Test ղ੄ ੹୓ పझ౟ प೯ ੹റ

Slide 19

Slide 19 text

పझ౟ झझ۽ ҳઑܳ ऺӝ द੘ CommonMethods WebPageBase FuncTestBase LogInPage SignupPage … LogInTest SignupTest … ೙ਃহח ੗ਗب ݽٚ పझ౟ Ӓܛ੉ ҕਬೞѱ ؽ

Slide 20

Slide 20 text

pytest औ૑݅ ъ۱ೠ పझ౟ ۄ੉࠳۞ܻ

Slide 21

Slide 21 text

pytest • పझ౟ܳ औѱ ೡ ࣻ ੓ب۾ ب৬઱ח ۄ੉࠳۞ܻ • ࢿࣼೠ ۄ੉࠳۞ܻ • 2007֙ 1ਘ ୐ ழ޿ • ୭Ӕө૑ ഝߊೞѱ ழ޿ “makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries”

Slide 22

Slide 22 text

ই઱ औѱ द੘ • ӝઓ పझ౟ ா੉झܳ ইޖ ࣻ੿হ੉ प೯ pip install pytest python –m pytest పझ౟ प೯: • python -m pytest 쏞쁢 pytest • python -m pytest test_code_unittest.py • python -m pytest test_code_unittest.py::TestMyAbs • python -m pytest test_code_unittest.py::TestMyAbs::test_return_itself_with_positive_param

Slide 23

Slide 23 text

рಞೠ పझ౟ ੘ࢿ • పझ౟ܳ ߈٘द ௿ېझ۽ ੘ࢿೡ ೙ਃ হ਺ • self.assert* ݫࣗ٘ ࢎਊೡ ೙ਃ হ਺ class TestMyAbs(unittest.TestCase): def test_return_itself_with_positive_param(self): self.assertEqual(myabs(5), 5) test_code_pytest.py class TestMyAbs: def test_return_itself_with_positive_param(self): assert myabs(5) == 5 test_code_unittest.py

Slide 24

Slide 24 text

੗ࣁೠ पಁ ࠁҊ FAIL: test_this_will_fail (test_code_unittest.TestMyAbs) ----------------------------------------------- Traceback (most recent call last): File "/Users/adrysn/Develop/pycon17/test_code_unitte st.py", line 7, in test_this_will_fail self.assertEqual(myabs(5), 6) AssertionError: 5 != 6 ----------------------------------------------- Ran 1 test in 0.000s FAILED (failures=1) platform darwin -- Python 3.6.2, pytest-3.2.0, py-1.4.34, pluggy- 0.4.0 rootdir: /Users/adrysn/Develop/pycon17, inifile: collected 1 item s test_code_unittest.py F ========================== FAILURES ========================== _______________ TestMyAbs.test_this_will_fail ________________ self = def test_this_will_fail(self): > assert myabs(5) == 6 E AssertionError: assert 5 == 6 E + where 5 = myabs(5) test_code_unittest.py:7: AssertionError ================== 1 failed in 0.04 seconds ================== test_code_pytest.py test_code_unittest.py test exceptionਵ۽ ੋध

Slide 25

Slide 25 text

Fixture • పझ౟ী ೙ਃೠ ҕా ੗ਗ • ೣࣻ ഋక • పझ౟ ா੉झীࢲ ೙ਃ۽ ೞח fixtureܳ ࢶఖ੸ਵ۽ औѱ ࢎਊ • Dependency injection ഋక • పझ౟੄ ة݀ࢿҗ ੗ਗ ੤ࢎਊ ࢎ੉੄ ઑചܳ ଺ח ೠ ߑಞ

Slide 26

Slide 26 text

Fixture ੘ࢿҗ ࢎਊ # content of ./test_smtpsimple.py import pytest @pytest.fixture def smtp(): import smtplib return smtplib.SMTP("smtp.gmail.com", 587, timeout=5) def test_ehlo(smtp): response, msg = smtp.ehlo() assert response == 250 assert 0 # for demo purposes def test_ehlo2(smtp): # can use `smtp` again $ pytest test_smtpsimple.py ======= test session starts ======== platform linux -- Python 3.x.y, pytest- 3.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item test_smtpsimple.py F ======= FAILURES ======== _______ test_ehlo ________ smtp = def test_ehlo(smtp): response, msg = smtp.ehlo() assert response == 250 > assert 0 # for demo purposes E assert 0 test_smtpsimple.py:11: AssertionError ======= 1 failed in 0.12 seconds ========

Slide 27

Slide 27 text

Fixture੄ ׮নೠ ২࣌ @pytest.fixture() def smtp(): smtp = smtplib.SMTP("smtp.gmail.com", 587, timeout=5) yield smtp # provide the fixture value print("teardown smtp") smtp.close() Finalization ௏٘ प೯ (yield ੉ਊ) @pytest.fixture(params=["smtp.gmail.com", "mail.py.org"]) def smtp(request): return smtplib.SMTP(request.param, 587, timeout=5) Parametrization class App(object): def __init__(self, smtp): self.smtp = smtp @pytest.fixture(scope="module") def app(smtp): return App(smtp) Fixtureীࢲ ׮ܲ fixture ࢎਊ https://docs.pytest.org/en/latest/fixture.html ׮ܲ fixture

Slide 28

Slide 28 text

Fixture য়ߡۄ੉٬ tests/ __init__.py conftest.py @pytest.fixture def username(): return 'username' test_something.py def test_username(username): assert username == 'username' subfolder/ __init__.py conftest.py @pytest.fixture def username(username): return 'overridden-' + username test_something.py def test_username(username): assert username == 'overridden-username' @pytest.fixture def username(): return 'username' class TestMyAbs: @pytest.fixture def username(username): return 'MyAbs-' + username def test_something(self, username): assert username == 'MyAbs-username'

Slide 29

Slide 29 text

pytest builtin fixtures (neumann) ~/l/neumann ››› pytest -q –fixtures cache Return a cache object that can persist state between testing sessions. cache.get(key, default) cache.set(key, value) ... monkeypatch The returned ``monkeypatch`` fixture provides these helper methods to modify objects, dictionaries or os.environ:: monkeypatch.setattr(obj, name, value, raising=True) monkeypatch.delattr(obj, name, raising=True) ... tmpdir Return a temporary directory path object which is unique to each test function invocation, created as a sub directory of the base temporary directory. The returned object is a `py.path.local`_ path object.

Slide 30

Slide 30 text

pytest ೒۞Ӓੋ • ઱۽ ਬਊೠ ӝמ੉ա fixture ઁҕ • pytest-cov : పझ౟ ழߡܻ૑ • pytest-xdist : పझ౟ ߽۳ച • pytest-django : Django ۄ੉࠳ ࢲߡ • pytest-selenium : selenium పझ౟ ҙ۲ fixture • …

Slide 31

Slide 31 text

class LablupWebdriver(webdriver.Chrome): """ Wrapper for selenium's WebDriver. The purpose is to append additional features/helpers to WebDriver. Currently, only Chrome is taken accounted for. """ def __init__(self, base_url, timeout=10, *args, **kwargs): self._base_url = base_url self._timeout = timeout self._web_element_cls = LablupWebElement desired_capabilities = dict() desired_capabilities['chromeOptions'] = { "args": ["--no-sandbox", "--proxy-server null"], "extensions": [] } super().__init__(desired_capabilities=desired_capabilities, *args, **kwargs) @pytest.fixture def browser(live_server): driver = LablupWebdriver(live_server.url) driver.set_window_size(1280, 1024) yield driver driver.quit() def test_search_course(self, browser, tester): # Create a new course to be searched course = self.create_course(owner=tester) # Search course browser.login(tester) browser.get('dashboard:course') course_page = CoursePage(browser) course_page.search_course(course.title) # Visit searched circle url = self.reverse('course:course', args=(str(course.id),)) browser.get(url) assert url in browser.current_url pytest-django + pytest-selenium ੉ਊ೧ࢲ పझ౟ प೯ೞח ৘

Slide 32

Slide 32 text

pytest੄ ੢੼ • ૊द द੘ оמ • Boilerplate ௏٘ܳ ੸ѱ • ب਑੉ غח पಁ Ѿҗ ࠁҊ • Fixtureܳ ా೧ పझ౟ ੄ઓ ੗ਗਸ ݽٕച • ׮নೠ ೒۞Ӓੋ

Slide 33

Slide 33 text

Travis CI పझ౟ ੗زച

Slide 34

Slide 34 text

పझ౟ प೯ೞח Ѫب ࠺ਊ • ੹୓ పझ౟ܳ प೯೧ࢲ ઱ӝ੸ਵ۽ ௏٘ উ੿ࢿ ഛੋ ೙ਃ • పझ౟ ࣻ೯ী ߹ب दр ೙ਃ • ױਤ పझ౟ח ࡅܰ૑݅ ੹୓ పझ౟ח दр ೙ਃ • Selenium పझ౟ э਷ ҃਋ ೠ ઴ ૞ܻ పझ౟ب ࣻ ୡ ࣗਃ • ઁо ଵৈೞח ೠ ೐۽ં౟੄ ੹୓ పझ౟ ࣻ೯ दр • ױਤ పझ౟: 3~4࠙ (582ѐ) • Selenium పझ౟: 20࠙ (161ѐ)

Slide 35

Slide 35 text

੗୓ పझ౟ ࢲߡ – ৉द दрҗ ֢۱੉ ೙ਃ Unit tests Selenium tests Local1 Test server (on EC2) git commit; git push Actual codes Github GithubAutoDeploy (listen hook ➡ run scripts) hook git pull post-merge hook Run DB migration Run unit tests listen err no err Error posting script Run functional tests err no err Unit tests Selenium tests Local2 git commit; git push Actual codes once a day comment on issue … Slack notification

Slide 36

Slide 36 text

Travis CI • పझ౟/ߓನ ੗زച ࢲ࠺झ • Github ҅੿ োز • ࢜ commit ߊࢤೡ ٸ݃׮ ੗زਵ۽ పझ౟ (࢜۽਍ о࢚ݠन ࢤࢿ) • పझ౟ ജ҃ ߂ ݺ۸਷ .travis.yml ౵ੌ۽ ࢸ੿ • য়೑ࣗझ ೐۽ં౟ח ޖܐ : https://travis-ci.org • ࠺ҕѐ ೐۽ં౟ח җӘ : https://travis-ci.com

Slide 37

Slide 37 text

Travis CI۽ పझ౟ प೯ • Github ҅੿ োز + Travis CIীࢲ ࣻ೯ೡ ੘স ࢸ੿ (.travis.yml) language: python python: - "2.7" - "3.6" install: "pip install -r requirements.txt" script: pytest notifications: slack: secure: .travis.yml పझ౟ ജ҃ ࢸ੿ ೙ਃೠ ੄ઓಁః૑ ࢸ஖ పझ౟ प೯ పझ౟ Ѿҗ ঌۈ

Slide 38

Slide 38 text

travis.yml੄ ੗ࣁೠ ࢸ੿਷ https://docs.travis-ci.com/user/customizing-the-build/

Slide 39

Slide 39 text

Commit status ੗ز সؘ੉౟ ௿ܼ

Slide 40

Slide 40 text

ݽٚ җ੿ਸ ௑ࣛ ۽Ӓ۽ ഛੋ оמ

Slide 41

Slide 41 text

աب ੉ઁ పझ౟ ੗زച • ই઱ рױ൤ పझ౟ ੗زച • CRON job ӝמ ഝਊ ઱ӝ੸ పझ౟ оמ (ই૒ ߬ఋ) • ৘) ೞܖ ೠ ߣ selenium పझ౟ܳ ࣻ೯ • Ҷ੉ ױ੼ਸ Ԟ੗ݶ • పझ౟݃׮ ࢜ о࢚ݠनਸ ࢤࢿ/౵Ҧೞ޲۽ ӝࠄ੸ਵ۽ ੟ইݡח दр੉ ੓਺ • .travis.yml ࣻ੿ೠ Ѿҗܳ commit റ Travis CI ࢎ੉౟ оঠ݅ ഛੋೡ ࣻ ੓ যࢲ, ખ ࠂ੟ೠ ࢸ੿ਸ ઱Ҋ रਸ ٸ ٣ߡӒо য۰਑

Slide 42

Slide 42 text

ஶప੉ցܳ ాೠ పझ౟ प೯ Docker + Travis CI

Slide 43

Slide 43 text

Docker • ஶప੉ց о࢚ച ೒ۖಬ • ة݀੸੉Ҋ ӵՖೠ о࢚ݠनਸ ࡅܰѱ ࢤࢿ/౵Ҧ Eliminate “works on my computer” problem https://www.docker.com

Slide 44

Slide 44 text

Images vs containers • Images : ౵ੌदझమ੄ झշࢫ • Containers : ة݀੸ੋ о࢚ ݠन • ݎೠ ࠺ਬ • ੗ࣁೠ Ѫ਷ بழ കಕ੉૑ܳ ଵઑ ਦب਋ૉ ੉޷૑ ਦب਋ૉ PC (ஶప੉ց?) ࢎਊ੗ ࢎਊ - dir - run.bat - starcraft

Slide 45

Slide 45 text

Images vs containers (pycon17) ~/D/pycon17 ››› docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS 74342efab4b6 neumann_neumann-web "supervisord -n -c..." 6 weeks ago Up About a minute cada03cea397 rabbitmq "docker-entrypoint..." 6 weeks ago Up About a minute 036f711b0530 redis "docker-entrypoint..." 6 weeks ago Up About a minute 6e19dd3e97bf postgres "/docker-entrypoin..." 6 weeks ago Up About a minute (pycon17) ~/D/pycon17 ››› docker images REPOSITORY TAG IMAGE ID CREATED SIZE alpine 3.6 a41a7446062d 2 months ago 3.97MB neumann_neumann-web latest 84404c459bed 2 months ago 1.75GB redis latest 45c3ea2cecac 6 months ago 183MB rabbitmq 3.6-alpine 241555f3f25a 2 months ago 37.5MB postgres latest c9994f4753b2 6 months ago 265MB ubuntu 14.04 302fa07d8117 3 months ago 188MB lablup/kernel-python3-tensorflow latest b3245c09ab05 4 months ago 1.68GB sornaweb_sorna-web latest 622345c5c78c 6 months ago 1.01GB ࢎਊ੗о ੽ࣘ೧ࢲ ݺ۸ਸ प೯ೡ ࣻ ੓਺

Slide 46

Slide 46 text

Dockerfile : ੉޷૑ܳ ݅٘ח ౵ੌ FROM ubuntu:latest MAINTAINER Jonghyun Park "[email protected]" ENV PATH $pyenv/bin:/usr/local/sbin RUN apt-get update RUN apt-get install -y python python-pip wget RUN pip install Django ADD test_myabs.py /home/test_myabs.py WORKDIR /home EXPOSE 80, 443 CMD ["echo", "hello"] Dockerfile docker build -t "my_image" . docker run my_image my_image ੉޷૑۽ࠗఠ ஶప੉ց प೯ my_image ੉޷૑ ࢤࢿ

Slide 47

Slide 47 text

docker-compose : ৈ۞ ஶప੉ց زद प೯ version: '2' services: neumann-web: container_name: web build: context: . dockerfile: Dockerfile.dev ports: - 8080:8080 extra_hosts: - "docker.local:192.168.65.1" volumes: - .:/neumann - /dev/shm:/dev/shm environment: - DBUS_SESSION_BUS_ADDRESS=/dev/null depends_on: - neumann-db - neumann-mq - redis redis: container_name: cache image: redis ports: - 6379:6379 neumann-db: container_name: neumann-db image: postgres ports: - 5442:5432 environment: - POSTGRES_PASSWORD=develove - POSTGRES_DB=neumann neumann-mq: container_name: neumann-mq image: rabbitmq docker-compose.yml docker-compose -f docker-compose.yml up ੉޷૑ ੉ܴ ੉޷૑ ࠽٘

Slide 48

Slide 48 text

Travis CIীࢲ ஶప੉ց۽ పझ౟ प೯ೞ۰ݶ • .travis.ymlী ׮਺ ݺ۸ਸ ӝࣿ • Dockerfile۽ ೙ਃೠ ੉޷૑ ੘ࢿ • ೙ਃೠ ҃਋ docker-compose۽ ৈ۞ ੉޷૑ زद ஶప੉ցച • ஶప੉ց উীࢲ పझ౟ী ೙ਃೠ ജ҃ ࢸ੿ • ஶప੉ց উীࢲ పझ౟ ݺ۸ प೯

Slide 49

Slide 49 text

sudo: required language: python python: - "3.6" services: - docker env: global: - DOCKER_COMPOSE_VERSION=1.14.0 - CACHE_DIR=$HOME/.cache - DOCKER_CACHE_FILE=$CACHE_DIR/docker/cache-image-web.tar.gz - NEUMANN_IMAGE_NAME=neumann_neumann-web - RUN_IN_PYVENV="docker exec -it web /neumann/run-in-pyvenv.sh" cache: directories: - $CACHE_DIR/docker install: # Install newer version of docker. - sudo apt-get update - sudo apt-get -y -o Dpkg::Options::="--force-confold" --force-yes install docker-ce - docker-compose --version - sudo rm /usr/local/bin/docker-compose - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker- compose-`uname -s`-`uname -m` > docker-compose - chmod +x docker-compose - sudo mv docker-compose /usr/local/bin بழ ࢲ࠺झܳ ࢎਊ ஶప੉ց ղ virtualenv प೯ .travis.yml ؀ࠗ࠙਷ بழ ੉޷૑ நय ҙ۲ ݺ۸

Slide 50

Slide 50 text

before_script: # Load neumann docker image previously cached if exists. - if [ -f ${DOCKER_CACHE_FILE} ]; then gunzip -c ${DOCKER_CACHE_FILE} | docker load; fi - NEUMANN_IMAGE_ID=`docker images -q | head -n1` - docker-compose -f docker-compose.travis-ci.yml build - NEUMANN_IMAGE_ID_NEW=`docker images -q ${NEUMANN_IMAGE_NAME}` # Run neumann server by docker-compose. - find * -name '*.py[co]' -delete - docker-compose -f docker-compose.travis-ci.yml up -d - docker ps script: - echo ${TRAVIS_EVENT_TYPE} - SELENIUM=0 && [[ ${TRAVIS_EVENT_TYPE} == "cron" ]] && SELENIUM=1 || true - if [[ SELENIUM -eq 0 ]]; then ${RUN_IN_PYVENV} pytest --debug-mode --cov=. -m "not selenium" ; else ${RUN_IN_PYVENV} xvfb-run pytest --debug-mode -m "selenium" --rerun 2; fi # Save neumann docker image to cache directory only if new image was built. - if [[ ${NEUMANN_IMAGE_ID} != ${NEUMANN_IMAGE_ID_NEW} ]]; then mkdir -p $(dirname ${DOCKER_CACHE_FILE}); docker save $(docker history -q ${NEUMANN_IMAGE_NAME} | grep -v '') | gzip > ${DOCKER_CACHE_FILE}; fi notifications: webhooks: - https://outlook.office.com/webhook/****************** 4ѐ੄ بழ ஶప੉ց ڸ਑ ஶప੉ցীࢲ pytest प೯ notification

Slide 51

Slide 51 text

ಞೞ૓ ঋ਷ Ѫ э਷ؘਃ… • بழ۽ పझ౟ ೞݶ ࢎप ੌ੉ ݆णפ׮ • ೞ૑݅ জਸ ੉޷૑۽ ೠ ߣ ٜ݅য فݶ જणפ׮ • పझ౟ ೡ ٸח ખ ࠂ੟ೞ૑݅, ੹߈੸ਵ۽ ಞೣ • ৈ۞ о૑۽ જ਷ ੼੉ ݆ਵפ ॄࠁ૑ ঋਵन ࠙਷ ੉ߣ ӝഥী ೠ ߣ...

Slide 52

Slide 52 text

੿ܻ • pytestܳ ੜ ഝਊೞݶ పझ౟੄ ة݀ࢿਸ ਬ૑ೞݶࢲ పझ౟ ੗ਗ ੤ࢎ ਊਸ ࠁ׮ औѱ ೡ ࣻ ੓णפ׮ • য়೑ࣗझ ࢎਊೞח ҃਋ Travis CI ࢎਊ೤द׮ • పझ౟݅ਸ ਤ೧ بழܳ ࢎਊೡ Ѫө૑ח হח Ѫ эणפ׮… • ೞ૑݅ জ ੗୓ܳ بழച ೞݶ ಞ೤פ׮

Slide 53

Slide 53 text

хࢎ೤פ׮! ੹੗ন੄ Էਸ Բח উ٘۽੉٘: Pythonҗ NLTK, TensorFlowܳ ੉ਊೠ ୁࠈ х੿ݽഋ ҳഅ 14:40 (न੿ӏ) Meet aiotools: asyncio Idiom Library ղੌ(8/13) 13:30 (ӣળӝ)