Refactor tests

- moved snapshot tests to better named folder
- refactored gilded-rose tests to unit tests in their right location.
- mock implementation details in gilded rose test
- wrote unit tests for behavior resolver
- fix testing config to account for the changes
This commit is contained in:
Arno Chauveau 2025-07-24 12:56:19 +02:00
parent 372111147f
commit cc8a2b35b0
9 changed files with 219 additions and 162 deletions

View File

@ -0,0 +1,40 @@
const getUpdateBehaviorMock = jest.fn((item: Item) => new MockBehavior(item));
const updateMock = jest.fn((item: Item) => item);
jest.mock("@app/update-behaviors", () => ({
getUpdateBehaviorFor: getUpdateBehaviorMock,
}));
import { GildedRose } from "@app/gilded-rose";
import { Item } from "@app/item";
import { IUpdateBehavior } from "@app/update-behaviors";
export class MockBehavior implements IUpdateBehavior {
constructor(public item: Item) {}
update() {
return updateMock(this.item);
}
}
describe("Gilded Rose", () => {
it("should have an empty array as items when no constructor parameter is provided", () => {
const gildedRose = new GildedRose();
expect(gildedRose.items).toEqual([]);
});
it("should call the behavior resolver and update function for each item", () => {
const item1 = new Item("item 1", 0, 0);
const item2 = new Item("item 2", 0, 0);
const gildedRose = new GildedRose([item1, item2]);
gildedRose.updateQuality();
expect(getUpdateBehaviorMock).toHaveBeenCalledWith(item1);
expect(getUpdateBehaviorMock).toHaveBeenCalledWith(item2);
expect(updateMock).toHaveBeenCalledWith(item1);
expect(updateMock).toHaveBeenCalledWith(item2);
});
// to implement: "Conjured" items degrade in Quality twice as fast as normal items
});

View File

@ -0,0 +1,18 @@
import { Item } from "@app/item";
import { getUpdateBehaviorFor } from "./behavior-resolver";
import { AgedBrieBehavior } from "./implementations/aged-brie-behavior";
import { LegacyBehavior } from "./implementations/legacy-behavior";
describe("Behavior resolver", () => {
it("should correctly resolve Aged Brie", () => {
expect(getUpdateBehaviorFor(new Item("Aged Brie", 0, 0))).toBeInstanceOf(
AgedBrieBehavior
);
});
it("should correctly resolve the rest to Legacy behavior", () => {
expect(
getUpdateBehaviorFor(new Item("some other item", 0, 0))
).toBeInstanceOf(LegacyBehavior);
});
});

View File

@ -0,0 +1,40 @@
import { Item } from "@app/item";
import { AgedBrieBehavior } from "./aged-brie-behavior";
describe("AgedBrie Behavior", () => {
it("should increase quality of Aged Brie as it gets older", () => {
const behavior = new AgedBrieBehavior(new Item("Aged Brie", 1, 1));
const result = behavior.update();
expect(result).toMatchObject({
name: "Aged Brie",
sellIn: 0,
quality: 2,
});
});
it("should increase quality of Aged Brie twice as fast after sell in date", () => {
const behavior = new AgedBrieBehavior(new Item("Aged Brie", 0, 1));
const result = behavior.update();
expect(result).toMatchObject({
name: "Aged Brie",
sellIn: -1,
quality: 3,
});
});
it("should never increase quality of Aged Brie over 50", () => {
const behavior = new AgedBrieBehavior(new Item("Aged Brie", 0, 50));
const result = behavior.update();
expect(result).toMatchObject({
name: "Aged Brie",
sellIn: -1,
quality: 50,
});
});
});

View File

