Skip to content

Commit

Permalink
Implement Order Domain Objects - Cart, CartItem, CartItemOption (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
seongbin9786 committed Jan 30, 2022
1 parent 749a3c2 commit 5407cc4
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 0 deletions.
44 changes: 44 additions & 0 deletions src/AUSG.Eats.Order.Domain/Cart.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
namespace AUSG.Eats.Order.Domain;

public class Cart
{
public long UserId { get; set; }

// IList에는 AsReadOnly();가 없다. (why?)
private readonly List<CartItem> _items = new List<CartItem>();

// IReadOnlyCollection에서 hint로 IEnumerable로 변경함
// hint로 Expression Body로 변경함
public IEnumerable<CartItem> Items => this._items.AsReadOnly();

public void AddToCart(CartItem newItem)
{
this._items.Add(newItem);
}

public void RemoveFromCart(CartItem itemToRemove)
{
this._items.Remove(itemToRemove);
}

public void AlterCartItem(CartItem itemToChange)
{
if (!this._items.Remove(itemToChange)) // Id 비교이므로 제거된다.
throw new ArgumentException("없는 CartItem을 변경하려고 한다.");
this._items.Add(itemToChange);
}

public override bool Equals(object? obj)
{
if ((obj == null) || !(this.GetType() == obj.GetType()))
return false;
var another = (Cart) obj;
return this.UserId == another.UserId;
}

public override int GetHashCode()
{
// Non-readonly property referenced in 'GetHashCode()' ??
return this.UserId.GetHashCode();
}
}
28 changes: 28 additions & 0 deletions src/AUSG.Eats.Order.Domain/CartItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace AUSG.Eats.Order.Domain;

public class CartItem
{
public long Id { get; set; }
public List<CartItemOption> Options { get; set; }
public int Quantity { get; set; }

public CartItem(List<CartItemOption> options, int quantity)
{
this.Options = options;
this.Quantity = quantity;
}

public override bool Equals(object? obj)
{
if ((obj == null) || !(this.GetType() == obj.GetType()))
return false;
var another = (CartItem) obj;
return this.Id == another.Id;
}

public override int GetHashCode()
{
// Non-readonly property referenced in 'GetHashCode()' ??
return this.Id.GetHashCode();
}
}
13 changes: 13 additions & 0 deletions src/AUSG.Eats.Order.Domain/CartItemOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace AUSG.Eats.Order.Domain;

public class CartItemOption
{
public string Name { get; }
public decimal Price { get; }

public CartItemOption(string name, decimal price)
{
this.Name = name;
this.Price = price;
}
}
143 changes: 143 additions & 0 deletions src/AUSG.Eats.Order.Test/OrderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using System.Collections.ObjectModel;
using AUSG.Eats.Order.Domain;
using Xunit;

namespace AUSG.Eats.Order.Test;

public class OrderTests
{

private CartItem MakeNewCartItem()
{
var options = new List<CartItemOption>();
const int quantity = 1;
return new CartItem(options, quantity);
}

[Fact(DisplayName = "CartItemOption 객체는 Name과 Price 필드를 노출한다.")]
public void CartItemOption_Shares_Name_and_Price()
{
const string name = "name";
const decimal price = 1000m;
var cartItemOption = new CartItemOption(name, price);

Assert.Equal(cartItemOption.Name, name);
Assert.Equal(cartItemOption.Price, price);
}

[Fact(DisplayName = "CartItem 객체는 Id, List<CarItemOption>, Quantity 필드를 노출한다.")]
public void CartItem_Shares_Id_and_ListOfCartItemOption_and_Quantity()
{
const long pid = 1L;
var options = new List<CartItemOption>();
const int quantity = 0;
var cartItem = new CartItem(options, quantity);
cartItem.Id = pid;

Assert.Equal(cartItem.Options, options); // IEnumerable ?
Assert.Equal(cartItem.Quantity, quantity);
Assert.Equal(cartItem.Id, pid);
}

[Fact(DisplayName = "Cart 객체는 List<CartItem> 필드를 노출한다.")]
public void Cart_Shares_ListOfCartItem()
{
var cart = new Cart();
Assert.Empty(cart.Items); // Collection && size=0
}

[Fact(DisplayName = "Cart 객체가 노출하는 List<CartItem>은 외부에서 그 내용을 수정할 수 없어야 한다.")]
public void ListOfCartItem_of_Cart_Must_Not_Be_Mutable()
{
var cart = new Cart();

Assert.True(cart.Items is ReadOnlyCollection<CartItem>);
}

[Fact(DisplayName = "Cart 객체는 UserId 필드를 노출하며 UserId로 식별할 수 있다.")]
public void Cart_Shares_UserId_and_Can_be_Identified_Using_UserId()
{
var cart1 = new Cart();
var cart2 = new Cart();
var userId = 1L;
cart1.UserId = userId;
cart2.UserId = userId;

// Shares Id
Assert.Equal(cart1.UserId, userId);

// Identified by UserId
Assert.Equal(cart1, cart2);

// Compare HashCode
Assert.Equal(cart1.GetHashCode(), cart2.GetHashCode());
}

[Fact(DisplayName = "Cart 객체는 CartItem을 추가할 수 있다.")]
public void Cart_can_Add_CartItem()
{
var cart = new Cart();
var cartItem1 = MakeNewCartItem();
cart.AddToCart(cartItem1);

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();
oldItem.Id = cartItemId;
cart.AddToCart(oldItem);

// when (Item의 상태를 변경함)
// 가능한 모든 필드가 변경됨을 확인하는 게 적절할 것으로 보임
var newOptions = new List<CartItemOption>();
const int newQuantity = 2;
oldItem.Options = newOptions;
oldItem.Quantity = newQuantity;
cart.AlterCartItem(oldItem);

// then
var alteredItem = cart.Items.ElementAt(0);
Assert.Equal(alteredItem.Id, oldItem.Id);
Assert.Equal(alteredItem.Options, newOptions); // Collection의 새 레퍼런스의 일치 확인해도 충분하다.
Assert.Equal(alteredItem.Quantity, newQuantity);
}

// 추가 TC
[Fact(DisplayName = "Cart 객체는 CartItem을 변경할 때 해당 Item이 이미 List에 없다면 예외를 발생시킨다.")]
public void Cart_throw_ArgumentException_when_Alter_CartItem()
{
// given
var cart = new Cart();
var oldItem = MakeNewCartItem();
oldItem.Id = 1L;

// when (Item의 상태를 변경함)
// 가능한 모든 필드가 변경됨을 확인하는 게 적절할 것으로 보임
Action alterItem = () => cart.AlterCartItem(oldItem);

// then
ArgumentException ex = Assert.Throws<ArgumentException>(alterItem);
}
}

0 comments on commit 5407cc4

Please sign in to comment.