๐Ÿ  ๋ฐฉ์„ฑ๋ฒ” ๋ธ”๋กœ๊ทธ > ๐Ÿ ํŒŒ์ด์ฌ

unittest vs pytest

์œ ๋ช…ํ•œ ํŒŒ์ด์ฌ ํ…Œ์ŠคํŒ… ํ”„๋ ˆ์ž„์›Œํฌ์ธ unittest(์œ ๋‹›ํ…Œ์ŠคํŠธ)์™€ pytest(ํŒŒ์ดํ…Œ์ŠคํŠธ)์˜ ์žฅ๋‹จ์ ์„ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. pytest์˜ ๋…ํŠนํ•œ ํ…Œ์ŠคํŠธ ๋ฐฉ์‹์ด ๋ณ„๋กœ๋ผ๋ฉด unittest๋ฅผ, ๊ฐ„๊ฒฐํ•˜๊ณ  ์•„๋ฆ„๋‹ค์šด ํ…Œ์ŠคํŠธ๊ฐ€ ์ค‘์š”ํ•˜๋‹ค๋ฉด pytest๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

๋น„๊ต์—์„œ ์ œ์™ธํ•œ ํ”„๋ ˆ์ž„์›Œํฌ

nose(๋…ธ์ฆˆ): ์œ ์ง€ ๋ณด์ˆ˜ํ•  ์‚ฌ๋žŒ์„ ๊ตฌํ•˜์ง€ ๋ชปํ•ด ์ตœ๊ทผ ๋ช‡ ๋…„๋™์•ˆ ์—…๋ฐ์ดํŠธ๊ฐ€ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. nose ๋ฌธ์„œ์—์„œ์กฐ์ฐจ nose ๋Œ€์‹  pytest๋‚˜ unittest๋ฅผ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค1.

doctest(๋…ํ…Œ์ŠคํŠธ): ํŒŒ์ด์ฌ์˜ ๋…์ŠคํŠธ๋ง(docstring)์— ์žˆ๋Š” ์ƒ˜ํ”Œ ์ฝ”๋“œ๋งŒ์„ ํ…Œ์ŠคํŠธํ•˜๊ธฐ์œ„ํ•œ ํŠน์ˆ˜ ๋ชฉ์ ์˜ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. ์ด ๊ธ€์—์„œ๋Š” ์ผ๋ฐ˜ ๋ชฉ์ ์˜ ํ…Œ์ŠคํŒ… ํ”„๋ ˆ์ž„์›Œํฌ๋งŒ์„ ๋น„๊ตํ•˜๋ ค ํ•˜๋ฏ€๋กœ ๋น„๊ต ๋Œ€์ƒ์—์„œ ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ ์‚ฌ๋ก€

unittest: ํŒŒ์ด์ฌ ๋‚ด๋ถ€ ํ…Œ์ŠคํŠธ2, Django(์žฅ๊ณ )์—์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

pytest: Flask(ํ”Œ๋ผ์Šคํฌ), Requests(๋ฆฌํ€˜์ŠคํŠธ), pip์—์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋‘ ํ”„๋ ˆ์ž„์›Œํฌ ๋ชจ๋‘ ๊ถŒ์œ„ ์žˆ๋Š” ํ”„๋กœ์ ํŠธ์—์„œ ์“ฐ์ž…๋‹ˆ๋‹ค. ๋‹ค๋งŒ pytest๊ฐ€ ๋” ๋„๋ฆฌ ์“ฐ์ด๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.

unittest์˜ ๋‹จ์ : ์žฅํ™ฉํ•œ ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ํ…Œ์ŠคํŠธ

unittest: ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด ๋ฐ˜๋“œ์‹œ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋ถˆํŽธํ•ฉ๋‹ˆ๋‹ค.

pytest: ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐ ์žˆ์–ด ํ•จ์ˆ˜๋งŒ ์ •์˜ํ•˜๋ฉด ๋˜๋ฏ€๋กœ ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

