[Recent Interests] Data Engineering / MLOps
[test] ์‰ฝ๊ฒŒ ์‹œ์ž‘ํ•˜๋Š” ํŒŒ์ด์ฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

์†Œํ”„ํŠธ์›จ์–ด ํ…Œ์ŠคํŠธ๋Š” ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ํ”„๋กœ์„ธ์Šค์˜ ์ค‘์š”ํ•œ ๋‹จ๊ณ„๋กœ์„œ, ๊ฐœ๋ฐœ๋œ ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ๋ชฉํ‘œ์— ๋ถ€ํ•ฉํ•˜๋ฉฐ ์•ˆ์ •์ ์ด๊ณ  ํšจ์œจ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•œ ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋Š” ๋ฒ„๊ทธ๋ฅผ ์ฐพ๊ณ  ์ˆ˜์ •ํ•˜๋ฉฐ, ์†Œํ”„ํŠธ์›จ์–ด์˜ ํ’ˆ์งˆ์„ ํ–ฅ์ƒ์‹œํ‚ค๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ์‹ ๋ขฐ์„ฑ ์žˆ๋Š” ์ œํ’ˆ์„ ์ œ๊ณตํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค.

 

ํ•จ์ˆ˜์— ์–ด๋–ค ์ž…๋ ฅ์ด ์ฃผ์–ด์กŒ์„๋•Œ ๊ธฐ๋Œ€ํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ค์–ด ๋‚ด๋Š”์ง€์™€ ๊ฐ™์€ ๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ๋ถ€ํ„ฐ ํ•œ ๋ฒˆ์— ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘์†ํ• ๋•Œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ž˜ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ ๊นŒ์ง€ ๋‹ค์–‘ํ•œ ํ…Œ์ŠคํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์€ ๋ฒ„๊ทธ๊ฐ€ ์žฌ๋ฐœํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ณ , ์ดํ›„ ๋ฆฌํŒฉํ† ๋ง ๊ณผ์ •์—์„œ ์‹œ๊ฐ„์„ ์ ˆ์•ฝํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

 

[ ํ…Œ์ŠคํŠธ์˜ ์ข…๋ฅ˜ ]
- ๋‹จ์œ„ ํ…Œ์ŠคํŠธ(Unit Testing): ๊ฐœ๋ณ„ ๋ชจ๋“ˆ ๋˜๋Š” ํ•จ์ˆ˜๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค.
- ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ(Integration Testing): ๊ฐ ๋ชจ๋“ˆ์ด ๊ฒฐํ•ฉ๋˜์–ด ์ „์ฒด ์‹œ์Šคํ…œ์ด ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค.
- ์‹œ์Šคํ…œ ํ…Œ์ŠคํŠธ(System Testing): ์ „์ฒด ์‹œ์Šคํ…œ์ด ์‚ฌ์šฉ์ž์˜ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ถฉ์กฑ์‹œํ‚ค๊ณ  ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค.
- ์ธ์ˆ˜ ํ…Œ์ŠคํŠธ(Acceptance Testing): ์ตœ์ข… ์‚ฌ์šฉ์ž ๋˜๋Š” ๊ณ ๊ฐ์ด ์‹œ์Šคํ…œ์˜ ๊ธฐ๋Šฅ ๋ฐ ์„ฑ๋Šฅ์„ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค.

 

์ด๋ฒˆ ๊ฒŒ์‹œ๊ธ€์—์„œ๋Š” ๊ฐ„๋‹จํ•œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ด์ฌ ๋‚ด์žฅ ํŒจํ‚ค์ง€์ธ unittest๋ฅผ ์ด์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๊ณ , ๋งˆ์ง€๋ง‰์—๋Š” ์ข€ ๋” ์‚ฌ์šฉํ•˜๊ธฐ ํŽธ๋ฆฌํ•œ pytest๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์•Œ์•„๋ด…๋‹ˆ๋‹ค.

 

๋‹จ์œ„ ํ…Œ์ŠคํŠธ(unit testing)

๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋Š” ์†Œํ”„ํŠธ์›จ์–ด์˜ ๋ชจ๋“  ์ž‘์€ ๋ถ€๋ถ„๋“ค์ด ๋™์ž‘ํ•˜๋Š”์ง€๋ฅผ ํ™•์ธํ•˜๊ณ , ์—”๋“œํˆฌ์—”๋“œ ํ…Œ์ŠคํŠธ ๊ฐ™์€ ๋Œ€๊ทœ๋ชจ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ํ† ๋Œ€๋ฅผ ๋งˆ๋ จํ•ด์ค๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฐœ๋ณ„ ์ฝ”๋“œ ๋‹จ์œ„๊ฐ€ ์˜ˆ์ƒํ•œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ๊ฒ€์‚ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒŒ์ด์ฌ์— ๋‚ด์žฅ๋œ unittest๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ„ฐ๋ฏธ๋„์— `python -m unittest`๋ผ๊ณ  ์ž…๋ ฅํ•˜๋ฉด ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์•„๋ž˜์™€ ๊ฐ™์€ ๊ณผ์ •์œผ๋กœ ํ…Œ์ŠคํŠธ๊ฐ€ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค.

 

1. ํ˜„์žฌ ๋””๋ ‰ํ„ฐ๋ฆฌ ๋ฐ ํ•˜์œ„ ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ test_* ๋˜๋Š” *_test๋ผ๋Š” ์ด๋ฆ„์˜ ๋ชจ๋“ˆ ์ฐพ๊ธฐ

2. ํ•ด๋‹น ๋ชจ๋“ˆ์—์„œ unittest.TestCase๋ฅผ ์ƒ์†ํ•œ ํด๋ž˜์Šค๋ฅผ ์ฐพ๊ธฐ

3. ํ•ด๋‹น ํด๋ž˜์Šค์—์„œ test_๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ฐพ๊ธฐ

 

ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด์„œ ์ „์ž์ƒ๊ฑฐ๋ž˜ ์‹œ์Šคํ…œ์˜ ์ƒํ’ˆ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ด…๋‹ˆ๋‹ค. 

# pruduct.py
class Product:
    def __init__(self, name, size, color):  # Product ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ์ƒํ’ˆ ์†์„ฑ์ด ์ง€์ •
        self.name = name
        self.size = size
        self.color = color

    def transform_name_for_sku(self):
        return self.name.upper()

    def transform_color_for_sku(self):
        return self.color.upper()

    def generate_sku(self):  # SKU๋Š” ์ƒํ’ˆ ์†์„ฑ์„ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„ํ•ฉ๋‹ˆ๋‹ค.
        """
        Generates a SKU for this product.

        Example:
            >>> small_black_shoes = Product('shoes', 'S', 'black')
            >>> small_black_shoes.generate_sku()
            'SHOES-S-BLACK'
        """
        name = self.transform_name_for_sku()
        color = self.transform_color_for_sku()
        return f'{name}-{self.size}-{color}'

 

์ด ํด๋ž˜์Šค๋Š” ์ „์ž์ƒ๊ฑฐ๋ž˜ ์‹œ์Šคํ…œ์—์„œ ๊ตฌ๋งคํ•  ์ƒํ’ˆ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์ƒํ’ˆ์€ ์ด๋ฆ„, ์‚ฌ์ด์ฆˆ ์˜ต์…˜, ์ƒ‰์ƒ ์†์„ฑ์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  ์ด๋Ÿฌํ•œ ์†์„ฑ์˜ ์กฐํ•ฉ์ด ์žฌ๊ณ ๊ด€๋ฆฌ๋‹จ์œ„(Stock Keeping Unit, SKU)๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. SKU๋Š” ๊ฐ€๊ฒฉ๊ณผ ์žฌ๊ณ  ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๊ณ ์œ ํ•œ ์‹๋ณ„์ž์ž…๋‹ˆ๋‹ค.

 

๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ์˜ ๊ตฌ์กฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ด๋ฃจ์–ด์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž…๋ ฅ ์ค€๋น„ -> ์˜ˆ์ƒ ๊ฒฐ๊ณผ ์‹๋ณ„ -> ์‹ค์ œ ๊ฒฐ๊ณผ ์–ป๊ธฐ -> ์˜ˆ์ƒ ๊ฒฐ๊ณผ์™€ ์‹ค์ œ ๊ฒฐ๊ณผ ๋น„๊ต

 

