mirror of
https://github.com/emilybache/GildedRose-Refactoring-Kata.git
synced 2026-02-14 22:21:20 +00:00
Add ItemBuilders hierarchy to enforce building valid items
Also updated unit tests: - Added unit tests for the new ItemBuilders classes - Refactored existing tests to use the new builders
This commit is contained in:
parent
eb741a95c5
commit
d076fc9ba4
13
csharpcore/GildedRose/AbstractItemBuilder.cs
Normal file
13
csharpcore/GildedRose/AbstractItemBuilder.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace GildedRoseKata;
|
||||
|
||||
public abstract class AbstractItemBuilder(string name, int sellIn, int quality)
|
||||
{
|
||||
protected readonly string Name = name;
|
||||
protected readonly int SellIn = sellIn;
|
||||
protected readonly int Quality = quality;
|
||||
|
||||
public virtual Item Build()
|
||||
{
|
||||
return new() { Name = Name, SellIn = SellIn, Quality = Quality };
|
||||
}
|
||||
}
|
||||
23
csharpcore/GildedRose/ItemBuilder.cs
Normal file
23
csharpcore/GildedRose/ItemBuilder.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace GildedRoseKata;
|
||||
|
||||
public class ItemBuilder(string name, int sellIn, int quality) : AbstractItemBuilder(name, sellIn, quality)
|
||||
{
|
||||
public override Item Build()
|
||||
{
|
||||
if (Quality < ItemQuality.MinQuality || Quality > ItemQuality.MaxQuality)
|
||||
{
|
||||
throw new ArgumentException("An item's quality must be >=0 and <=50");
|
||||
}
|
||||
|
||||
if (ItemType.IsLegendaryItem(Name))
|
||||
{
|
||||
throw new ArgumentException("Legendary Items cannot be constructed using this builder");
|
||||
}
|
||||
|
||||
return base.Build();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
17
csharpcore/GildedRose/LegendaryItemBuilder.cs
Normal file
17
csharpcore/GildedRose/LegendaryItemBuilder.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace GildedRoseKata;
|
||||
|
||||
public class LegendaryItemBuilder(string name, int sellIn)
|
||||
: AbstractItemBuilder(name, sellIn, ItemQuality.LegendaryItemQuality)
|
||||
{
|
||||
public override Item Build()
|
||||
{
|
||||
if (!ItemType.IsLegendaryItem(Name))
|
||||
{
|
||||
throw new ArgumentException("Only Legendary items can be built using this builder");
|
||||
}
|
||||
|
||||
return base.Build();
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,8 @@ However, I've chosen to use the factory itself to manually manage it using a sem
|
||||
11. Moved ItemType and ItemQuality to their own classes to allow re-use
|
||||
12. Seperated the derived DailyUpdater classes into their own files.<br>
|
||||
Now, adding a new updater won't have to change the existing DailyUpdater file.
|
||||
13. Added a new ItemBuilders hierarchy to enforce building valid items (quality limits and special rules for Legendary items). Also added unit tests and refactored the existing tests to use the new builders.<br>
|
||||
note: if we could change Item, this could have been implemented in the Item's constructor itself and by using Quality TinyType. However, since we can't change Item, the builders gives us a nice decoupled way to validate the items are constructed consistently.
|
||||
|
||||
|
||||
|
||||
|
||||
40
csharpcore/GildedRoseTests/ItemBuilderTestFixture.cs
Normal file
40
csharpcore/GildedRoseTests/ItemBuilderTestFixture.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using GildedRoseKata;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GildedRoseTests;
|
||||
|
||||
public class ItemBuilderTestFixture
|
||||
{
|
||||
[TestCase("Aged Brie", 5, 0)]
|
||||
[TestCase("Elixir of the Mongoose", 0, 5)]
|
||||
[TestCase("Backstage passes to a TAFKAL80ETC concert", -5, 39)]
|
||||
public void WhenGivenValidParameters_ShouldBuildExpectedItemSuccessfully(string name, int sellIn,
|
||||
int quality)
|
||||
{
|
||||
var item = new ItemBuilder(name, sellIn, quality).Build();
|
||||
item.Name.Should().Be(name);
|
||||
item.SellIn.Should().Be(sellIn);
|
||||
item.Quality.Should().Be(quality);
|
||||
}
|
||||
|
||||
|
||||
[TestCase("Aged Brie", 5, -1)]
|
||||
[TestCase("Elixir of the Mongoose", 0, 51)]
|
||||
public void WhenGivenInvalidQuality_ShouldThrow(string name, int sellIn, int quality)
|
||||
{
|
||||
var action = () => new ItemBuilder(name, sellIn, quality).Build();
|
||||
action.Should().Throw<Exception>().WithMessage("An item's quality must be >=0 and <=50");
|
||||
}
|
||||
|
||||
|
||||
[TestCase("Sulfuras, Hand of Ragnaros", 3, 3)]
|
||||
[TestCase("something sulfuras", 3, 3)]
|
||||
public void WhenGivenLegendaryItem_ShouldThrow(string name, int sellIn, int quality)
|
||||
{
|
||||
var action = () => new ItemBuilder(name, sellIn, quality).Build();
|
||||
action.Should().Throw<Exception>().WithMessage("Legendary Items cannot be constructed using this builder");
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using GildedRoseKata;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GildedRoseTests;
|
||||
|
||||
public class LegendaryItemBuilderTestFixture
|
||||
{
|
||||
|
||||
[TestCase("Aged Brie", 3)]
|
||||
[TestCase("Elixir of the Mongoose", 0)]
|
||||
[TestCase("Backstage passes to a TAFKAL80ETC concert", -3)]
|
||||
public void WhenGivenOtherItemTypes_ShouldThrow(string name, int sellIn)
|
||||
{
|
||||
var action = () => new LegendaryItemBuilder(name, sellIn).Build();
|
||||
action.Should().Throw<Exception>().WithMessage("Only Legendary items can be built using this builder");
|
||||
}
|
||||
|
||||
|
||||
[TestCase("Sulfuras, Hand of Ragnaros", 3)]
|
||||
[TestCase("Sulfuras, Hand of Ragnaros", 0)]
|
||||
[TestCase("something sulfuras", -3)]
|
||||
public void WhenGivenValidParameters_ShouldBuildExpectedItemSuccessfully(string name, int sellIn)
|
||||
{
|
||||
var item = new LegendaryItemBuilder(name, sellIn).Build();
|
||||
item.Name.Should().Be(name);
|
||||
item.SellIn.Should().Be(sellIn);
|
||||
item.Quality.Should().Be(ItemQuality.LegendaryItemQuality);
|
||||
}
|
||||
|
||||
}
|
||||
@ -11,31 +11,25 @@ public static class TextTestFixture
|
||||
Console.WriteLine("OMGHAI!");
|
||||
|
||||
var items = new List<Item>{
|
||||
new Item {Name = "+5 Dexterity Vest", SellIn = 10, Quality = 20},
|
||||
new Item {Name = "Aged Brie", SellIn = 2, Quality = 0},
|
||||
new Item {Name = "Elixir of the Mongoose", SellIn = 5, Quality = 7},
|
||||
new Item {Name = "Sulfuras, Hand of Ragnaros", SellIn = 0, Quality = 80},
|
||||
new Item {Name = "Sulfuras, Hand of Ragnaros", SellIn = -1, Quality = 80},
|
||||
new Item
|
||||
{
|
||||
Name = "Backstage passes to a TAFKAL80ETC concert",
|
||||
SellIn = 15,
|
||||
Quality = 20
|
||||
},
|
||||
new Item
|
||||
{
|
||||
Name = "Backstage passes to a TAFKAL80ETC concert",
|
||||
SellIn = 10,
|
||||
Quality = 49
|
||||
},
|
||||
new Item
|
||||
{
|
||||
Name = "Backstage passes to a TAFKAL80ETC concert",
|
||||
SellIn = 5,
|
||||
Quality = 49
|
||||
},
|
||||
new ItemBuilder("+5 Dexterity Vest", 10,20).Build(),
|
||||
new ItemBuilder("Aged Brie", 2, 0).Build(),
|
||||
new ItemBuilder("Elixir of the Mongoose", 5, 7).Build(),
|
||||
new LegendaryItemBuilder("Sulfuras, Hand of Ragnaros", 0).Build(),
|
||||
new LegendaryItemBuilder("Sulfuras, Hand of Ragnaros", -1).Build(),
|
||||
new ItemBuilder("Backstage passes to a TAFKAL80ETC concert",
|
||||
15,
|
||||
20
|
||||
).Build(),
|
||||
new ItemBuilder("Backstage passes to a TAFKAL80ETC concert",
|
||||
10,
|
||||
49
|
||||
).Build(),
|
||||
new ItemBuilder("Backstage passes to a TAFKAL80ETC concert",
|
||||
5,
|
||||
49
|
||||
).Build(),
|
||||
// this conjured item does not work properly yet
|
||||
new Item {Name = "Conjured Mana Cake", SellIn = 3, Quality = 6}
|
||||
new ItemBuilder("Conjured Mana Cake", 3, 6).Build()
|
||||
};
|
||||
|
||||
var app = new GildedRose(items);
|
||||
|
||||
@ -11,8 +11,8 @@ public class UpdateQualityTestFixture
|
||||
[Test]
|
||||
public void PlainItems_WhenNotExpired_Should_DecreaseSellInAndQualityByOne()
|
||||
{
|
||||
var items = new List<Item> { new() { Name = "item 1", SellIn = 2, Quality = 3 } };
|
||||
var expectedItemsAfterTest = new List<Item> { new() { Name = "item 1", SellIn = 1, Quality = 2 } };
|
||||
var items = new List<Item> { new ItemBuilder("item 1", 2, 3).Build() };
|
||||
var expectedItemsAfterTest = new List<Item> { new ItemBuilder("item 1", 1, 2).Build() };
|
||||
|
||||
var app = new GildedRose(items);
|
||||
app.UpdateQuality();
|
||||
@ -24,9 +24,9 @@ public class UpdateQualityTestFixture
|
||||
[Test]
|
||||
public void PlainItems_WhenExpired_Should_DecreaseSellInByOneAndQualityByTwo()
|
||||
{
|
||||
var items = new List<Item> { new() { Name = "item 1", SellIn = 0, Quality = 5 } };
|
||||
var expectedItemsAfterDay1 = new List<Item> { new() { Name = "item 1", SellIn = -1, Quality = 3 } };
|
||||
var expectedItemsAfterDay2 = new List<Item> { new() { Name = "item 1", SellIn = -2, Quality = 1 } };
|
||||
var items = new List<Item> { new ItemBuilder("item 1", 0, 5).Build() };
|
||||
var expectedItemsAfterDay1 = new List<Item> { new ItemBuilder("item 1", -1, 3).Build() };
|
||||
var expectedItemsAfterDay2 = new List<Item> { new ItemBuilder("item 1", -2, 1).Build() };
|
||||
|
||||
|
||||
var app = new GildedRose(items);
|
||||
@ -43,13 +43,13 @@ public class UpdateQualityTestFixture
|
||||
{
|
||||
var items = new List<Item>
|
||||
{
|
||||
new() { Name = "item 1", SellIn = 0, Quality = 0 },
|
||||
new() { Name = "item 1", SellIn = 0, Quality = 1 }
|
||||
new ItemBuilder("item 1", 0, 0).Build(),
|
||||
new ItemBuilder("item 1", 0, 1).Build()
|
||||
};
|
||||
var expectedItemsAfterTest = new List<Item>
|
||||
{
|
||||
new() { Name = "item 1", SellIn = -1, Quality = 0 },
|
||||
new() { Name = "item 1", SellIn = -1, Quality = 0 }
|
||||
new ItemBuilder("item 1", -1, 0).Build(),
|
||||
new ItemBuilder("item 1", -1, 0).Build()
|
||||
};
|
||||
|
||||
|
||||
@ -63,8 +63,8 @@ public class UpdateQualityTestFixture
|
||||
[Test]
|
||||
public void BetterWithAgeItems_WhenNotExpired_Should_IncreaseQualityByOne()
|
||||
{
|
||||
var items = new List<Item> { new() { Name = "Aged Brie", SellIn = 1, Quality = 0 } };
|
||||
var expectedItemsAfterTest = new List<Item> { new() { Name = "Aged Brie", SellIn = 0, Quality = 1 } };
|
||||
var items = new List<Item> { new ItemBuilder("Aged Brie", 1, 0).Build() };
|
||||
var expectedItemsAfterTest = new List<Item> { new ItemBuilder("Aged Brie", 0, 1).Build() };
|
||||
|
||||
var app = new GildedRose(items);
|
||||
app.UpdateQuality();
|
||||
@ -75,8 +75,8 @@ public class UpdateQualityTestFixture
|
||||
[Test]
|
||||
public void BetterWithAgeItems_WhenExpired_Should_IncreaseQualityByTwo()
|
||||
{
|
||||
var items = new List<Item> { new() { Name = "aged Brie", SellIn = 0, Quality = 1 } };
|
||||
var expectedItemsAfterTest = new List<Item> { new() { Name = "aged Brie", SellIn = -1, Quality = 3 } };
|
||||
var items = new List<Item> { new ItemBuilder("aged Brie", 0, 1).Build() };
|
||||
var expectedItemsAfterTest = new List<Item> { new ItemBuilder("aged Brie", -1, 3).Build() };
|
||||
|
||||
var app = new GildedRose(items);
|
||||
app.UpdateQuality();
|
||||
@ -88,9 +88,9 @@ public class UpdateQualityTestFixture
|
||||
[Test]
|
||||
public void ItemsQuality_Should_NeverIncreaseAbove50()
|
||||
{
|
||||
var items = new List<Item> { new() { Name = "Aged Brie", SellIn = 1, Quality = 49 } };
|
||||
var expectedItemsAfterDay1 = new List<Item> { new() { Name = "Aged Brie", SellIn = 0, Quality = 50 } };
|
||||
var expectedItemsAfterDay2 = new List<Item> { new() { Name = "Aged Brie", SellIn = -1, Quality = 50 } };
|
||||
var items = new List<Item> { new ItemBuilder("Aged Brie", 1, 49).Build() };
|
||||
var expectedItemsAfterDay1 = new List<Item> { new ItemBuilder("Aged Brie", 0, 50).Build() };
|
||||
var expectedItemsAfterDay2 = new List<Item> { new ItemBuilder("Aged Brie", -1, 50).Build() };
|
||||
|
||||
var app = new GildedRose(items);
|
||||
|
||||
@ -107,13 +107,13 @@ public class UpdateQualityTestFixture
|
||||
{
|
||||
var items = new List<Item>
|
||||
{
|
||||
new() { Name = "Sulfuras, Hand of Ragnaros", SellIn = 3, Quality = 3 },
|
||||
new() { Name = "sulfuras", SellIn = 3, Quality = 31 }
|
||||
new LegendaryItemBuilder("Sulfuras, Hand of Ragnaros", 3).Build(),
|
||||
new LegendaryItemBuilder("sulfuras", 3).Build()
|
||||
};
|
||||
var expectedItemsAfterTest = new List<Item>
|
||||
{
|
||||
new() { Name = "Sulfuras, Hand of Ragnaros", SellIn = 3, Quality = 3 },
|
||||
new() { Name = "sulfuras", SellIn = 3, Quality = 31 }
|
||||
new LegendaryItemBuilder("Sulfuras, Hand of Ragnaros", 3).Build(),
|
||||
new LegendaryItemBuilder("sulfuras", 3).Build()
|
||||
};
|
||||
|
||||
var app = new GildedRose(items);
|
||||
@ -123,11 +123,10 @@ public class UpdateQualityTestFixture
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("not implemented yet - Legendary Items should not take quality (constant at 80)")]
|
||||
public void LegendaryItems_Quality_IsConstant80()
|
||||
{
|
||||
var items = new List<Item> { new() { Name = "something sulfuras something", SellIn = 3 } };
|
||||
var expectedItemsAfterTest = new List<Item> { new() { Name = "something sulfuras something", SellIn = 3, Quality = 80 } };
|
||||
var items = new List<Item> { new LegendaryItemBuilder("something sulfuras something", 3).Build() };
|
||||
var expectedItemsAfterTest = new List<Item> { new LegendaryItemBuilder("something sulfuras something", 3).Build() };
|
||||
|
||||
var app = new GildedRose(items);
|
||||
|
||||
@ -141,13 +140,13 @@ public class UpdateQualityTestFixture
|
||||
{
|
||||
var items = new List<Item>
|
||||
{
|
||||
new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 11, Quality = 4 },
|
||||
new() { Name = "backstage passes to some other show", SellIn = 20, Quality = 40 }
|
||||
new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 11, 4).Build(),
|
||||
new ItemBuilder("backstage passes to some other show", 20, 40).Build()
|
||||
};
|
||||
var expectedItemsAfterTest = new List<Item>
|
||||
{
|
||||
new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 10, Quality = 5 },
|
||||
new() { Name = "backstage passes to some other show", SellIn = 19, Quality = 41 }
|
||||
new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 10, 5).Build(),
|
||||
new ItemBuilder("backstage passes to some other show", 19, 41).Build()
|
||||
};
|
||||
|
||||
var app = new GildedRose(items);
|
||||
@ -162,13 +161,13 @@ public class UpdateQualityTestFixture
|
||||
{
|
||||
var items = new List<Item>
|
||||
{
|
||||
new() { Name = "backstage passes to a TAFKAL80ETC concert", SellIn = 10, Quality = 4 },
|
||||
new() { Name = "Backstage passes to some other show", SellIn = 6, Quality = 41 }
|
||||
new ItemBuilder("backstage passes to a TAFKAL80ETC concert", 10, 4).Build(),
|
||||
new ItemBuilder("Backstage passes to some other show", 6, 41).Build()
|
||||
};
|
||||
var expectedItemsAfterTest = new List<Item>
|
||||
{
|
||||
new() { Name = "backstage passes to a TAFKAL80ETC concert", SellIn = 9, Quality = 6 },
|
||||
new() { Name = "Backstage passes to some other show", SellIn = 5, Quality = 43 }
|
||||
new ItemBuilder("backstage passes to a TAFKAL80ETC concert", 9, 6).Build(),
|
||||
new ItemBuilder("Backstage passes to some other show", 5, 43).Build()
|
||||
};
|
||||
|
||||
var app = new GildedRose(items);
|
||||
@ -182,13 +181,13 @@ public class UpdateQualityTestFixture
|
||||
{
|
||||
var items = new List<Item>
|
||||
{
|
||||
new() { Name = "Backstage passes to some other show", SellIn = 5, Quality = 4 },
|
||||
new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 1, Quality = 41 }
|
||||
new ItemBuilder("Backstage passes to some other show", 5, 4).Build(),
|
||||
new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 1, 41).Build()
|
||||
};
|
||||
var expectedItemsAfterTest = new List<Item>
|
||||
{
|
||||
new() { Name = "Backstage passes to some other show", SellIn = 4, Quality = 7 },
|
||||
new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 0, Quality = 44 }
|
||||
new ItemBuilder("Backstage passes to some other show", 4, 7).Build(),
|
||||
new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 0, 44).Build()
|
||||
};
|
||||
|
||||
var app = new GildedRose(items);
|
||||
@ -202,21 +201,21 @@ public class UpdateQualityTestFixture
|
||||
{
|
||||
var items = new List<Item>
|
||||
{
|
||||
new() { Name = "Backstage passes", SellIn = 15, Quality = 49 },
|
||||
new() { Name = "Backstage passes to some other show", SellIn = 10, Quality = 49 },
|
||||
new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 5, Quality = 48 }
|
||||
new ItemBuilder("Backstage passes", 15, 49).Build(),
|
||||
new ItemBuilder("Backstage passes to some other show", 10,49).Build(),
|
||||
new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 5, 48).Build()
|
||||
};
|
||||
var expectedItemsAfterDay1 = new List<Item>
|
||||
{
|
||||
new() { Name = "Backstage passes", SellIn = 14, Quality = 50 },
|
||||
new() { Name = "Backstage passes to some other show", SellIn = 9, Quality = 50 },
|
||||
new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 4, Quality = 50 }
|
||||
new ItemBuilder("Backstage passes", 14, 50).Build(),
|
||||
new ItemBuilder("Backstage passes to some other show", 9, 50).Build(),
|
||||
new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 4, 50).Build()
|
||||
};
|
||||
var expectedItemsAfterDay2 = new List<Item>
|
||||
{
|
||||
new() { Name = "Backstage passes", SellIn = 13, Quality = 50 },
|
||||
new() { Name = "Backstage passes to some other show", SellIn = 8, Quality = 50 },
|
||||
new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 3, Quality = 50 }
|
||||
new ItemBuilder("Backstage passes", 13, 50).Build(),
|
||||
new ItemBuilder("Backstage passes to some other show", 8, 50).Build(),
|
||||
new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 3, 50).Build()
|
||||
};
|
||||
|
||||
var app = new GildedRose(items);
|
||||
@ -233,13 +232,13 @@ public class UpdateQualityTestFixture
|
||||
{
|
||||
var items = new List<Item>
|
||||
{
|
||||
new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 0, Quality = 4 },
|
||||
new() { Name = "Another type of backstage passes", SellIn = 0, Quality = 4 }
|
||||
new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 0, 4).Build(),
|
||||
new ItemBuilder("Another type of backstage passes", 0, 4).Build()
|
||||
};
|
||||
var expectedItemsAfterTest = new List<Item>
|
||||
{
|
||||
new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = -1, Quality = 0 },
|
||||
new() { Name = "Another type of backstage passes", SellIn = -1, Quality = 0 }
|
||||
new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", -1, 0).Build(),
|
||||
new ItemBuilder("Another type of backstage passes", -1, 0).Build()
|
||||
};
|
||||
|
||||
var app = new GildedRose(items);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user