unittest๋Š” ์ž๋ฐ”์˜ JUnit(J์œ ๋‹›)์ด๋ผ๋Š” ํ…Œ์ŠคํŒ… ํ”„๋ ˆ์ž„์›Œํฌ๋กœ๋ถ€ํ„ฐ ๊ฐ•๋ ฅํ•œ ์˜ํ–ฅ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค3. ์ž๋ฐ”๋Š” ํด๋ž˜์Šค ์ค‘์‹ฌ์ ์ธ ์–ธ์–ด๋กœ, ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์œผ๋ฉด ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋ช‡๋ช‡ ํŒŒ์ด์ฌ ๊ฐœ๋ฐœ์ž๋“ค์€ ํด๋ž˜์Šค๋ณด๋‹ค๋Š” ํ•จ์ˆ˜ ์œ„์ฃผ๋กœ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ด์ฌ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—ญ์‹œ ํด๋ž˜์Šค ๋ฐฉ์‹๊ณผ ํ•จ์ˆ˜ ๋ฐฉ์‹์„ ๋‘˜ ๋‹ค ์ง€์›ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. json.JSONEncoder์™€ json.dumps()์ฒ˜๋Ÿผ ๋ง์ž…๋‹ˆ๋‹ค. unittest๊ฐ€ ํด๋ž˜์Šค ์œ„์ฃผ์˜ ํ…Œ์ŠคํŠธ๋งŒ ์ง€์›ํ•˜๋Š” ๊ฒƒ์€ ํ•ฉ๋ฆฌ์ ์ด์ง€ ๋ชปํ•˜๋‹ค๊ณ  ๋А๊ปด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ๋˜‘๊ฐ™์€ ํ…Œ์ŠคํŠธ๋ฅผ unittest์™€ pytest๋กœ ์ž‘์„ฑํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค:

unittest:

from unittest import TestCase


class UpperTestCase(TestCase):
    def test_upper(self):
        self.assertEqual("foo".upper(), "FOO")

pytest:

def test_upper():
    assert "foo".upper() == "FOO"

unittest์˜ ๋‹จ์ : ์นด๋ฉœ ์ผ€์ด์Šค

unittest: ์–ด์ฉ” ์ˆ˜ ์—†๋Š” ์ด์œ ๊ฐ€ ์žˆ๊ธด ํ•˜์ง€๋งŒ, ๊ถŒ์žฅ๋˜์ง€ ์•Š๋Š” ๋ฐฉ์‹์ธ ์นด๋ฉœ ์ผ€์ด์Šค๋กœ ๋‹จ์–ด๋ฅผ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. assertEqual(), setUp()์ฒ˜๋Ÿผ ๋ง์ž…๋‹ˆ๋‹ค.

pytest: ๊ถŒ์žฅ๋˜๋Š” ๋ฐฉ์‹์ธ ์–ธ๋”์Šค์ฝ”์–ด๋กœ ๋‹จ์–ด๋ฅผ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. assert_equal(), set_up()์ฒ˜๋Ÿผ ๋ง์ž…๋‹ˆ๋‹ค.

PEP 8์ด๋ผ๊ณ ๋„ ๋ถˆ๋ฆฌ๋Š” ํŒŒ์ด์ฌ ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ์—์„œ๋Š”, ๋ฉ”์„œ๋“œ์˜ ์ด๋ฆ„์„ ์ง€์„ ๋•Œ pytest์ฒ˜๋Ÿผ ์–ธ๋”์Šค์ฝ”์–ด๋กœ ๋‹จ์–ด๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค4.

