This commit is contained in:
elie-sokhon 2025-07-09 10:14:18 +02:00 committed by GitHub
commit fc73c619c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 694 additions and 43 deletions

View File

@ -0,0 +1,55 @@
name: Lint and Test
on:
push:
branches:
- main
- test/my_changes
pull_request:
branches:
- main
- test/my_changes
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m venv venv
source venv/bin/activate
pip install -r python/requirements-dev.txt
- name: Run Black (formatting)
run: |
black --check .
- name: Run isort (imports)
run: |
isort --check-only .
- name: Run flake8 (style)
run: |
flake8 .
- name: Run pylint (quality)
run: |
pylint python/gilded_rose.py
- name: Run mypy (type checking)
run: |
mypy python/gilded_rose.py
- name: Run tests
run: |
python -m unittest discover -s python/tests

44
python/Documentation.md Normal file
View File

@ -0,0 +1,44 @@
Gilded Rose Refactoring — Clean Code Edition (Python)
Introduction
This project is a clean-code refactor of the well-known Gilded Rose Kata. The goal of this refactoring effort was to apply python best practices, clean code, reduce cyclomatic complexity, and improve the maintainability and testability of the code. Key software design concepts such as the Open/Closed Principle (OCP), encapsulation, polymorphism, and modular architecture were used to restructure the core logic.
1. Refactored `update_quality` Using the Open/Closed Principle
Originally, the `update_quality` method in `GildedRose` contained a complex set of `if-elif` conditionals handling all item behaviors. This violated the Open/Closed Principle (OCP) by requiring changes to the method every time a new item type was added. We replaced this with a polymorphic structure, using item wrapper classes to handle item-specific behavior.
Old style:
if item.name == "Aged Brie":
...
New style:
wrapper = item_factory(item)
wrapper.update_quality()
2. Introduced a Factory Function: `item_factory()`
The factory function is responsible for returning an instance of the appropriate wrapper class based on the item name. This makes the core logic in `GildedRose` agnostic to item-specific rules and makes the system extensible without touching legacy code.
3. Created Dedicated Classes per Item Type
Each item behavior is now encapsulated in a dedicated class that extends a base wrapper. The classes include:
• AgedBrieItem
• BackstagePassItem
• SulfurasItem
• ConjuredItem
• DefaultItem
This use of polymorphism keeps the rules modular, isolated, and testable.
4. Maintained Interface Compatibility
The original `Item` class and `items` property were left untouched as required. This ensured backward compatibility with existing code and met the constraints defined in the kata.
5. Restructured Codebase for Clarity
Project structure:
python/
├── gilded_rose.py
├── item_wrappers.py
├── aged_brie.py
├── backstage.py
├── sulfuras.py
├── conjured.py
└── tests/
6. GitHub Actions CI/CD Pipeline
A GitHub Actions workflow was created to enforce code quality and automate tests. The workflow runs on pushes or PRs to the `main` and `test/my_changes` branches.
Tasks include:
• Code formatting (`black`)
• Import sorting (`isort`)
• Linting (`flake8`)
• Static code analysis (`pylint`)
• Type checking (`mypy`)
• Unit + Approval tests (`pytest`, `approvaltests`)
7. Tests Refactored
Tests were restructured into logical classes for each item type, improving readability and focus. Approval testing was preserved for regression validation.

12
python/aged_brie.py Normal file
View File

@ -0,0 +1,12 @@
from item_wrappers import ItemWrapper
class AgedBrieItem(ItemWrapper):
def update(self):
self.increase_quality()
self.item.sell_in -= 1
if self.item.sell_in < 0:
self.increase_quality()
def increase_quality(self):
if self.item.quality < 50:
self.item.quality += 1

19
python/backstage.py Normal file
View File

