diff --git a/src/AUSG.Eats.Order.Domain/Cart.cs b/src/AUSG.Eats.Order.Domain/Cart.cs new file mode 100644 index 0000000..1ca8b2a --- /dev/null +++ b/src/AUSG.Eats.Order.Domain/Cart.cs @@ -0,0 +1,47 @@ +namespace AUSG.Eats.Order.Domain; + +public class Cart +{ + private readonly List _items = new(); + + private long? _userId; + + public Cart(long? userId = null) + { + _userId = userId; + } + + public IEnumerable Items => _items.AsReadOnly(); + + public void AddToCart(CartItem newItem) + { + _items.Add(newItem); + } + + public void RemoveFromCart(CartItem itemToRemove) + { + _items.Remove(itemToRemove); + } + + public void AlterCartItem(CartItem itemToChange) + { + if (!_items.Remove(itemToChange)) // Id 비교이므로 제거된다. + throw new ArgumentException("없는 CartItem을 변경하려고 한다."); + _items.Add(itemToChange); + } + + public override bool Equals(object? obj) + { + // use null propagation (잘 이해 못하고 있습니다.) + // see TC: CSharpTests#compare_with_null_instance_returns_false + if (obj is not Cart other) + return false; + return _userId == other._userId; + } + + public override int GetHashCode() + { + // Non-readonly property referenced in 'GetHashCode()' ?? + return _userId.GetHashCode(); + } +} diff --git a/src/AUSG.Eats.Order.Domain/CartItem.cs b/src/AUSG.Eats.Order.Domain/CartItem.cs new file mode 100644 index 0000000..31f23a6 --- /dev/null +++ b/src/AUSG.Eats.Order.Domain/CartItem.cs @@ -0,0 +1,26 @@ +namespace AUSG.Eats.Order.Domain; + +public class CartItem +{ + private long? _id; + + public CartItem(long? id) + { + _id = id; + } + + public override bool Equals(object? obj) + { + // use null propagation (잘 이해 못하고 있습니다.) + // see TC: CSharpTests#compare_with_null_instance_returns_false + if (obj is not CartItem other) + return false; + return _id == other._id; + } + + public override int GetHashCode() + { + // Non-readonly property referenced in 'GetHashCode()' ?? + return _id.GetHashCode(); + } +} diff --git a/src/AUSG.Eats.Order.Test/CSharpTests.cs b/src/AUSG.Eats.Order.Test/CSharpTests.cs new file mode 100644 index 0000000..0b391b9 --- /dev/null +++ b/src/AUSG.Eats.Order.Test/CSharpTests.cs @@ -0,0 +1,61 @@ +using Xunit; + +namespace AUSG.Eats.Order.Test; + +public class CSharpTests +{ + [Fact(DisplayName = "Integer fields are initialized to 0")] + public void int_init_value_is_0_in_class() + { + var sut = new IntInit(); + + Assert.Equal(0, sut.id); + } + + [Fact(DisplayName = "Nullable integer fields are initialized to 0")] + public void nullable_int_init_value_is_null_in_class() + { + var sut = new IntInit(); + + // Assert.Null은 사용할 수 없다. + // Boxing allocation: conversion from 'int?' to 'object' requires boxing of value type + // Assert.Null(sut.nullableId); + + // Nullable의 null 여부를 체크할 수 있는 메소드가 없다. + Assert.True(sut.nullableId == null); + } + + [Fact(DisplayName = "Comparing with null instance returns false using null propagation")] + public void compare_with_null_instance_returns_false() + { + var NonNullInstance = new EqualsImplEx(1L); + var NullInstance = new EqualsImplEx(); + + Assert.True(NullInstance.Id == null); + Assert.NotEqual(NonNullInstance, NullInstance); + } + + private class IntInit + { + public int id { get; set; } + public int? nullableId { get; set; } + } + + private class EqualsImplEx + { + public EqualsImplEx(long? id = null) + { + Id = id; + } + + public long? Id { get; } + + public override bool Equals(object? obj) + { + // use null propagation + if (obj is not EqualsImplEx other) + return false; + return Id == other.Id; + } + } +} diff --git a/src/AUSG.Eats.Order.Test/OrderTests.cs b/src/AUSG.Eats.Order.Test/OrderTests.cs new file mode 100644 index 0000000..4f199cc --- /dev/null +++ b/src/AUSG.Eats.Order.Test/OrderTests.cs @@ -0,0 +1,104 @@ +using AUSG.Eats.Order.Domain; +using Xunit; + +namespace AUSG.Eats.Order.Test; + +public class OrderTests +{ + private CartItem MakeNewCartItem(long? id = null) + { + return new CartItem(id); + } + + [Fact(DisplayName = "CartItem 객체는 Id로 식별할 수 있다.")] + public void CartItem_Can_Be_Identified_By_Id() + { + // given + const long pid = 1L; + var cartItem1 = MakeNewCartItem(pid); + var cartItem2 = MakeNewCartItem(pid); + + // then + Assert.Equal(cartItem1, cartItem2); + Assert.Equal(cartItem1.GetHashCode(), cartItem2.GetHashCode()); + } + + [Fact(DisplayName = "Cart 객체는 UserId로 식별할 수 있다.")] + public void Cart_Shares_UserId_and_Can_be_Identified_Using_UserId() + { + // given + var userId = 1L; + var cart1 = new Cart(userId); + var cart2 = new Cart(userId); + + // then + Assert.Equal(cart1, cart2); + Assert.Equal(cart1.GetHashCode(), cart2.GetHashCode()); + } + + [Fact(DisplayName = "Cart 객체는 CartItem을 추가할 수 있다.")] + public void Cart_can_Add_CartItem() + { + // given + var cart = new Cart(); + var cartItem1 = MakeNewCartItem(); + + // when + cart.AddToCart(cartItem1); + + // then + Assert.Equal(cart.Items.ElementAt(0), cartItem1); + } + + [Fact(DisplayName = "Cart 객체는 CartItem을 제거할 수 있다.")] + public void Cart_can_Remove_CartItem() + { + // given + var cart = new Cart(); + var cartItem1 = MakeNewCartItem(); + cart.AddToCart(cartItem1); + Assert.Equal(cart.Items.ElementAt(0), cartItem1); + + // when + cart.RemoveFromCart(cartItem1); + + // then + Assert.Empty(cart.Items); + } + + [Fact(DisplayName = "Cart 객체는 CartItem을 변경할 수 있다.")] + public void Cart_can_Alter_CartItem() + { + // given + const long cartItemId = 1L; + var cart = new Cart(); + var oldItem = MakeNewCartItem(cartItemId); + cart.AddToCart(oldItem); + + // when + var newItem = MakeNewCartItem(cartItemId); + cart.AlterCartItem(newItem); + + // then + var alteredItem = cart.Items.ElementAt(0); + Assert.Equal(alteredItem, oldItem); + } + + [Fact(DisplayName = "Cart 객체는 CartItem을 변경할 때 해당 Item이 이미 List에 없다면 예외를 발생시킨다.")] + public void Cart_throw_ArgumentException_when_Alter_CartItem() + { + // given + var cart = new Cart(); + const long cartItemIdWhichDoesNotExist = 1L; + var oldItem = MakeNewCartItem(cartItemIdWhichDoesNotExist); + + // when + void AlterItem() + { + cart.AlterCartItem(oldItem); + } + + // then + Assert.Throws((Action) AlterItem); + } +}