์–ธ๋”์Šค์ฝ”์–ด๋ฅผ ๊ถŒ์žฅํ•˜๊ธฐ๋Š” ํ•˜์ง€๋งŒ, ํŒŒ์ด์ฌ ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด ๋” ์ค‘์š”ํ•˜๋‹ค๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค5. unittest๊ฐ€ ์ฒ˜์Œ๋ถ€ํ„ฐ ์นด๋ฉœ ์ผ€์ด์Šค๋กœ ๊ฐœ๋ฐœ๋˜์—ˆ๋‹ค๋ฉด ์ด๋ฅผ ๋ฐ”๊พธ๊ธฐ ๋ณด๋‹ค๋Š” ๊ณ„์† ์œ ์ง€ํ•˜๋Š” ๊ฒŒ ์ข‹๊ฒ ์ฃ .

์™œ unittest๊ฐ€ ์ฒ˜์Œ๋ถ€ํ„ฐ ์–ธ๋”์Šค์ฝ”์–ด๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋Š”๊ฐ€์— ๋Œ€ํ•ด์„œ๋„ ์ž˜๋ชป๋˜์—ˆ๋‹ค๋ผ๊ณ  ๋งํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. unittest๋Š” PyUnit(ํŒŒ์ด์œ ๋‹›)์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ 1999๋…„ ์‹œ์ž‘๋œ ํ”„๋กœ์ ํŠธ์ธ ๋ฐ˜๋ฉด6, ํŒŒ์ด์ฌ ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ๋Š” 2001๋…„ ์ฒ˜์Œ ๋งŒ๋“ค์–ด์กŒ์œผ๋‹ˆ๊นŒ์š”.

pytest์˜ ์žฅ์ : ๋…ํŠนํ•˜์ง€๋งŒ ๊ฐ•๋ ฅํ•œ ํ”ฝ์Šค์ฒ˜ ๋ฌธ๋ฒ•

unittest: ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์˜ ํ…Œ์ŠคํŒ… ํ”„๋ ˆ์ž„์›Œํฌ์™€ ์œ ์‚ฌํ•œ ํ”ฝ์Šค์ฒ˜ ์ •์˜ ๋ฐฉ์‹์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

pytest: ๊ธฐ์กด์˜ ์—ฌ๋Ÿฌ ํ…Œ์ŠคํŒ… ํ”„๋ ˆ์ž„์›Œํฌ์™€๋Š” ๋‹ค๋ฅธ ๋…ํŠนํ•œ ๋ฐฉ์‹์œผ๋กœ ํ”ฝ์Šค์ฒ˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์–ด๋–ค ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋–ค ํ”ฝ์Šค์ฒ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์‰ฝ๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ํ…Œ์ŠคํŠธ๋งˆ๋‹ค ๊ผญ ํ•„์š”ํ•œ ํ”ฝ์Šค์ฒ˜๋งŒ ๋ช…์‹œํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํ”ฝ์Šค์ฒ˜๋ฅผ ๋งŒ๋“œ๋Š”๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์„ ์•„๋‚„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

pytest ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋Š” ์ด๋Ÿฌํ•œ ํ”ฝ์Šค์ฒ˜ ์‚ฌ์šฉ ๋ฐฉ์‹์ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์žฅ์ ์„ ๊ฐ€์ ธ๋‹ค ์ค€๋‹ค๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค:

https://docs.pytest.org/en/latest/fixture.html

pytest ํ”ฝ์Šค์ฒ˜๋Š” ๊ณ ์ „์ ์ธ xUnit ์Šคํƒ€์ผ์˜ setUp/tearDown๊ณผ ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ๊ทน์ ์œผ๋กœ ๊ฐœ์„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค:

pytest์˜ ๋‹จ์ : ๊ธฐ์กด ํŒŒ์ด์ฌ ํ๋ฆ„๊ณผ ๋‹ค๋ฅธ ํ”ฝ์Šค์ฒ˜

unittest: ํ”ฝ์Šค์ฒ˜๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด, ๋‹ค๋ฅธ ์–ธ์–ด์˜ ํ…Œ์ŠคํŒ… ํ”„๋ ˆ์ž„์›Œํฌ๋“ค๊ณผ ๋งค์šฐ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค.