@ -0,0 +1,19 @@
from item_wrappers import ItemWrapper
class BackstagePassItem(ItemWrapper):
def update(self):
self.increase_quality()
if self.item.sell_in <= 10:
self.increase_quality()
if self.item.sell_in <= 5:
self.increase_quality()
self.item.sell_in -= 1
if self.item.sell_in < 0:
self.item.quality = 0
def increase_quality(self):
if self.item.quality < 50:
self.item.quality += 1

14
python/conjured.py Normal file
View File

@ -0,0 +1,14 @@
from item_wrappers import ItemWrapper
class ConjuredItem(ItemWrapper):
def update(self):
self.decrease_quality()
self.decrease_quality()
self.item.sell_in -= 1
if self.item.sell_in < 0:
self.decrease_quality()
self.decrease_quality()
def decrease_quality(self):
if self.item.quality > 0:
self.item.quality -= 1

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from item_wrappers import item_factory
class GildedRose(object):
@ -7,40 +8,8 @@ class GildedRose(object):
def update_quality(self):
for item in self.items:
if item.name != "Aged Brie" and item.name != "Backstage passes to a TAFKAL80ETC concert":
if item.quality > 0:
if item.name != "Sulfuras, Hand of Ragnaros":
item.quality = item.quality - 1
else:
if item.quality < 50:
item.quality = item.quality + 1
if item.name == "Backstage passes to a TAFKAL80ETC concert":
if item.sell_in < 11:
if item.quality < 50:
item.quality = item.quality + 1
if item.sell_in < 6:
if item.quality < 50:
item.quality = item.quality + 1
if item.name != "Sulfuras, Hand of Ragnaros":
item.sell_in = item.sell_in - 1
if item.sell_in < 0:
if item.name != "Aged Brie":
if item.name != "Backstage passes to a TAFKAL80ETC concert":
if item.quality > 0:
if item.name != "Sulfuras, Hand of Ragnaros":
item.quality = item.quality - 1
else:
item.quality = item.quality - item.quality
else:
if item.quality < 50:
item.quality = item.quality + 1
wrapper = item_factory(item)
wrapper.update()
class Item:
def __init__(self, name, sell_in, quality):
self.name = name
self.sell_in = sell_in
self.quality = quality
def __repr__(self):
return "%s, %s, %s" % (self.name, self.sell_in, self.quality)

43
python/item_wrappers.py Normal file
View File

@ -0,0 +1,43 @@
from models import Item
class ItemWrapper:
"""
Classe abstraite représentant un item enrichi d'une logique métier propre.
"""
def __init__(self, item: Item):
self.item = item
def update(self):
raise NotImplementedError("Subclasses must implement update().")
class NormalItem(ItemWrapper):
def update(self):
self.decrease_quality()
self.item.sell_in -= 1
if self.item.sell_in < 0:
self.decrease_quality()
def decrease_quality(self):
if self.item.quality > 0:
self.item.quality -= 1
def item_factory(item: Item) -> ItemWrapper:
"""
Retourne un wrapper métier adapté à l'item donné.
"""
if item.name == "Aged Brie":
from aged_brie import AgedBrieItem
return AgedBrieItem(item)
elif item.name == "Sulfuras, Hand of Ragnaros":
from sulfuras import SulfurasItem
return SulfurasItem(item)
elif item.name == "Backstage passes to a TAFKAL80ETC concert":
from backstage import BackstagePassItem
return BackstagePassItem(item)
elif "Conjured" in item.name:
from conjured import ConjuredItem
return ConjuredItem(item)
else:
return NormalItem(item)

8
python/models.py Normal file
View File

@ -0,0 +1,8 @@
class Item:
def __init__(self, name, sell_in, quality):
self.name = name
self.sell_in = sell_in
self.quality = quality
def __repr__(self):
return "%s, %s, %s" % (self.name, self.sell_in, self.quality)

View File

@ -2,3 +2,9 @@ pytest
approvaltests
pytest-approvaltests
coverage
black
flake8
pylint
isort
mypy

5
python/sulfuras.py Normal file
View File

@ -0,0 +1,5 @@
from item_wrappers import ItemWrapper
class SulfurasItem(ItemWrapper):
def update(self):
pass # Sulfuras does not change in quality or sell_in

