|
module VendingMachineTest |
|
|
|
open NUnit.Framework |
|
open FsCheck |
|
open FsCheck.NUnit |
|
open Swensen.Unquote |
|
open FSharpx.Choice |
|
open FSharpx.Validation |
|
open Money |
|
open VendingMachine |
|
open DrinkStock |
|
|
|
let fsCheck t = fsCheck "" t |
|
|
|
let chooseFromList xs = gen { |
|
let! i = Gen.choose (0, List.length xs-1) |
|
return (List.nth xs i) |
|
} |
|
|
|
module 初期状態 = |
|
|
|
let vendingMachine = VendingMachine.init |
|
|
|
[<Test>] |
|
let ``お金を投入しなければ合計金額は0円``() = |
|
test <@ vendingMachine |> VendingMachine.getTotalAmount = 0 @> |
|
|
|
[<Test>] |
|
let ``10円を投入したら合計金額は10円``() = |
|
test <| |
|
<@ |
|
match vendingMachine |> VendingMachine.insert Ten with |
|
| Success vendingMachine -> vendingMachine |> VendingMachine.getTotalAmount = 10 |
|
| Failure _ -> false |
|
@> |
|
|
|
[<Test>] |
|
let ``お金を投入せずに払い戻し操作をしたら釣り銭は空``() = |
|
test <@ vendingMachine |> VendingMachine.payback |> fst = [] @> |
|
|
|
[<Test>] |
|
let ``想定外のお金を投入したらそのままつり銭として返される``() = |
|
test <| |
|
<@ |
|
match vendingMachine |> VendingMachine.insert One with |
|
| Success vendingMachine -> false |
|
| Failure(vendingMachine, m) -> m = One |
|
@> |
|
|
|
[<Test>] |
|
let ``利用可能なお金を1回投入して払い戻し操作をしたら釣り銭は投入したお金が戻ってくる``() = |
|
let allowMoney = chooseFromList vendingMachine.allowMoneys |
|
fsCheck <| Prop.forAll (Arb.fromGen allowMoney) (fun x -> |
|
match vendingMachine |> VendingMachine.insert x with |
|
| Success vendingMachine -> vendingMachine |> VendingMachine.payback |> fst = [x] |
|
| Failure(vendingMachine, m) -> false) |
|
|
|
[<Test>] |
|
let ``お金を投入していないのでコーラは購入可能ではない``() = |
|
test <@ vendingMachine |> VendingMachine.canBuy "コーラ" = false @> |
|
|
|
[<Test>] |
|
let ``お金を投入しなければ売り上げ金額は0円``() = |
|
test <@ vendingMachine |> VendingMachine.getSaleAmount = 0 @> |
|
|
|
module ``10円が投入された状態`` = |
|
|
|
let vendingMachine = VendingMachine.init |> VendingMachine.insert Ten |> function Success vendingMachine -> vendingMachine | Failure _ -> failwith "10円の投入に失敗しました" |
|
|
|
[<Test>] |
|
let ``追加で100円を投入したら合計金額は110円``() = |
|
test <| |
|
<@ |
|
match vendingMachine |> VendingMachine.insert Hundred with |
|
| Success vendingMachine -> vendingMachine |> VendingMachine.getTotalAmount = 110 |
|
| Failure _ -> false |
|
@> |
|
|
|
[<Test>] |
|
let ``払い戻し操作をしたら釣り銭は10円``() = |
|
test <@ vendingMachine |> VendingMachine.payback |> fst = [Ten] @> |
|
|
|
[<Test>] |
|
let ``払い戻し操作をしたら投入金額の合計は0円``() = |
|
test <@ vendingMachine |> VendingMachine.payback |> snd |> VendingMachine.getTotalAmount = 0 @> |
|
|
|
[<Test>] |
|
let ``投入金額が足りないのでコーラを購入できない``() = |
|
match vendingMachine |> VendingMachine.buy "コーラ" with |
|
| Success _ -> Assert.Fail("投入金額が足りないのにコーラが購入できました") |
|
| Failure e -> test <@ e.Message = "コーラを購入できませんでした" @> |
|
|
|
module ``120円が投入された状態`` = |
|
|
|
let vendingMachine = |
|
let x = FSharpx.Choice.choose { |
|
let v = VendingMachine.init |
|
let! v = v |> VendingMachine.insert Ten |
|
let! v = v |> VendingMachine.insert Ten |
|
return! v |> VendingMachine.insert Hundred |
|
} |
|
match x with |
|
| Success vendingMachine -> vendingMachine |
|
| Failure _ -> failwith "120円の投入に失敗しました" |
|
|
|
[<Test>] |
|
let ``コーラは購入可能である``() = |
|
test <@ vendingMachine |> VendingMachine.canBuy "コーラ" = true @> |
|
|
|
[<Test>] |
|
let ``存在しないドリンクは購入不可能``() = |
|
test <@ vendingMachine |> VendingMachine.canBuy "---" = false @> |
|
|
|
[<Test>] |
|
let ``コーラの購入を実行するとコーラの購入に成功する``() = |
|
test <| |
|
<@ |
|
match vendingMachine |> VendingMachine.buy "コーラ" with |
|
| Success(drink, _) -> drink.name = "コーラ" |
|
| Failure _ -> false |
|
@> |
|
|
|
[<Test>] |
|
let ``コーラの購入に成功するとコーラの在庫が一つ減る``() = |
|
match vendingMachine |> VendingMachine.buy "コーラ" with |
|
| Success(_, vendingMachine) -> test <@ vendingMachine |> VendingMachine.getDrinkStockInfo = "コーラ:120円:4本\nレッドブル:200円:5本\n水:100円:5本\n" @> |
|
| Failure e -> raise e |
|
|
|
[<Test>] |
|
let ``コーラの購入に成功するとコーラの値段分だけ投入金額が減少する``() = |
|
match vendingMachine |> VendingMachine.buy "コーラ" with |
|
| Success(_, vendingMachine) -> test <@ vendingMachine |> VendingMachine.getTotalAmount = 0 @> |
|
| Failure e -> raise e |
|
|
|
[<Test>] |
|
let ``コーラの購入に成功するとコーラの値段分だけ売り上げ金額が増加する``() = |
|
match vendingMachine |> VendingMachine.buy "コーラ" with |
|
| Success(_, vendingMachine) -> test <@ vendingMachine |> VendingMachine.getSaleAmount = 120 @> |
|
| Failure e -> raise e |
|
|
|
[<Test>] |
|
let ``コーラの購入して払い戻し操作を行うと釣り銭は0円``() = |
|
match vendingMachine |> VendingMachine.buy "コーラ" with |
|
| Success(_, vendingMachine) -> test <@ vendingMachine |> VendingMachine.payback |> fst = [] @> |
|
| Failure e -> raise e |
|
|
|
[<Test>] |
|
let ``コーラの在庫がないので購入できない``() = |
|
let vendingMachine = { vendingMachine with drinkStock = [] } |
|
match vendingMachine |> VendingMachine.buy "コーラ" with |
|
| Success _ -> Assert.Fail("コーラの在庫がないのにコーラが購入できました") |
|
| Failure e -> test <@ e.Message = "コーラを購入できませんでした" @> |
|
|
|
[<Test>] |
|
let ``購入可能な飲み物として水とコーラがリストとして取得できる``() = |
|
test <@ vendingMachine |> VendingMachine.purchasableDrink = [{name = "コーラ"; price = 120}; {name = "水"; price = 100}] @> |
|
|
|
module ``140円が投入された状態`` = |
|
|
|
let vendingMachine = |
|
let x = FSharpx.Choice.choose { |
|
let v = VendingMachine.init |
|
let! v = v |> VendingMachine.insert Ten |
|
let! v = v |> VendingMachine.insert Ten |
|
let! v = v |> VendingMachine.insert Ten |
|
let! v = v |> VendingMachine.insert Ten |
|
return! v |> VendingMachine.insert Hundred |
|
} |
|
match x with |
|
| Success vendingMachine -> vendingMachine |
|
| Failure _ -> failwith "140円の投入に失敗しました" |
|
|
|
[<Test>] |
|
let ``コーラの購入に成功するとコーラの値段分だけ投入金額が減少する``() = |
|
match vendingMachine |> VendingMachine.buy "コーラ" with |
|
| Success(_, vendingMachine) -> test <@ vendingMachine |> VendingMachine.getTotalAmount = 20 @> |
|
| Failure e -> raise e |
|
|
|
module ``1000円が投入された状態`` = |
|
|
|
let vendingMachine = |
|
VendingMachine.init |
|
|> VendingMachine.insert Thousand |
|
|> function Success vendingMachine -> vendingMachine | Failure _ -> failwith "1000円の投入に失敗しました" |
|
let drinkList = vendingMachine.drinkStock |> Seq.distinct |> Seq.toList |> chooseFromList |
|
|
|
[<Test>] |
|
let ``飲み物の購入に成功すると飲み物の値段分だけ投入金額が減少する``() = |
|
fsCheck <| Prop.forAll (Arb.fromGen drinkList) (fun x -> |
|
let expected = VendingMachine.getTotalAmount vendingMachine - x.price |
|
match vendingMachine |> VendingMachine.buy x.name with |
|
| Success(_, vendingMachine) -> vendingMachine |> VendingMachine.getTotalAmount = expected |
|
| Failure e -> raise e) |
|
|
|
[<Test>] |
|
let ``飲み物の購入に成功すると飲み物の値段分だけ売り上げ金額が増加する``() = |
|
fsCheck <| Prop.forAll (Arb.fromGen drinkList) (fun x -> |
|
match vendingMachine |> VendingMachine.buy x.name with |
|
| Success(_, vendingMachine) -> vendingMachine |> VendingMachine.getSaleAmount = x.price |
|
| Failure e -> raise e) |
|
|
|
[<Test>] |
|
let ``購入可能な飲み物として水,コーラ,レッドブルがリストとして取得できる``() = |
|
test <@ vendingMachine |> VendingMachine.purchasableDrink = [{name = "コーラ"; price = 120}; {name = "レッドブル"; price = 200}; {name = "水"; price = 100}] @> |
|
|
|
module 釣り銭用の小銭が10円100枚のみのとき = |
|
|
|
let vendingMachine = { vendingMachine with change = Array.create 100 Ten |> Array.toList } |
|
|
|
[<Test>] |
|
let ``コーラの購入して払い戻し操作を行うと釣り銭は10円玉が88枚``() = |
|
match vendingMachine |> VendingMachine.buy "コーラ" with |
|
| Success(_, vendingMachine) -> test <@ vendingMachine |> VendingMachine.payback |> fst = (Array.create 88 Ten |> Array.toList) @> |
|
| Failure e -> raise e |