pytest: pytest๋งŒ์˜ ๊ณ ์œ ํ•œ ๋ฐฉ์‹์„ ์ตํ˜€์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์€ ์ผ๋ฐ˜์ ์ธ ํŒŒ์ด์ฌ์˜ ์ฝ”๋“œ ํ๋ฆ„์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ๊ฐ€ ์˜ค๋ฅ˜๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

pytest์˜ ํ”ฝ์Šค์ฒ˜๋Š” ํŒŒ์ด์ฌ์—์„œ ์“ฐ์ด๋Š” ์ผ๋ฐ˜์ ์ธ ์ฝ”๋“œ์˜ ํ๋ฆ„๊ณผ ์™„์ „ํžˆ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๋…ํŠนํ•œ ๋ฌธ๋ฒ•์œผ๋กœ ์ธํ•ด, ์ดˆ๋ณด ํŒŒ์ด์ฌ ๊ฐœ๋ฐœ์ž๋Š” ๋ฌผ๋ก  pytest๋ฅผ ์ ‘ํ•ด๋ณด์ง€ ๋ชปํ•œ ์ˆ™๋ จ๋œ ํŒŒ์ด์ฌ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์žˆ์–ด์„œ๋„ ๋‹นํ˜น๊ฐ์„ ์•ˆ๊ฒจ์ค๋‹ˆ๋‹ค.

์‚ฌ๋žŒ์ด ์•„๋‹Œ ๊ธฐ๊ณ„๊ฐ€ ํ”ฝ์Šค์ฒ˜๋ฅผ ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค๋ฉด ๋ฌธ์ œ๋Š” ๋”์šฑ ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค. Pylint(ํŒŒ์ด๋ฆฐํŠธ)๋ฅผ ์˜ˆ๋กœ ๋“ค์–ด๋ด…์‹œ๋‹ค. Pylint๋Š” ํŒŒ์ด์ฌ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•ด ๋ฌธ์ œ๊ฐ€ ๋ ๋งŒํ•œ ๋ถ€๋ถ„์„ ์ฐพ์•„ ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•ด์ฃผ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ, ๋ฐ”๊นฅ ์˜์—ญ์— ์„ ์–ธ๋œ ์ด๋ฆ„๊ณผ ๋™์ผํ•œ ์ด๋ฆ„์œผ๋กœ ๋ฌด์–ธ๊ฐ€๋ฅผ ์„ ์–ธํ•˜๋Š” ๊ฒƒ์€ ์œ„ํ—˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ์— ๋Œ€ํ•ด Pylint๋Š” redefined-outer-name (W0621)์ด๋ผ๋Š” ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

pytest์—์„œ๋Š” ํ”ฝ์Šค์ฒ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ฐ”๊นฅ์˜ ํ•จ์ˆ˜ ์ด๋ฆ„๊ณผ ๋™์ผํ•œ ์ด๋ฆ„์œผ๋กœ ํ…Œ์ŠคํŠธ ํ•จ์ˆ˜์˜ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Pylint๋Š” pytest์˜ ํ”ฝ์Šค์ฒ˜ ๋ฌธ๋ฒ•์„ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๋ฏ€๋กœ ์•ž์„œ ๋งํ–ˆ๋˜ ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

pytest์˜ ์žฅ์ : assert ๋ฌธ ์žฌ์ž‘์„ฑ์œผ๋กœ ์ธํ•œ ํŽธ๋ฆฌํ•จ

unittest: assertEqual(), assertGreater()๊ฐ™์€ assert ๋ฉ”์„œ๋“œ๋ฅผ ์ ์žฌ์ ์†Œ์— ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋ถˆํŽธํ•จ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