View File

@ -0,0 +1,373 @@
OMGHAI!
-------- day 0 --------
name, sellIn, quality
+5 Dexterity Vest, 10, 20
Aged Brie, 2, 0
Elixir of the Mongoose, 5, 7
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 15, 20
Backstage passes to a TAFKAL80ETC concert, 10, 49
Backstage passes to a TAFKAL80ETC concert, 5, 49
Conjured Mana Cake, 3, 6
-------- day 1 --------
name, sellIn, quality
+5 Dexterity Vest, 9, 19
Aged Brie, 1, 1
Elixir of the Mongoose, 4, 6
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 14, 21
Backstage passes to a TAFKAL80ETC concert, 9, 50
Backstage passes to a TAFKAL80ETC concert, 4, 50
Conjured Mana Cake, 2, 4
-------- day 2 --------
name, sellIn, quality
+5 Dexterity Vest, 8, 18
Aged Brie, 0, 2
Elixir of the Mongoose, 3, 5
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 13, 22
Backstage passes to a TAFKAL80ETC concert, 8, 50
Backstage passes to a TAFKAL80ETC concert, 3, 50
Conjured Mana Cake, 1, 2
-------- day 3 --------
name, sellIn, quality
+5 Dexterity Vest, 7, 17
Aged Brie, -1, 4
Elixir of the Mongoose, 2, 4
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 12, 23
Backstage passes to a TAFKAL80ETC concert, 7, 50
Backstage passes to a TAFKAL80ETC concert, 2, 50
Conjured Mana Cake, 0, 0
-------- day 4 --------
name, sellIn, quality
+5 Dexterity Vest, 6, 16
Aged Brie, -2, 6
Elixir of the Mongoose, 1, 3
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 11, 24
Backstage passes to a TAFKAL80ETC concert, 6, 50
Backstage passes to a TAFKAL80ETC concert, 1, 50
Conjured Mana Cake, -1, 0
-------- day 5 --------
name, sellIn, quality
+5 Dexterity Vest, 5, 15
Aged Brie, -3, 8
Elixir of the Mongoose, 0, 2
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 10, 25
Backstage passes to a TAFKAL80ETC concert, 5, 50
Backstage passes to a TAFKAL80ETC concert, 0, 50
Conjured Mana Cake, -2, 0
-------- day 6 --------
name, sellIn, quality
+5 Dexterity Vest, 4, 14
Aged Brie, -4, 10
Elixir of the Mongoose, -1, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 9, 27
Backstage passes to a TAFKAL80ETC concert, 4, 50
Backstage passes to a TAFKAL80ETC concert, -1, 0
Conjured Mana Cake, -3, 0
-------- day 7 --------
name, sellIn, quality
+5 Dexterity Vest, 3, 13
Aged Brie, -5, 12
Elixir of the Mongoose, -2, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 8, 29
Backstage passes to a TAFKAL80ETC concert, 3, 50
Backstage passes to a TAFKAL80ETC concert, -2, 0
Conjured Mana Cake, -4, 0
-------- day 8 --------
name, sellIn, quality
+5 Dexterity Vest, 2, 12
Aged Brie, -6, 14
Elixir of the Mongoose, -3, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 7, 31
Backstage passes to a TAFKAL80ETC concert, 2, 50
Backstage passes to a TAFKAL80ETC concert, -3, 0
Conjured Mana Cake, -5, 0
-------- day 9 --------
name, sellIn, quality
+5 Dexterity Vest, 1, 11
Aged Brie, -7, 16
Elixir of the Mongoose, -4, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 6, 33
Backstage passes to a TAFKAL80ETC concert, 1, 50
Backstage passes to a TAFKAL80ETC concert, -4, 0
Conjured Mana Cake, -6, 0
-------- day 10 --------
name, sellIn, quality
+5 Dexterity Vest, 0, 10
Aged Brie, -8, 18
Elixir of the Mongoose, -5, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 5, 35
Backstage passes to a TAFKAL80ETC concert, 0, 50
Backstage passes to a TAFKAL80ETC concert, -5, 0
Conjured Mana Cake, -7, 0
-------- day 11 --------
name, sellIn, quality
+5 Dexterity Vest, -1, 8
Aged Brie, -9, 20
Elixir of the Mongoose, -6, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 4, 38
Backstage passes to a TAFKAL80ETC concert, -1, 0
Backstage passes to a TAFKAL80ETC concert, -6, 0
Conjured Mana Cake, -8, 0
-------- day 12 --------
name, sellIn, quality
+5 Dexterity Vest, -2, 6
Aged Brie, -10, 22
Elixir of the Mongoose, -7, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 3, 41
Backstage passes to a TAFKAL80ETC concert, -2, 0
Backstage passes to a TAFKAL80ETC concert, -7, 0
Conjured Mana Cake, -9, 0
-------- day 13 --------
name, sellIn, quality
+5 Dexterity Vest, -3, 4
Aged Brie, -11, 24
Elixir of the Mongoose, -8, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 2, 44
Backstage passes to a TAFKAL80ETC concert, -3, 0
Backstage passes to a TAFKAL80ETC concert, -8, 0
Conjured Mana Cake, -10, 0
-------- day 14 --------
name, sellIn, quality
+5 Dexterity Vest, -4, 2
Aged Brie, -12, 26
Elixir of the Mongoose, -9, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 1, 47
Backstage passes to a TAFKAL80ETC concert, -4, 0
Backstage passes to a TAFKAL80ETC concert, -9, 0
Conjured Mana Cake, -11, 0
-------- day 15 --------
name, sellIn, quality
+5 Dexterity Vest, -5, 0
Aged Brie, -13, 28
Elixir of the Mongoose, -10, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 0, 50
Backstage passes to a TAFKAL80ETC concert, -5, 0
Backstage passes to a TAFKAL80ETC concert, -10, 0
Conjured Mana Cake, -12, 0
-------- day 16 --------
name, sellIn, quality
+5 Dexterity Vest, -6, 0
Aged Brie, -14, 30
Elixir of the Mongoose, -11, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -1, 0
Backstage passes to a TAFKAL80ETC concert, -6, 0
Backstage passes to a TAFKAL80ETC concert, -11, 0
Conjured Mana Cake, -13, 0
-------- day 17 --------
name, sellIn, quality
+5 Dexterity Vest, -7, 0
Aged Brie, -15, 32
Elixir of the Mongoose, -12, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -2, 0
Backstage passes to a TAFKAL80ETC concert, -7, 0
Backstage passes to a TAFKAL80ETC concert, -12, 0
Conjured Mana Cake, -14, 0
-------- day 18 --------
name, sellIn, quality
+5 Dexterity Vest, -8, 0
Aged Brie, -16, 34
Elixir of the Mongoose, -13, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -3, 0
Backstage passes to a TAFKAL80ETC concert, -8, 0
Backstage passes to a TAFKAL80ETC concert, -13, 0
Conjured Mana Cake, -15, 0
-------- day 19 --------
name, sellIn, quality
+5 Dexterity Vest, -9, 0
Aged Brie, -17, 36
Elixir of the Mongoose, -14, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -4, 0
Backstage passes to a TAFKAL80ETC concert, -9, 0
Backstage passes to a TAFKAL80ETC concert, -14, 0
Conjured Mana Cake, -16, 0
-------- day 20 --------
name, sellIn, quality
+5 Dexterity Vest, -10, 0
Aged Brie, -18, 38
Elixir of the Mongoose, -15, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -5, 0
Backstage passes to a TAFKAL80ETC concert, -10, 0
Backstage passes to a TAFKAL80ETC concert, -15, 0
Conjured Mana Cake, -17, 0
-------- day 21 --------
name, sellIn, quality
+5 Dexterity Vest, -11, 0
Aged Brie, -19, 40
Elixir of the Mongoose, -16, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -6, 0
Backstage passes to a TAFKAL80ETC concert, -11, 0
Backstage passes to a TAFKAL80ETC concert, -16, 0
Conjured Mana Cake, -18, 0
-------- day 22 --------
name, sellIn, quality
+5 Dexterity Vest, -12, 0
Aged Brie, -20, 42
Elixir of the Mongoose, -17, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -7, 0
Backstage passes to a TAFKAL80ETC concert, -12, 0
Backstage passes to a TAFKAL80ETC concert, -17, 0
Conjured Mana Cake, -19, 0
-------- day 23 --------
name, sellIn, quality
+5 Dexterity Vest, -13, 0
Aged Brie, -21, 44
Elixir of the Mongoose, -18, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -8, 0
Backstage passes to a TAFKAL80ETC concert, -13, 0
Backstage passes to a TAFKAL80ETC concert, -18, 0
Conjured Mana Cake, -20, 0
-------- day 24 --------
name, sellIn, quality
+5 Dexterity Vest, -14, 0
Aged Brie, -22, 46
Elixir of the Mongoose, -19, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -9, 0
Backstage passes to a TAFKAL80ETC concert, -14, 0
Backstage passes to a TAFKAL80ETC concert, -19, 0
Conjured Mana Cake, -21, 0
-------- day 25 --------
name, sellIn, quality
+5 Dexterity Vest, -15, 0
Aged Brie, -23, 48
Elixir of the Mongoose, -20, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -10, 0
Backstage passes to a TAFKAL80ETC concert, -15, 0
Backstage passes to a TAFKAL80ETC concert, -20, 0
Conjured Mana Cake, -22, 0
-------- day 26 --------
name, sellIn, quality
+5 Dexterity Vest, -16, 0
Aged Brie, -24, 50
Elixir of the Mongoose, -21, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -11, 0
Backstage passes to a TAFKAL80ETC concert, -16, 0
Backstage passes to a TAFKAL80ETC concert, -21, 0
Conjured Mana Cake, -23, 0
-------- day 27 --------
name, sellIn, quality
+5 Dexterity Vest, -17, 0
Aged Brie, -25, 50
Elixir of the Mongoose, -22, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -12, 0
Backstage passes to a TAFKAL80ETC concert, -17, 0
Backstage passes to a TAFKAL80ETC concert, -22, 0
Conjured Mana Cake, -24, 0
-------- day 28 --------
name, sellIn, quality
+5 Dexterity Vest, -18, 0
Aged Brie, -26, 50
Elixir of the Mongoose, -23, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -13, 0
Backstage passes to a TAFKAL80ETC concert, -18, 0
Backstage passes to a TAFKAL80ETC concert, -23, 0
Conjured Mana Cake, -25, 0
-------- day 29 --------
name, sellIn, quality
+5 Dexterity Vest, -19, 0
Aged Brie, -27, 50
Elixir of the Mongoose, -24, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -14, 0
Backstage passes to a TAFKAL80ETC concert, -19, 0
Backstage passes to a TAFKAL80ETC concert, -24, 0
Conjured Mana Cake, -26, 0
-------- day 30 --------
name, sellIn, quality
+5 Dexterity Vest, -20, 0
Aged Brie, -28, 50
Elixir of the Mongoose, -25, 0
Sulfuras, Hand of Ragnaros, 0, 80
Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, -15, 0
Backstage passes to a TAFKAL80ETC concert, -20, 0
Backstage passes to a TAFKAL80ETC concert, -25, 0
Conjured Mana Cake, -27, 0