`Pruduct` ํด๋ž˜์Šค์˜ `transform_name_for_sku`๋ฅผ ํ…Œ์ŠคํŠธ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์•„๋ž˜์˜ ๊ณผ์ •์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

1. ์ด๋ฆ„, ์‚ฌ์ด์ฆˆ, ์ƒ‰์ƒ์œผ๋กœ Product ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ

2. `transform_name_for_sku`๊ฐ€ `name.upper()`๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€๋ฅผ ํ™•์ธ. ์˜ˆ์ƒ ๊ฒฐ๊ณผ๋Š” ๋Œ€๋ฌธ์ž ์ด๋ฆ„

3. `Product` ์ธ์Šคํ„ด์Šค์˜ `transform_name_for_sku` ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ณ€์ˆ˜์— ์ €์žฅ

4. ์˜ˆ์ƒ ๊ฒฐ๊ณผ์™€ ์‹ค์ œ ๊ฒฐ๊ณผ๋ฅผ ๋น„๊ต

 

์ฒซ ๋ฒˆ์งธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ด…๋‹ˆ๋‹ค.

# test_product.py
import unittest

from product import Product

class ProductTestCase(unittest.TestCase):
    def test_transform_name_for_sku(self):
        # ์ด ์†์„ฑ์„ ๊ฐ€์ง„ ์ƒํ’ˆ์„ ๋งŒ๋“ค์–ด์„œ transform_name_for_sku ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•œ ์ค€๋น„๋ฅผ ํ•ฉ๋‹ˆ๋‹ค.
        small_black_shoes = Product('shoes', 'S', 'black')
        # ์˜ˆ์ƒ๋˜๋Š” ๊ฐ’์ž…๋‹ˆ๋‹ค.
        expected_value = 'SHOES'  
        # ๋น„๊ต๋ฅผ ์œ„ํ•ด ์‹ค์ œ ๊ฐ’์„ ์–ป์Šต๋‹ˆ๋‹ค.
        actual_value = small_black_shoes.transform_name_for_sku()  
        # ๋‘ ๊ฐ’์„ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค(๊ฐ™๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ธฐ ๋•Œ๋ฌธ์— assertEqual๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.)
        self.assertEqual(expected_value, actual_value)

 

์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•œ ํ›„์— unittest๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

> python -m unittest
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

์ •์ƒ์ ์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•˜์—ฌ OK๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.  ์œ„์˜ expected_value๋ฅผ 'SHOEZ'๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ๋‹ค์‹œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด๋ด…๋‹ˆ๋‹ค.

> python -m unittest
F
======================================================================
FAIL: test_transform_name_for_sku (test_product.ProductTestCase.test_transform_name_for_sku)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_product.py", line 10, in test_transform_name_for_sku
    self.assertEqual(expected_value, actual_value) 
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 'SHOEZ' != 'SHOES'
- SHOEZ
?     ^
+ SHOES
?     ^


----------------------------------------------------------------------
Ran 1 test in 0.002s

์˜ˆ์ƒ๋˜๋Š” ๊ฒฐ๊ณผ์™€ ๋‹ค๋ฅธ ๊ฐ’์ด ๋‚˜์˜ฌ๋•Œ๋Š” ํ…Œ์ŠคํŠธ์— ์‹คํŒจํ•˜์˜€๋‹ค๋Š” ์•Œ๋ฆผ์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

 

ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ(integration testing)