pytest: assert ๋‹ค์Œ์— ๋‚˜์˜ค๋Š” ๊ฒ€์ฆ์‹์ด ์–ด๋–ค ์˜๋ฏธ๋ฅผ ๊ฐ€์ง€๋Š”์ง€ ์ž๋™์œผ๋กœ ๋ถ„์„ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์–ธ์–ด์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” assert ๋ฌธ(assert 1 == 2)์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ๋Œ€์‹  ํ…Œ์ŠคํŒ… ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ถ”๊ฐ€์ ์œผ๋กœ ์ง€์›ํ•˜๋Š” assert ๋ฉ”์„œ๋“œ(assertEqual(1, 2))๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š”, ํ…Œ์ŠคํŠธ ์‹คํŒจ ์‹œ ์ข€ ๋” ์ •ํ™•ํ•œ ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ์–ป๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค.

assert ๋ฌธ์€ assert ๋‹ค์Œ์— ๋‚˜์˜ค๋Š” ํ‘œํ˜„์‹์˜ ์„ฑ๊ณต/์‹คํŒจ ์—ฌ๋ถ€๋งŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” 1 == 2๊ฐ€ ๊ฐ™์Œ์„ ๋น„๊ตํ•˜๋Š” ๊ฒƒ์ด๊ณ  1 > 2๊ฐ€ ๋Œ€์†Œ๋ฅผ ๋น„๊ตํ•˜๋Š” ๊ฒƒ์ด๋ผ๋Š” ๊ฑธ ์•Œ์ง€๋งŒ assert ๋ฌธ์€ ์•Œ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. assert ์‹คํŒจ ๋ฉ”์‹œ์ง€์— ํ‘œํ˜„์‹์˜ ์˜๋„๋ฅผ ๋‹ด๊ธฐ ์œ„ํ•ด์„œ๋Š”, ์•ž์„œ ๋งํ•œ assert ๋ฉ”์„œ๋“œ์™€ ๊ฐ™์€ ์ถ”๊ฐ€์ ์ธ ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

pytest๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋” ์ด์ƒ ์—ฌ๋Ÿฌ ์ข…๋ฅ˜์˜ assert ๋ฉ”์„œ๋“œ๋ฅผ ๋ฒˆ๊ฐˆ์•„ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. assert ๋ฌธ ํ•˜๋‚˜๋กœ ๋ชจ๋“  ๊ฒƒ์„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. pytest๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์„ฑํ•œ ํŒŒ์ด์ฌ ์ฝ”๋“œ์—์„œ assert ๋ฌธ์„ ๋ถ„์„ํ•œ ๋’ค, ์ƒ์„ธํ•œ ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ๋„์šฐ๋„๋ก ๋‚ด๋ถ€์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์žฌ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด assert ๋ฌธ๋งŒ์„ ์‚ฌ์šฉํ•˜๊ณ ๋„ ํ’๋ถ€ํ•œ ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ Behind the scenes of pytestโ€™s new assertion rewriting์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

pytest์˜ ๋‹จ์ : assert ๋ฌธ ์žฌ์ž‘์„ฑ์˜ ํ•œ๊ณ„

unittest: ํŠน๋ณ„ํ•œ ๊ฒŒ ์—†์Šต๋‹ˆ๋‹ค.

pytest: ์žฌ์ž‘์„ฑ์œผ๋กœ ์ธํ•ด ๋ฌธ์ œ๊ฐ€ ๋ ๋งŒํ•œ ๊ฒฝ์šฐ๋ฅผ ์ธ์ง€ํ•˜๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

assert ๋ฌธ ์žฌ์ž‘์„ฑ์€ ํŽธ๋ฆฌํ•œ ๊ธฐ๋Šฅ์ด์ง€๋งŒ, unittest์˜ assertRegex()๊ฐ™์€ ๋ณต์žกํ•œ assert ๋ฉ”์„œ๋“œ๋ฅผ ๋Œ€์‹ ํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ unittest์˜ assertRegex()์™€ pytest์˜ assert re.search()๋ฅผ ์ด์šฉํ•ด ์ •๊ทœ ํ‘œํ˜„์‹์œผ๋กœ ๋ฌธ์ž์—ด์„ ๊ฒ€์ƒ‰ํ•˜๋Š” ํ…Œ์ŠคํŠธ์™€ ๊ทธ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค. assertRegex()๋ฅผ ์‚ฌ์šฉํ•œ ์ชฝ์˜ ์‹คํŒจ ๋ฉ”์‹œ์ง€๊ฐ€ ๋” ๋ช…ํ™•ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

    def test_regex_unittest(self):
