mirror of
https://github.com/emilybache/GildedRose-Refactoring-Kata.git
synced 2026-02-18 07:51:29 +00:00
refactor: set global architecture and refactor code
- Create an Ioc container and set factories and services - Adjust degrading Strategies - Clean up and do global chores here and there and avoid Dry code - Write unit and integration tests and ensure correct business logic
This commit is contained in:
parent
181b48aff7
commit
66867fbe7a
@ -1,35 +1,38 @@
|
||||
{
|
||||
"name": "emilybache/gilded-rose-refactoring-kata",
|
||||
"description": "A kata to practice refactoring, tests and polymorphism",
|
||||
"require": {
|
||||
"php": "^7.3 || ^8.0"
|
||||
"name": "emilybache/gilded-rose-refactoring-kata",
|
||||
"description": "A kata to practice refactoring, tests and polymorphism",
|
||||
"require": {
|
||||
"php": "^7.3 || ^8.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GildedRose\\": "src/"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GildedRose\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"phpstan/phpstan": "^0.12.85",
|
||||
"phpstan/phpstan-phpunit": "^0.12.18",
|
||||
"symplify/easy-coding-standard": "^9.3",
|
||||
"symplify/phpstan-extensions": "^9.3",
|
||||
"approvals/approval-tests": "^1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"checkcode": "phpcs src tests --standard=PSR12",
|
||||
"fixcode": "phpcbf src tests --standard=PSR12",
|
||||
"test": "phpunit",
|
||||
"tests": "phpunit",
|
||||
"test-coverage": "phpunit --coverage-html build/coverage",
|
||||
"check-cs": "ecs check",
|
||||
"fix-cs": "ecs check --fix",
|
||||
"phpstan": "phpstan analyse --ansi"
|
||||
"files": [
|
||||
"config/ioc.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"phpstan/phpstan": "^0.12.85",
|
||||
"phpstan/phpstan-phpunit": "^0.12.18",
|
||||
"symplify/easy-coding-standard": "^9.3",
|
||||
"symplify/phpstan-extensions": "^9.3",
|
||||
"approvals/approval-tests": "^1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"checkcode": "phpcs src tests --standard=PSR12",
|
||||
"fixcode": "phpcbf src tests --standard=PSR12",
|
||||
"test": "phpunit",
|
||||
"tests": "phpunit",
|
||||
"test-coverage": "phpunit --coverage-html build/coverage",
|
||||
"check-cs": "ecs check",
|
||||
"fix-cs": "ecs check --fix",
|
||||
"phpstan": "phpstan analyse --ansi"
|
||||
}
|
||||
}
|
||||
|
||||
24
php/config/ioc.php
Normal file
24
php/config/ioc.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use GildedRose\Services\AbstractDegradingStrategyFactory;
|
||||
use GildedRose\Services\DegradingStrategyFactory;
|
||||
|
||||
/**
|
||||
* Here We set Bindings between Interfaces and abstraction to implementation
|
||||
*/
|
||||
const BINDINGS =
|
||||
[
|
||||
AbstractDegradingStrategyFactory::class => DegradingStrategyFactory::class
|
||||
];
|
||||
|
||||
// Helper Method to get implementation
|
||||
function resolve(string $interface)
|
||||
{
|
||||
try {
|
||||
return BINDINGS[$interface];
|
||||
} catch (Throwable $e) {
|
||||
throw new Exception('Cannot Find Implementation');
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use GildedRose\Core\Container;
|
||||
use GildedRose\GildedRose;
|
||||
use GildedRose\Item;
|
||||
|
||||
@ -22,7 +23,7 @@ $items = [
|
||||
new Item('Conjured Mana Cake', 3, 6),
|
||||
];
|
||||
|
||||
$app = new GildedRose($items);
|
||||
$app = (new Container())->get(GildedRose::class, [$items]);
|
||||
|
||||
$days = 2;
|
||||
if (count($argv) > 1) {
|
||||
|
||||
70
php/src/Core/Container.php
Normal file
70
php/src/Core/Container.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GildedRose\Core;
|
||||
|
||||
use Exception;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionMethod;
|
||||
|
||||
class Container implements ContainerInterface
|
||||
{
|
||||
private $deps = [];
|
||||
|
||||
/**
|
||||
* Get Dependency and Respect Dependency Inversion principle
|
||||
* The Interface/Class Binding is gotten from ioc config file
|
||||
* Where we set bindings between interfaces,custom classes,aliases.. mostly anything can be injected
|
||||
*/
|
||||
public function get(string $name, array $args = null): object
|
||||
{
|
||||
if (! isset($this->deps[$name])) {
|
||||
$this->deps[$name] = $name;
|
||||
}
|
||||
try {
|
||||
$reflection = new ReflectionClass($this->deps[$name]);
|
||||
|
||||
if (! $reflection->isInstantiable()) {
|
||||
if (! $reflection->isInterface()) {
|
||||
throw new Exception("{$reflection->getName()} is not instantiable !");
|
||||
}
|
||||
|
||||
$reflection = new ReflectionClass(resolve($reflection->getName()));
|
||||
}
|
||||
} catch (ReflectionException $e) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
$constructor = $reflection->getConstructor();
|
||||
return $constructor === null ?
|
||||
$reflection->newInstanceArgs() :
|
||||
$reflection->newInstanceArgs($this->resolveArgs($constructor, $args));
|
||||
}
|
||||
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return isset($this->deps[$name]);
|
||||
}
|
||||
|
||||
private function resolveArgs(ReflectionMethod $constructor, $args)
|
||||
{
|
||||
$args = $args ?? [];
|
||||
|
||||
$params = $constructor->getParameters();
|
||||
|
||||
foreach ($params as $param) {
|
||||
if ($param->getType() !== null && ! $param->getType()->isBuiltin()) {
|
||||
if (! array_key_exists($param->getName(), $args)) {
|
||||
$args[] = $this->get(
|
||||
$param->getType()->getName()
|
||||
);
|
||||
}
|
||||
} elseif ($param->isDefaultValueAvailable()) {
|
||||
$args[] = $param->getDefaultValue();
|
||||
}
|
||||
}
|
||||
return array_values($args);
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace GildedRose;
|
||||
|
||||
use GildedRose\Services\AbstractDegradingStrategyFactory;
|
||||
|
||||
final class GildedRose
|
||||
{
|
||||
/**
|
||||
@ -11,59 +13,31 @@ final class GildedRose
|
||||
*/
|
||||
private $items;
|
||||
|
||||
public function __construct(array $items)
|
||||
/**
|
||||
* Degrading Strategy Factory
|
||||
* @var AbstractDegradingStrategyFactory
|
||||
*/
|
||||
private $degradingStrategyFactory;
|
||||
|
||||
public function __construct(array $items, AbstractDegradingStrategyFactory $degradingStrategyFactory)
|
||||
{
|
||||
$this->items = $items;
|
||||
$this->degradingStrategyFactory = $degradingStrategyFactory;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
return property_exists($this, $name) ? $this->{$name} : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Updating quality in cleaner way respecting multiple software engineering principles
|
||||
* Such as Solid/Dry etc
|
||||
*/
|
||||
public function updateQuality(): void
|
||||
{
|
||||
foreach ($this->items as $item) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->degradingStrategyFactory->getDegradingStrategy($item)->handle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
php/src/Services/AbstractDegradingStrategyFactory.php
Normal file
19
php/src/Services/AbstractDegradingStrategyFactory.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace GildedRose\Services;
|
||||
|
||||
use GildedRose\Item;
|
||||
use GildedRose\Services\Strategies\BaseStrategy;
|
||||
|
||||
interface AbstractDegradingStrategyFactory
|
||||
{
|
||||
public const AGED = 'Aged Brie';
|
||||
|
||||
public const SULFURAS = 'Sulfuras, Hand of Ragnaros';
|
||||
|
||||
public const BACKSTAGE = 'Backstage passes to a TAFKAL80ETC concert';
|
||||
|
||||
public const CONJURED = 'Conjured Mana Cake';
|
||||
|
||||
public function getDegradingStrategy(Item $item): BaseStrategy;
|
||||
}
|
||||
56
php/src/Services/DegradingStrategyFactory.php
Normal file
56
php/src/Services/DegradingStrategyFactory.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace GildedRose\Services;
|
||||
|
||||
use GildedRose\Core\Container;
|
||||
use GildedRose\Item;
|
||||
use GildedRose\Services\Strategies\AgedBrieStrategy;
|
||||
use GildedRose\Services\Strategies\BackstageStrategy;
|
||||
use GildedRose\Services\Strategies\BaseStrategy;
|
||||
use GildedRose\Services\Strategies\ConjuredStrategy;
|
||||
use GildedRose\Services\Strategies\DefaultStrategy;
|
||||
use GildedRose\Services\Strategies\SulfurasStrategy;
|
||||
|
||||
/**
|
||||
* DegradingStrategy builder that returns strategy based on item following Factory Design pattern
|
||||
*/
|
||||
class DegradingStrategyFactory implements AbstractDegradingStrategyFactory
|
||||
{
|
||||
private $container;
|
||||
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
public function getDegradingStrategy(Item $item): BaseStrategy
|
||||
{
|
||||
switch ($item->name) {
|
||||
case self::AGED:
|
||||
return $this->container->get(AgedBrieStrategy::class, [
|
||||
'item' => $item,
|
||||
]);
|
||||
break;
|
||||
case self::BACKSTAGE:
|
||||
return $this->container->get(BackstageStrategy::class, [
|
||||
'item' => $item,
|
||||
]);
|
||||
break;
|
||||
case self::SULFURAS:
|
||||
return $this->container->get(SulfurasStrategy::class, [
|
||||
'item' => $item,
|
||||
]);
|
||||
break;
|
||||
case self::CONJURED:
|
||||
return $this->container->get(ConjuredStrategy::class, [
|
||||
'item' => $item,
|
||||
]);
|
||||
break;
|
||||
|
||||
default:
|
||||
return $this->container->get(DefaultStrategy::class, [
|
||||
'item' => $item,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
php/src/Services/Strategies/AgedBrieStrategy.php
Normal file
21
php/src/Services/Strategies/AgedBrieStrategy.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace GildedRose\Services\Strategies;
|
||||
|
||||
use GildedRose\Item;
|
||||
|
||||
class AgedBrieStrategy extends DefaultStrategy
|
||||
{
|
||||
public function setSellIn(): Item
|
||||
{
|
||||
$this->item->sell_in--;
|
||||
return $this->item;
|
||||
}
|
||||
|
||||
public function setQuality(): Item
|
||||
{
|
||||
$this->item->quality = ($this->item->quality + $this->degradeRate) > self::MAX_QUALITY_VALUE ?
|
||||
self::MAX_QUALITY_VALUE : $this->item->quality += $this->degradeRate;
|
||||
return $this->item;
|
||||
}
|
||||
}
|
||||
32
php/src/Services/Strategies/BackstageStrategy.php
Normal file
32
php/src/Services/Strategies/BackstageStrategy.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace GildedRose\Services\Strategies;
|
||||
|
||||
use GildedRose\Item;
|
||||
|
||||
class BackstageStrategy extends DefaultStrategy
|
||||
{
|
||||
public function setDegradeRate()
|
||||
{
|
||||
if ($this->item->sell_in <= 0) {
|
||||
$this->degradeRate = -$this->item->quality;
|
||||
} elseif ($this->item->sell_in <= 5) {
|
||||
$this->degradeRate = 3;
|
||||
} elseif ($this->item->sell_in <= 10) {
|
||||
$this->degradeRate = 2;
|
||||
}
|
||||
}
|
||||
|
||||
public function setSellIn(): Item
|
||||
{
|
||||
$this->item->sell_in--;
|
||||
return $this->item;
|
||||
}
|
||||
|
||||
public function setQuality(): Item
|
||||
{
|
||||
$this->item->quality = ($this->item->quality + $this->degradeRate) > self::MAX_QUALITY_VALUE ?
|
||||
self::MAX_QUALITY_VALUE : $this->item->quality += $this->degradeRate;
|
||||
return $this->item;
|
||||
}
|
||||
}
|
||||
20
php/src/Services/Strategies/BaseStrategy.php
Normal file
20
php/src/Services/Strategies/BaseStrategy.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace GildedRose\Services\Strategies;
|
||||
|
||||
use GildedRose\Item;
|
||||
|
||||
interface BaseStrategy
|
||||
{
|
||||
public const MAX_QUALITY_VALUE = 50;
|
||||
|
||||
public const MIN_QUALITY_VALUE = 0;
|
||||
|
||||
public function __construct(Item $item);
|
||||
|
||||
public function setSellIn(): Item;
|
||||
|
||||
public function setQuality(): Item;
|
||||
|
||||
public function handle();
|
||||
}
|
||||
21
php/src/Services/Strategies/ConjuredStrategy.php
Normal file
21
php/src/Services/Strategies/ConjuredStrategy.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace GildedRose\Services\Strategies;
|
||||
|
||||
use GildedRose\Item;
|
||||
|
||||
class ConjuredStrategy extends DefaultStrategy
|
||||
{
|
||||
public function setSellIn(): Item
|
||||
{
|
||||
$this->item->sell_in--;
|
||||
return $this->item;
|
||||
}
|
||||
|
||||
public function setQuality(): Item
|
||||
{
|
||||
$this->item->quality = ($this->item->quality - ($this->degradeRate * 2)) < self::MIN_QUALITY_VALUE ?
|
||||
self::MIN_QUALITY_VALUE : $this->item->quality -= ($this->degradeRate * 2);
|
||||
return $this->item;
|
||||
}
|
||||
}
|
||||
55
php/src/Services/Strategies/DefaultStrategy.php
Normal file
55
php/src/Services/Strategies/DefaultStrategy.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace GildedRose\Services\Strategies;
|
||||
|
||||
use GildedRose\Item;
|
||||
|
||||
/**
|
||||
* Behaves as a default Strategy and template for other strategies
|
||||
* based on Template Method Design pattern and factory method design pattern for setDegrateRate method
|
||||
* in a way to share common code and
|
||||
*/
|
||||
class DefaultStrategy implements BaseStrategy
|
||||
{
|
||||
protected $degradeRate = 1;
|
||||
|
||||
protected $item;
|
||||
|
||||
public function __construct(Item $item)
|
||||
{
|
||||
$this->item = $item;
|
||||
}
|
||||
|
||||
public function setDegradeRate()
|
||||
{
|
||||
if ($this->item->sell_in < 0) {
|
||||
$this->degradeRate *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
public function setSellIn(): Item
|
||||
{
|
||||
$this->item->sell_in--;
|
||||
return $this->item;
|
||||
}
|
||||
|
||||
public function setQuality(): Item
|
||||
{
|
||||
$this->item->quality = ($this->item->quality - $this->degradeRate) < self::MIN_QUALITY_VALUE ?
|
||||
self::MIN_QUALITY_VALUE : $this->item->quality -= $this->degradeRate;
|
||||
return $this->item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template Method that runs steps for all strategies in order
|
||||
* First we Downgrade the sellIn
|
||||
* Second We set the degradeRate/riseRate
|
||||
* Finally we Set the item quality based on degrate/rise Rate
|
||||
*/
|
||||
final public function handle()
|
||||
{
|
||||
$this->setSellIn();
|
||||
$this->setDegradeRate();
|
||||
$this->setQuality();
|
||||
}
|
||||
}
|
||||
18
php/src/Services/Strategies/SulfurasStrategy.php
Normal file
18
php/src/Services/Strategies/SulfurasStrategy.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace GildedRose\Services\Strategies;
|
||||
|
||||
use GildedRose\Item;
|
||||
|
||||
class SulfurasStrategy extends DefaultStrategy
|
||||
{
|
||||
public function setSellIn(): Item
|
||||
{
|
||||
return $this->item;
|
||||
}
|
||||
|
||||
public function setQuality(): Item
|
||||
{
|
||||
return $this->item;
|
||||
}
|
||||
}
|
||||
45
php/tests/DegradingStrategyFactoryTest.php
Normal file
45
php/tests/DegradingStrategyFactoryTest.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use GildedRose\Item;
|
||||
use GildedRose\Services\AbstractDegradingStrategyFactory;
|
||||
use GildedRose\Services\Strategies\ConjuredStrategy;
|
||||
use GildedRose\Services\Strategies\DefaultStrategy;
|
||||
use GildedRose\Services\Strategies\SulfurasStrategy;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DegradingStrategyFactoryTest extends TestCase
|
||||
{
|
||||
use InitApp;
|
||||
|
||||
public $degradingStrategyFactory;
|
||||
|
||||
public $container;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->init();
|
||||
$this->degradingStrategyFactory = $this->container->get(AbstractDegradingStrategyFactory::class);
|
||||
}
|
||||
|
||||
public function testStrategyFactoryIsLoaded(): void
|
||||
{
|
||||
$this->assertNotNull($this->degradingStrategyFactory);
|
||||
$this->assertInstanceOf(AbstractDegradingStrategyFactory::class, $this->degradingStrategyFactory);
|
||||
}
|
||||
|
||||
public function testLoadsTheCorrectStrategy()
|
||||
{
|
||||
$item = $this->container->get(Item::class, ['Foo', 2, 3]);
|
||||
$this->assertInstanceOf(DefaultStrategy::class, $this->degradingStrategyFactory->getDegradingStrategy($item));
|
||||
|
||||
$item = $this->container->get(Item::class, ['Sulfuras, Hand of Ragnaros', 0, 80]);
|
||||
$this->assertInstanceOf(SulfurasStrategy::class, $this->degradingStrategyFactory->getDegradingStrategy($item));
|
||||
|
||||
$item = $this->container->get(Item::class, ['Conjured Mana Cake', 3, 6]);
|
||||
$this->assertInstanceOf(ConjuredStrategy::class, $this->degradingStrategyFactory->getDegradingStrategy($item));
|
||||
}
|
||||
}
|
||||
119
php/tests/DegradingStrategyTest.php
Normal file
119
php/tests/DegradingStrategyTest.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use GildedRose\Item;
|
||||
use GildedRose\Services\AbstractDegradingStrategyFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DegradingStrategyTest extends TestCase
|
||||
{
|
||||
use InitApp;
|
||||
|
||||
public $degradingStrategyFactory;
|
||||
|
||||
public $container;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->init();
|
||||
$this->degradingStrategyFactory = $this->container->get(AbstractDegradingStrategyFactory::class);
|
||||
}
|
||||
|
||||
public function testDefaultStrategy(): void
|
||||
{
|
||||
$item = $this->container->get(Item::class, ['Foo', 2, 3]);
|
||||
$this->getStrategy($item)->handle();
|
||||
$this->assertSame(2, $item->quality);
|
||||
$this->assertSame(1, $item->sell_in);
|
||||
$item = $this->container->get(Item::class, ['Foo', -1, 3]);
|
||||
$this->getStrategy($item)->handle();
|
||||
$this->assertSame(1, $item->quality);
|
||||
$this->assertSame(-2, $item->sell_in);
|
||||
}
|
||||
|
||||
public function testAgedBrieStrategy(): void
|
||||
{
|
||||
$item = $this->container->get(Item::class, ['Aged Brie', 2, 30]);
|
||||
$this->getStrategy($item)->handle();
|
||||
$this->assertSame(31, $item->quality);
|
||||
$this->assertSame(1, $item->sell_in);
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$this->getStrategy($item)->handle();
|
||||
}
|
||||
|
||||
$this->assertSame(50, $item->quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* "Backstage passes", like aged brie, increases in Quality as its SellIn value approaches;
|
||||
* Quality increases by 2 when there are 10 days or less and by 3 when there are 5 days or less but
|
||||
* Quality drops to 0 after the concert
|
||||
* Yet the results are not correct under approvals/ , as the sell_in reaches 0 the quality stand still
|
||||
* and only drop to 0 when sell_in < 0
|
||||
* As same for increasing by 2 when sell_in reaches 10, it only increases by 2 when it's < 10
|
||||
* As same also for sell_in reaching 5 it does not increase by 3 only after sell_in is lower than 5
|
||||
*/
|
||||
public function testBackstageStrategy(): void
|
||||
{
|
||||
$item = $this->container->get(Item::class, ['Backstage passes to a TAFKAL80ETC concert', 15, 20]);
|
||||
$this->getStrategy($item)->handle();
|
||||
$this->assertSame(21, $item->quality);
|
||||
$this->assertSame(14, $item->sell_in);
|
||||
$item->sell_in = 10;
|
||||
$this->getStrategy($item)->handle();
|
||||
$this->assertSame(23, $item->quality);
|
||||
|
||||
$item->sell_in = 5;
|
||||
$this->getStrategy($item)->handle();
|
||||
$this->assertSame(26, $item->quality);
|
||||
|
||||
$item->sell_in = 0;
|
||||
$this->getStrategy($item)->handle();
|
||||
$this->assertSame(0, $item->quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* "Conjured" items degrade in Quality twice as fast as normal items
|
||||
* though the results under approvals/ are not correct since it doesn't
|
||||
* degrade as mentioned in requirements, it degrades same as AgedBrie
|
||||
*/
|
||||
public function testConjuredStrategy(): void
|
||||
{
|
||||
$item = $this->container->get(Item::class, ['Conjured Mana Cake', 3, 8]);
|
||||
$this->getStrategy($item)->handle();
|
||||
$this->assertSame(6, $item->quality);
|
||||
$this->assertSame(2, $item->sell_in);
|
||||
$item->sell_in = 1;
|
||||
$this->getStrategy($item)->handle();
|
||||
$this->assertSame(4, $item->quality);
|
||||
$item->sell_in = 0;
|
||||
$this->getStrategy($item)->handle();
|
||||
$this->assertSame(0, $item->quality);
|
||||
}
|
||||
|
||||
public function testSulfurasStrategy(): void
|
||||
{
|
||||
$item = $this->container->get(Item::class, ['Sulfuras, Hand of Ragnaros', 5, 80]);
|
||||
$this->getStrategy($item)->handle();
|
||||
$this->assertSame(80, $item->quality);
|
||||
$this->assertSame(5, $item->sell_in);
|
||||
|
||||
$item->sell_in = 1;
|
||||
$this->getStrategy($item)->handle();
|
||||
$this->assertSame(80, $item->quality);
|
||||
$this->assertSame(1, $item->sell_in);
|
||||
|
||||
$item->sell_in = 0;
|
||||
$this->getStrategy($item)->handle();
|
||||
$this->assertSame(80, $item->quality);
|
||||
$this->assertSame(0, $item->sell_in);
|
||||
}
|
||||
|
||||
private function getStrategy($item)
|
||||
{
|
||||
return $this->degradingStrategyFactory->getDegradingStrategy($item);
|
||||
}
|
||||
}
|
||||
@ -6,15 +6,43 @@ namespace Tests;
|
||||
|
||||
use GildedRose\GildedRose;
|
||||
use GildedRose\Item;
|
||||
use GildedRose\Services\AbstractDegradingStrategyFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class GildedRoseTest extends TestCase
|
||||
{
|
||||
public function testFoo(): void
|
||||
use InitApp;
|
||||
|
||||
private $items;
|
||||
|
||||
private $app;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$items = [new Item('foo', 0, 0)];
|
||||
$gildedRose = new GildedRose($items);
|
||||
$gildedRose->updateQuality();
|
||||
$this->assertSame('fixme', $items[0]->name);
|
||||
$this->init();
|
||||
$this->items = [$this->container->get(Item::class, ['Aged Brie', 2, 0])];
|
||||
|
||||
$this->bootstrap();
|
||||
}
|
||||
|
||||
public function testApplicationIsInstanciatedCorrectly(): void
|
||||
{
|
||||
$this->assertNotNull($this->app);
|
||||
$this->assertInstanceOf(GildedRose::class, $this->app);
|
||||
$this->assertInstanceOf(AbstractDegradingStrategyFactory::class, $this->app->degradingStrategyFactory);
|
||||
}
|
||||
|
||||
public function testItemsAreSetAndCorrect()
|
||||
{
|
||||
$this->assertIsArray($this->items);
|
||||
$this->assertIsArray($this->app->items);
|
||||
$this->assertContainsOnlyInstancesOf(Item::class, $this->app->items);
|
||||
$this->assertSame(new Item('Aged Brie', 2, 0), array_pop($this->items));
|
||||
}
|
||||
|
||||
public function testUpdateQuality()
|
||||
{
|
||||
$this->app->updateQuality();
|
||||
$this->assertSame(new Item('Aged Brie', 1, 1), $this->app->items[0]);
|
||||
}
|
||||
}
|
||||
|
||||
24
php/tests/InitApp.php
Normal file
24
php/tests/InitApp.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use GildedRose\Core\Container;
|
||||
use GildedRose\GildedRose;
|
||||
|
||||
/**
|
||||
* Small Trait to speed up bootstrapping process and avoid Dry code
|
||||
*/
|
||||
trait InitApp
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->container = new Container();
|
||||
}
|
||||
|
||||
public function bootstrap()
|
||||
{
|
||||
$this->app = $this->container->get(GildedRose::class, [$this->items]);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user