ํ†ตํ•ฉํ…Œ์ŠคํŠธ๋Š” ์ผ๋ จ์˜ ๋‹จ์œ„ ์ž‘์—… ์ „์ฒด๊ฐ€ ์ž˜ ๋™์ž‘ํ•˜๋Š”์ง€์— ์ดˆ์ ์„ ๋‘ก๋‹ˆ๋‹ค. ๋งŒ์•ฝ 10๊ฐœ์˜ ์™„๋ฒฝํ•œ ๋‹จ์œ„๊ธฐ๋Šฅ์ด ์žˆ๋‹ค๊ณ  ํ•˜๋”๋ผ๋„ ์ด๊ฒƒ๋“ค์„ ๊ฒฐํ•ฉํ•˜์—ฌ ์›ํ•˜๋Š” ์ž‘์—…์„ ํ•  ์ˆ˜ ์—†๋‹ค๋ฉด ์˜ฌ๋ฐ”๋ฅธ ๊ธฐ๋Šฅ์„ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์— ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ณด๋‹ค ์˜ค๋ž˜ ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค. 

ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด `shoppingCart` ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์„œ ์ƒํ’ˆ์„ ์ถ”๊ฐ€ ๋˜๋Š” ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฐ์ดํ„ฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋”•์…”๋„ˆ๋ฆฌ ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

{
    'SHOES-S-BLUE': {'quantity': 1},
    'SHIRT-M-GRAY': {'quantity': 1}
}

 

 `shoppingCart` ํด๋ž˜์Šค๋Š” ์œ„์˜ ๋”•์…”๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ƒํ’ˆ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

# cart.py
from collections import defaultdict

class ShoppingCart:
    def __init__(self):
        self.products = defaultdict(lambda: defaultdict(int)) # defaultdict๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋”•์…”๋„ˆ๋ฆฌ์— ์ œํ’ˆ์ด ์ด๋ฏธ ์žˆ๋Š”์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ๋กœ์ง์ด ๋‹จ์ˆœํ•ด์ง‘๋‹ˆ๋‹ค.

    def add_product(self, product, quantity=1):  # ์ƒํ’ˆ ์ˆ˜๋Ÿ‰ ์ถ”๊ฐ€ํ•˜๊ธฐ
        self.products[product.generate_sku()]['quantity'] += quantity

    def remove_product(self, product, quantity=1):  # ์žฅ๋ฐ”๊ตฌ๋‹ˆ์—์„œ ์ƒํ’ˆ ์ œ๊ฑฐํ•˜๊ธฐ
        sku = product.generate_sku()
        self.products[sku]['quantity'] -= quantity
        if self.products[sku]['quantity'] <= 0:
            del self.products[sku]

 

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ณผ์ •์œผ๋กœ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

- ์žฅ๋ฐ”๊ตฌ๋‹ˆ๋Š” Product ์ธ์Šคํ„ด์Šค์˜ generate_sku ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

- ์ƒํ’ˆ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์ œ๊ฑฐํ•˜๋Š” ์ž‘์—…์€ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋ฉฐ, ์ด์ „์— ์ถ”๊ฐ€๋œ ์ œํ’ˆ๋„ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

# test_cart.py
import unittest
from cart import ShoppingCart
from product import Product

class ShoppingCartTestCase(unittest.TestCase):
    def test_cart_initially_empty(self):
        cart = ShoppingCart()
        # ์นดํŠธ๊ฐ€ ๋น„์–ด์žˆ๋Š”์ง€ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.
        self.assertDictEqual({}, cart.products)

    def test_add_two_of_a_product(self):
        cart = ShoppingCart()
        product = Product('shoes', 'S', 'blue')
        # ์นดํŠธ์— ์ƒํ’ˆ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
        cart.add_product(product, quantity=2)
        # ์ถ”๊ฐ€๋œ ์ƒํ’ˆ์ด ๋™์ผํ•œ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
        self.assertDictEqual({'SHOES-S-BLUE': {'quantity': 2}}, cart.products)

    def test_add_two_different_products(self):
        cart = ShoppingCart()
        product_one = Product('shoes', 'S', 'blue')
        product_two = Product('shirt', 'M', 'gray')
        # ๋‘ ๊ฐœ์˜ ์ƒํ’ˆ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
        cart.add_product(product_one)
        cart.add_product(product_two)
        # ์˜ˆ์ƒ๋˜๋Š” ๊ฒฐ๊ณผ์™€ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.
        self.assertDictEqual(
            {
                'SHOES-S-BLUE': {'quantity': 1},
                'SHIRT-M-GRAY': {'quantity': 1}
            },
            cart.products
        )

    def test_add_and_remove_product(self):
        cart = ShoppingCart()
        product = Product('shoes', 'S', 'blue')
        # ์ƒํ’ˆ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
        cart.add_product(product)
        cart.remove_product(product)
        # ์นดํŠธ์˜ ์ œํ’ˆ์ด ๋น„์–ด์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
        self.assertDictEqual({}, cart.products)

    def test_remove_too_many_products(self):
        cart = ShoppingCart()
        product = Product('shoes', 'S', 'blue')

        cart.add_product(product)
        cart.remove_product(product, quantity=2)
        # ์นดํŠธ์˜ ์ œํ’ˆ์ด ๋น„์—ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
        self.assertDictEqual({}, cart.products)

 

 