>       self.assertRegex("foobar", "var")
E       AssertionError: Regex didn't match: 'var' not found in 'foobar'
    def test_regex_pytest():
>       assert search("var", "foobar")
E       AssertionError: assert None
E        +  where None = search('var', 'foobar')

๋˜๋‹ค๋ฅธ ๋ฌธ์ œ์ ์€ pytest๊ฐ€ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ”์œ„ ๋‚ด์—์„œ๋งŒ assert ์žฌ์ž‘์„ฑ ๊ธฐ๋Šฅ์ด ์ด๋ฃจ์–ด์ง„๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์™ธ๋ถ€ ํŒŒ์ด์ฌ ์ฝ”๋“œ์—์„œ assert ๋ฌธ์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ register_assert_rewrite()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํŒŒ์ผ์„ ๋“ฑ๋กํ•ด์•ผ ์žฌ์ž‘์„ฑ์ด ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค.

pytest์˜ ์žฅ์ : ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ

unittest: ๊ผญ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋งŒ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

pytest: ๋‹ค์–‘ํ•œ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค:

๊ฒฐ๋ก : ์ด๋Ÿด ๋•Œ ์‚ฌ์šฉํ•˜์„ธ์š”

unittest: ์ด๋Ÿด ๋•Œ ์‚ฌ์šฉํ•˜์„ธ์š”:

pytest: ์ด๋Ÿด ๋•Œ ์‚ฌ์šฉํ•˜์„ธ์š”:

unittest ๊ธฐ๋ฐ˜์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋˜, assert ๋ฌธ ์žฌ์ž‘์„ฑ์ด๋‚˜ ๋ณ‘๋ ฌ ํ…Œ์ŠคํŠธ, ํ…Œ์ŠคํŠธ ์‹คํŒจ ๋””๋ฒ„๊น…๊ณผ ๊ฐ™์€ pytest์˜ ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿด ๋•Œ unittest๋ฅผ ์“ธ์ง€ pytest๋ฅผ ์“ธ์ง€ ๊ณ ๋ฏผํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค. pytest๋Š” unittest๋กœ ์ž‘์„ฑํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋Œ๋ฆด ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค7.

์ฐธ๊ณ 

Footnotes

  1. https://nose.readthedocs.io/en/latest/#note-to-users

    Nose has been in maintenance mode for the past several years and will likely cease without a new person/team to take over maintainership. New projects should consider using Nose2, py.test, or just plain unittest/unittest2.

    โ†ฉ
  2. https://docs.python.org/3/library/test.html

    The test package contains all regression tests for Python ...

    All new tests should be written using the unittest or doctest module.

    โ†ฉ
  3. https://docs.python.org/3/library/unittest.html

    The unittest unit testing framework was originally inspired by JUnit ...

    โ†ฉ
  4. https://www.python.org/dev/peps/pep-0008/#method-names-and-instance-variables

    Method Names and Instance Variables

    ... lowercase with words separated by underscores as necessary to improve readability.

    โ†ฉ
  5. https://www.python.org/dev/peps/pep-0008/#a-foolish-consistency-is-the-hobgoblin-of-little-minds

    ... Consistency with this style guide is important. Consistency within a project is more important. ...

    โ†ฉ
  6. http://pyunit.sourceforge.net/

    In production use on many sites since the first release in late 1999

    โ†ฉ
  7. http://doc.pytest.org/en/latest/unittest.html โ†ฉ