View File

@ -1,15 +1,112 @@
# -*- coding: utf-8 -*-
import unittest
import os
import sys
from models import Item
from gilded_rose import Item, GildedRose
# insérer le dossier parent dans sys.path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from gilded_rose import GildedRose
class GildedRoseTest(unittest.TestCase):
def test_foo(self):
items = [Item("foo", 0, 0)]
gilded_rose = GildedRose(items)
gilded_rose.update_quality()
self.assertEqual("fixme", items[0].name)
# class GildedRoseTest(unittest.TestCase):
# def test_foo(self):
# items = [Item("foo", 0, 0)]
# gilded_rose = GildedRose(items)
# gilded_rose.update_quality()
# self.assertEqual("fixme", items[0].name)
class TestNormalItem(unittest.TestCase):
def test_quality_decreases_by_one_before_expiration(self):
items = [Item(name="+5 Dexterity Vest", sell_in=10, quality=20)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 19)
self.assertEqual(items[0].sell_in, 9)
def test_quality_decreases_by_two_after_expiration(self):
items = [Item(name="Elixir of the Mongoose", sell_in=0, quality=6)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 4)
self.assertEqual(items[0].sell_in, -1)
def test_quality_never_negative(self):
items = [Item(name="Elixir of the Mongoose", sell_in=0, quality=0)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 0)
class TestAgedBrie(unittest.TestCase):
def test_quality_increases_by_one_before_expiration(self):
items = [Item(name="Aged Brie", sell_in=2, quality=0)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 1)
self.assertEqual(items[0].sell_in, 1)
def test_quality_increases_by_two_after_expiration(self):
items = [Item(name="Aged Brie", sell_in=0, quality=0)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 2)
self.assertEqual(items[0].sell_in, -1)
def test_quality_never_exceeds_fifty(self):
items = [Item(name="Aged Brie", sell_in=5, quality=50)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 50)
class TestSulfuras(unittest.TestCase):
def test_quality_and_sellin_never_change(self):
items = [Item(name="Sulfuras, Hand of Ragnaros", sell_in=5, quality=80)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 80)
self.assertEqual(items[0].sell_in, 5)
class TestBackstagePasses(unittest.TestCase):
def test_quality_increases_by_one_above_10_days(self):
items = [Item(name="Backstage passes to a TAFKAL80ETC concert", sell_in=15, quality=20)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 21)
def test_quality_increases_by_two_between_10_and_6_days(self):
items = [Item(name="Backstage passes to a TAFKAL80ETC concert", sell_in=10, quality=20)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 22)
def test_quality_increases_by_three_between_5_and_1_days(self):
items = [Item(name="Backstage passes to a TAFKAL80ETC concert", sell_in=5, quality=20)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 23)
def test_quality_drops_to_zero_after_concert(self):
items = [Item(name="Backstage passes to a TAFKAL80ETC concert", sell_in=0, quality=20)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 0)
def test_quality_never_exceeds_fifty(self):
items = [Item(name="Backstage passes to a TAFKAL80ETC concert", sell_in=5, quality=49)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 50)
class TestConjuredItems(unittest.TestCase):
def test_quality_degrades_twice_as_fast_before_expiration(self):
items = [Item(name="Conjured Mana Cake", sell_in=3, quality=6)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 4)
def test_quality_degrades_twice_as_fast_after_expiration(self):
items = [Item(name="Conjured Mana Cake", sell_in=0, quality=6)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 2)
def test_quality_never_negative(self):
items = [Item(name="Conjured Mana Cake", sell_in=0, quality=1)]
GildedRose(items).update_quality()
self.assertEqual(items[0].quality, 0)
if __name__ == '__main__':

View File

@ -1,9 +1,14 @@
import io
import sys
import os
# insérer le dossier parent dans sys.path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from approvaltests.reporters import PythonNativeReporter
from approvaltests import verify
from texttest_fixture import main
reporter = PythonNativeReporter()
def test_gilded_rose_approvals():
orig_sysout = sys.stdout
try:
@ -15,7 +20,7 @@ def test_gilded_rose_approvals():
finally:
sys.stdout = orig_sysout
verify(answer)
verify(answer, reporter=reporter)
if __name__ == "__main__":
test_gilded_rose_approvals()

View File

@ -2,6 +2,7 @@
from __future__ import print_function
from gilded_rose import *
from models import Item
def main():