ํŒŒ์ด์ฌ์— ๋‚ด์žฅ๋œ unittest๋„ ํ›Œ๋ฅญํ•˜์ง€๋งŒ ์กฐ๊ธˆ ๋” ๊ฐ„๊ฒฐํ•œ ๋ฐฉ์‹์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด pytest๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

pytest๋ฅผ ์„ค์น˜ํ•˜๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ pytest๋งŒ ์ž…๋ ฅํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์ œ๊ณตํ•ด์ค๋‹ˆ๋‹ค.

> pytest
==================== test session starts ==================== 
platform win32 -- Python 3.9.12, pytest-7.1.1, pluggy-1.3.0   
rootdir: [ํ…Œ์ŠคํŠธ ์‹คํ–‰ํ•œ ์ ˆ๋Œ€ ๊ฒฝ๋กœ]
plugins: anyio-3.5.0
collected 3 items                                             

test_product.py ...                                    [100%] 

===================== 3 passed in 0.03s =====================

 

unittest๋ฅผ pytest๋กœ ์ „ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”

- unittest import ๊ตฌ๋ฌธ์„ ์ œ๊ฑฐํ•˜๊ณ  unittest.TestCase์˜ ์ƒ์†์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

- ๋ชจ๋“  self.assertEquel(expect, actual)์€ assert actual == expect๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. 

from product import Product

class TestProduct:  # ๊ธฐ๋ณธ ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.
    def test_transform_name_for_sku(self):
        small_black_shoes = Product('shoes', 'S', 'black')
        assert small_black_shoes.transform_name_for_sku() == 'SHOES'  # assert๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๊ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    def test_transform_color_for_sku(self):
        small_black_shoes = Product('shoes', 'S', 'black')
        assert small_black_shoes.transform_color_for_sku() == 'BLACK'

    def test_generate_sku(self):
        small_black_shoes = Product('shoes', 'S', 'black')
        assert small_black_shoes.generate_sku() == 'SHOES-S-BLACK'

 

์œ„์™€ ๊ฐ™์ด ๋” ์ฝ๊ธฐ ์‰ฌ์šด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

 

์ด๋ฒˆ ๊ฒŒ์‹œ๊ธ€์—์„œ๋Š” ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์˜ค๋Š˜ ๊ฒŒ์‹œ๊ธ€์˜ ๋‚ด์šฉ์„ ์š”์•ฝํ•ด๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

- ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ๋Š” ์ฃผ์–ด์ง„ ์ž…๋ ฅ์— ๋Œ€ํ•ด ์˜ˆ์ƒํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

- ํ…Œ์ŠคํŠธ๋Š” ๋ฒ„๊ทธ๋ฅผ ์žก์•„์ฃผ๊ณ  ๋ฆฌํŒฉํ† ๋ง ์ฝ”๋“œ๋ฅผ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ์–ด ์žฅ๊ธฐ์ ์œผ๋กœ ์‹œ๊ฐ„์„ ์ ˆ์•ฝํ•˜๋Š” ํšจ๊ณผ๋ฅผ ๋ƒ…๋‹ˆ๋‹ค.

- unittest์™€ pytest๋Š” ํŒŒ์ด์ฌ์—์„œ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ๋‹จ์œ„/ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.

 

reference

๋ฐ์ธ ํž๋ผ๋“œ, ํ”„๋กœ๊ทธ๋ž˜๋จธ๋ฅผ ์œ„ํ•œ ํŒŒ์ด์ฌ