@ -0,0 +1,110 @@
import { Item } from "@app/item";
import { LegacyBehavior } from "./legacy-behavior";
describe("Legacy Behavior", () => {
it("should degrade sell inn and quality each day", () => {
const behavior = new LegacyBehavior(new Item("standard item", 1, 1));
const result = behavior.update();
expect(result).toMatchObject({
name: "standard item",
sellIn: 0,
quality: 0,
});
});
it("should degrade quality twice as fast after sell in date", () => {
const behavior = new LegacyBehavior(new Item("standard item", 0, 2));
const result = behavior.update();
expect(result).toMatchObject({
name: "standard item",
sellIn: -1,
quality: 0,
});
});
it("should not degrade quality below 0", () => {
const behavior = new LegacyBehavior(new Item("standard item", 1, 0));
const result = behavior.update();
expect(result).toMatchObject({
name: "standard item",
sellIn: 0,
quality: 0,
});
});
it("should not change quality of Sulfuras", () => {
const behavior = new LegacyBehavior(
new Item("Sulfuras, Hand of Ragnaros", 0, 80)
);
const result = behavior.update();
expect(result).toMatchObject({
name: "Sulfuras, Hand of Ragnaros",
sellIn: 0,
quality: 80,
});
});
it("should increase quality of Backstage passes by 1 if sell in date is more than 10 days away", () => {
const behavior = new LegacyBehavior(
new Item("Backstage passes to a TAFKAL80ETC concert", 11, 20)
);
const result = behavior.update();
expect(result).toMatchObject({
name: "Backstage passes to a TAFKAL80ETC concert",
sellIn: 10,
quality: 21,
});
});
it("should increase quality of Backstage passes by 2 if sell in date is less than 10 days away but more than 5", () => {
const behavior = new LegacyBehavior(
new Item("Backstage passes to a TAFKAL80ETC concert", 9, 20)
);
const result = behavior.update();
expect(result).toMatchObject({
name: "Backstage passes to a TAFKAL80ETC concert",
sellIn: 8,
quality: 22,
});
});
it("should increase quality of Backstage passes by 3 if sell in date is less than 5 days away", () => {
const behavior = new LegacyBehavior(
new Item("Backstage passes to a TAFKAL80ETC concert", 4, 20)
);
const result = behavior.update();
expect(result).toMatchObject({
name: "Backstage passes to a TAFKAL80ETC concert",
sellIn: 3,
quality: 23,
});
});
it("should drop quality of Backstage passes to 0 after sell in date", () => {
const behavior = new LegacyBehavior(
new Item("Backstage passes to a TAFKAL80ETC concert", 0, 20)
);
const result = behavior.update();
expect(result).toMatchObject({
name: "Backstage passes to a TAFKAL80ETC concert",
sellIn: -1,
quality: 0,
});
});
});

View File

@ -1,13 +1,15 @@
import { pathsToModuleNameMapper } from "ts-jest";
import { compilerOptions } from './tsconfig.json'
import { pathsToModuleNameMapper } from "ts-jest";
import { compilerOptions } from "./tsconfig.json";
export default {
roots: ['<rootDir>/app', '<rootDir>/test/jest'],
roots: ["<rootDir>/app", "<rootDir>/snapshot-tests"],
collectCoverage: true,
coverageDirectory: 'coverage',
coverageProvider: 'v8',
coverageDirectory: "coverage",
coverageProvider: "v8",
transform: {
'^.+\\.tsx?$': 'ts-jest',
"^.+\\.tsx?$": "ts-jest",
},
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' } ),
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
prefix: "<rootDir>/",
}),
};

View File

@ -3,9 +3,9 @@
"version": "1.0.0",
"description": "Gilded Rose kata in TypeScript",
"scripts": {
"precompile": "rimraf app/**/*.js test/**/*.js",
"precompile": "rimraf app/**/*.js snapshot-tests/**/*.js",
"compile": "tsc",
"pretest": "rimraf app/**/*.js test/**/*.js",
"pretest": "rimraf app/**/*.js snapshot-tests/**/*.js",
"test": "jest",
"test:watch": "jest --watchAll"
},

View File

