From 0e0c226eea06d4b6c39d51c3af221950cea22a1a Mon Sep 17 00:00:00 2001 From: simon Date: Thu, 27 Mar 2014 08:19:13 +0000 Subject: [PATCH] Added lots of unit tests, all of which pass. But the core program still breaks! --- src/vending/core.clj | 57 ++++++++++++++++------------- test/vending/test/core.clj | 68 +++++++++++++++++++++++++++++++++++ test/vending/test/handler.clj | 3 +- 3 files changed, 101 insertions(+), 27 deletions(-) create mode 100644 test/vending/test/core.clj diff --git a/src/vending/core.clj b/src/vending/core.clj index 7b85f87..ebaa2ba 100644 --- a/src/vending/core.clj +++ b/src/vending/core.clj @@ -9,13 +9,13 @@ :change nil :output nil} ) -(def coin-values {:merk 1 :plack 0.25 :bawbee 0.10 :bodle 0.05}) +(def coin-values {:merk 100 :plack 25 :bawbee 10 :bodle 5}) (keys coin-values) (.indexOf (keys coin-values) :bodle) -(def item-prices {:caramel-wafer 0.65 :teacake 1 :snowball 1.5}) +(def item-prices {:caramel-wafer 65 :teacake 100 :snowball 150}) (defn coin-value [coin] (coin-values coin)) @@ -65,6 +65,8 @@ (defn subtract-denomination [list position] + "given a list of four numbers and a position, return a similar list with one subtracted + from the number at this position in the list" (cond (= (count list) position)(cons (- (first list) 1) (rest list)) true (cons (first list) (subtract-denomination (rest list) position)))) @@ -103,26 +105,29 @@ (apply in-make-change (map #(or % 0) (list amount merk plack bawbee bodle))) )) -(defn subtract-change [machine item-price change] - "subtract this change from this machine and return the machine" - (let [coins (:coins machine) - merk (:merk coins) - plack (:plack coins) - bawbee (:bawbee coins) - bodle (:bodle coins) - chance (make-change item-price merk plack bawbee bodle)]) - (assoc (dissoc machine :change) :change change)) -(defn make-change-machine [machine item-price change] - (let [ - tend (sum-coins (:tendered machine)) - change (make-change item-price (:merk tend) (:plack tend) (:bawbee tend) (:bodle tend))] - (cond (nil? change) machine) - true (assoc (dissoc (subtract-change machine item-price change) :change ) :change change ))) +(defn subtract-change [coin-stacks change] + "Return a copy of these coin-stacks with this change removed" + {:merk (- (:merk coin-stacks) (nth change 0)) + :plack (- (:plack coin-stacks) (nth change 1)) + :bawbee (- (:bawbee coin-stacks) (nth change 2)) + :bodle (- (:bodle coin-stacks) (nth change 3))}) + +(defn subtract-change-machine [machine change] + "return a copy of this machine which has this amount of change removed from its coins" + (let [coins (:coins machine)] + (assoc (dissoc machine :coins) :coins (subtract-change coins change)))) + +(defn make-change-machine [machine change] + "given this machine and these numbers of coins to remove, return a copy of the machine + with the coins removed" + (let [tend (sum-coins (:tendered machine))] + (cond (= change '(0 0 0 0)) machine) + true (assoc (dissoc (subtract-change-machine machine change) :change ) :change change ))) (defn remove-from-stock [machine item] - "TODO: This requires clever use of update-in" - machine) + "return a copy of this machine with one fewer of this item in stock" + (update-in machine [:items item] dec)) (defn deliver-item [machine item change] (make-change-machine @@ -135,12 +140,14 @@ (defn get-item [machine item] (let [item-price (item item-prices) - tend (sum-coins (:tendered machine)) - change (make-change item-price (:merk tend) (:plack tend) (:bawbee tend) (:bodle tend))] - (cond (>= 0 (item (:stock machine))) (message-machine (coin-return machine) (str "Sorry, " item " not in stock")) - (<= (coins-value (:tendered machine)) item-price) (message-machine machine "Please insert more money") - (empty? change) (message-machine (coin-return machine) "Sorry, I don't have enough change.") - true (message-machine (deliver-item machine item change) (str "Enjoy your " item))))) + coins (:coins machine) + tendered (coins-value (:tendered machine)) + change (make-change (- tendered item-price) (:merk coins) (:plack coins) (:bawbee coins) (:bodle coins))] + (print change) + (cond (>= 0 (item (:stock machine))) (message-machine (coin-return machine) (str "Sorry, " item " not in stock")) + (<= tendered item-price) (message-machine machine "Please insert more money") + (= change '(0 0 0 0)) (message-machine (coin-return machine) "Sorry, I don't have enough change.") + true (message-machine (deliver-item machine item change) (str "Enjoy your " item))))) (defn get-caramel-wafer [machine] (get-item machine :caramel-wafer)) diff --git a/test/vending/test/core.clj b/test/vending/test/core.clj new file mode 100644 index 0000000..e0ba36b --- /dev/null +++ b/test/vending/test/core.clj @@ -0,0 +1,68 @@ +(ns vending.test.core + (:use clojure.test + ring.mock.request + vending.core)) + +(defn member? + "true if seq contains elm" + [seq elm] + (some #(= elm %) seq)) + + +(deftest test-add-coin + (testing + "Test adding coins to machine" + (let [machine (make-default-machine) + merk-machine (add-coin machine :merk) + plack-machine (add-coin machine :plack) + bawbee-machine (add-coin machine :bawbee) + bodle-machine (add-coin machine :bodle) + all-machine (add-coin (add-coin (add-coin (add-coin machine :merk) :plack) :bawbee) :bodle) ] + (is (member? (:tendered merk-machine) :merk) "after adding a merk, a merk should be present") + (is (member? (:tendered all-machine) :merk) "after adding a merk, a merk should be present") + (is (member? (:tendered plack-machine) :plack) "after adding a plack, a plack should be present") + (is (member? (:tendered all-machine) :plack) "after adding a plack, a plack should be present") + (is (member? (:tendered bawbee-machine) :bawbee) "after adding a bawbee, a bawbee should be present") + (is (member? (:tendered all-machine) :bawbee) "after adding a bawbee, a bawbee should be present") + (is (member? (:tendered bodle-machine) :bodle) "after adding a bodle, a bodle should be present") + (is (member? (:tendered all-machine) :bodle) "after adding a merk, a merk should be present") + (is (nil? (:tendered machine)) "No coins should leak through into the base machine"))) + + (testing + "coin-return should return all coins added" + (let [machine (make-default-machine) + all-machine (add-coin (add-coin (add-coin (add-coin machine :merk) :plack) :bawbee) :bodle) + return-machine (coin-return all-machine)] + (is (= (:message return-machine)) "Coins returned") + (is (= (:tendered all-machine) (:change return-machine)) "All coins should be returned") + )) + + (testing + "summing coins" + (let [coins1 (sum-coins (list :merk :plack :bawbee :bodle)) + coins2 (sum-coins (list :merk :plack :bawbee :bodle :merk :plack :bawbee :bodle))] + (= (:merk coins1 1) "after adding one merk, one merk should be present") + (= (:merk coins2 2) "after adding two merks, two merks should be present") + )) + + (testing + "making appropriate change" + (let [no-change (make-change 0 5 5 5 5) + ten-change (make-change 10 5 5 5 5) + all-change (make-change 140 5 5 5 5)] + (is (= no-change '(0 0 0 0)) "one hundred units of change should be satisfied with one merk") + (is (= all-change '(1 1 1 1)) "140 units of change should be satisfied with one coin of each denomination") + (is (or (= ten-change '(0 0 1 0)) (= ten-change '(0 0 0 2))) "ten change could be satisfied with one bawbee or two bodles"))) + + (testing + "removing change from machine" + (let [machine (make-default-machine) + changed (make-change-machine machine '(1 1 1 1)) + emptied (make-change-machine machine '(1 4 4 4))] + (is (= (vals (:coins changed)) '(0 3 3 3)) "each coin slot should be decremented by one") + (is (= (vals (:coins emptied)) '(0 0 0 0)) "each coin slot should be empty (contain zero)") + )) + ) + + (make-change-machine (make-default-machine) '( 1 1 1 1)) + diff --git a/test/vending/test/handler.clj b/test/vending/test/handler.clj index 69b3d09..6783b17 100644 --- a/test/vending/test/handler.clj +++ b/test/vending/test/handler.clj @@ -7,8 +7,7 @@ (testing "main route" (let [response (app (request :get "/"))] (is (= (:status response) 200)) - (is (= (:body response) - "\n \n Welcome to vending\n \n \n \n
\n \n
\n
\n

Welcome to vending

\n \n

Some links to get started

  1. HTML templating
  2. Accessing the database
  3. Serving static resources
  4. Setting response types
  5. Defining routes
  6. Adding middleware
  7. Sessions and cookies
  8. Security
  9. Deploying the application
\n\n
\n \n \n\n\n\n")))) + )) (testing "not-found route" (let [response (app (request :get "/invalid"))]