@ -1,153 +0,0 @@
import { GildedRose } from "@app/gilded-rose";
import { Item } from "@app/item";
describe("Gilded Rose", () => {
it("should have an empty array as items when no constructor parameter is provided", () => {
const gildedRose = new GildedRose();
expect(gildedRose.items).toEqual([]);
});
it("should degrade sell inn and quality each day", () => {
const gildedRose = new GildedRose([new Item("standard item", 1, 1)]);
const items = gildedRose.updateQuality();
expect(items[0]).toMatchObject({
name: "standard item",
sellIn: 0,
quality: 0,
});
});
it("should degrade quality twice as fast after sell in date", () => {
const gildedRose = new GildedRose([new Item("standard item", 0, 2)]);
const items = gildedRose.updateQuality();
expect(items[0]).toMatchObject({
name: "standard item",
sellIn: -1,
quality: 0,
});
});
it("should not degrade quality below 0", () => {
const gildedRose = new GildedRose([new Item("standard item", 1, 0)]);
const items = gildedRose.updateQuality();
expect(items[0]).toMatchObject({
name: "standard item",
sellIn: 0,
quality: 0,
});
});
it("should increase quality of Aged Brie as it gets older", () => {
const gildedRose = new GildedRose([new Item("Aged Brie", 1, 1)]);
const items = gildedRose.updateQuality();
expect(items[0]).toMatchObject({
name: "Aged Brie",
sellIn: 0,
quality: 2,
});
});
it("should increase quality of Aged Brie twice as fast after sell in date", () => {
const gildedRose = new GildedRose([new Item("Aged Brie", 0, 1)]);
const items = gildedRose.updateQuality();
expect(items[0]).toMatchObject({
name: "Aged Brie",
sellIn: -1,
quality: 3,
});
});
it("should never increase quality of Aged Brie over 50", () => {
const gildedRose = new GildedRose([new Item("Aged Brie", 0, 50)]);
const items = gildedRose.updateQuality();
expect(items[0]).toMatchObject({
name: "Aged Brie",
sellIn: -1,
quality: 50,
});
});
it("should not change quality of Sulfuras", () => {
const gildedRose = new GildedRose([
new Item("Sulfuras, Hand of Ragnaros", 0, 80),
]);
const items = gildedRose.updateQuality();
expect(items[0]).toMatchObject({
name: "Sulfuras, Hand of Ragnaros",
sellIn: 0,
quality: 80,
});
});
it("should increase quality of Backstage passes by 1 if sell in date is more than 10 days away", () => {
const gildedRose = new GildedRose([
new Item("Backstage passes to a TAFKAL80ETC concert", 11, 20),
]);
const items = gildedRose.updateQuality();
expect(items[0]).toMatchObject({
name: "Backstage passes to a TAFKAL80ETC concert",
sellIn: 10,
quality: 21,
});
});
it("should increase quality of Backstage passes by 2 if sell in date is less than 10 days away but more than 5", () => {
const gildedRose = new GildedRose([
new Item("Backstage passes to a TAFKAL80ETC concert", 9, 20),
]);
const items = gildedRose.updateQuality();
expect(items[0]).toMatchObject({
name: "Backstage passes to a TAFKAL80ETC concert",
sellIn: 8,
quality: 22,
});
});
it("should increase quality of Backstage passes by 3 if sell in date is less than 5 days away", () => {
const gildedRose = new GildedRose([
new Item("Backstage passes to a TAFKAL80ETC concert", 4, 20),
]);
const items = gildedRose.updateQuality();
expect(items[0]).toMatchObject({
name: "Backstage passes to a TAFKAL80ETC concert",
sellIn: 3,
quality: 23,
});
});
it("should drop quality of Backstage passes to 0 after sell in date", () => {
const gildedRose = new GildedRose([
new Item("Backstage passes to a TAFKAL80ETC concert", 0, 20),
]);
const items = gildedRose.updateQuality();
expect(items[0]).toMatchObject({
name: "Backstage passes to a TAFKAL80ETC concert",
sellIn: -1,
quality: 0,
});
});
// to implement: "Conjured" items degrade in Quality twice as fast as normal items
});