<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    測試驅動開發案例之自動售貨機(第1集)

    測試驅動開發(TDD, Test Driven Development)是一種很有意思的軟件開發方式,本集以較小的步伐體驗TDD。

    /**  A program simulation of an automat.
     *
     *  Director: Zuo Baoquan
     *  Contact me:
     *     Blog:   http://www.tkk7.com/BaoquanInside
     *     MSN: Baoquan.Zuo [at] hotmail.com
     *     Email:  Baoquan.Zuo [at] gmail.com
     *
     *  Copyright 2006 BEC Studio
     */
     
    引言:
    (該問題截取自《面向對象的設計與模式》(Written by Cay Horstmann) Exercise 2.18
     
    設計并實現一個模擬自動售貨機的程序。通過給機器投入正確的硬幣,便可以購買到相應的商品。用戶從一個可用的商品列表中選擇商品、投入硬幣并得到商品。如果沒有足夠的錢幣或由于此商品已銷售完畢,則將硬幣退回。操作員可以重新備并將錢幣取走。
     
     
    場景#1
    地點:同濟大學西南樓
    涉眾:我,空自動售貨機
     
    寢室樓里面剛搬來了一臺空的自動售貨機,那我們來測試一下:
     
    public class TestEmptyAutomat extends TestCase
    {
      public void testIsEmpty()
      {
        Automat automat = new Automat(); 
        assertTrue("it should be empty.", automat.isEmpty());
      }
    }
     
    Eclipse提示"Automat cannot be resolved to a type",好,看我的:
    public class Automat
    {
      public Automat()  // constructor stub
      {
      }
    }
     
    再露一手:
    //class Automat
    public boolean isEmpty()
    {
      return true;
    }
     
    全部保存,運行測試,呵呵,綠色進度條!測試成功!
     
    好,既然我們還沒有投過硬幣,那么余額應該是0了~
     
    // class TestEmptyAutomat
    public void testBalance()
    {
      Automat automat = new Automat();
      assertEquals("the balance should be 0.", 0, automat.balance); 
    }
     
    繼續使用我們的法寶:
    // class Automat
    public final int balance = 0;
     
    運行測試,yeah!
     
    看了一遍測試程序,決定優化一下:
    public class TestEmptyAutomat extends TestCase
    {
      protected void setUp() throws Exception
      {
        super.setUp();
        automat = new Automat();
      }
     
      public void testIsEmpty()
      {
        assertTrue("it should be empty.", automat.isEmpty());
      }
     
      public void testBalance()
      {
        assertEquals("the balance should be 0.", 0, automat.balance); 
      }
     
      private Automat automat;
    }
    再運行一次測試,呵呵,又是綠色!
     
    場景#2
    地點:同濟大學西南樓
    涉眾:我,自動售貨機(有一瓶百事可樂)
     
    好消息,樓長在自動售貨機里面放了一瓶Pepsi。
     
    public class TestAutomatWithOnePepsi extends TestCase
    {
      protected void setUp() throws Exception
      {
        super.setUp();
        automat = new Automat();
        pepsi = new Pepsi();  // 一瓶百事可樂
        automat.add(pepsi);  // 樓長阿姨放的~
      }
      
      public void testEmpty()
      {
        assertFalse("it should not be empty.", automat.isEmpty());
      }
     
      public Automat automat;
      public Pepsi pepsi;
    }

    接著創建Pepsi類
     
    public class Pepsi
    {
      public Pepsi() // constructor stub
      {
       
      }
    }
     
    再給Automat添加add方法:
    // class Automat
    public void add(Pepsi pepsi)
    {
     
    }
     
    好,現在有兩個TC(Test Case)了,為了運行兩個測試案例,我們來創建一個Test Suite:
    public class AutomatTests
    {
      public static Test suite()
      {
        TestSuite suite = new TestSuite("Test for net.mybec.automat");
        //$JUnit-BEGIN$
        suite.addTestSuite(TestAutomatWithOnePepsi.class);
        suite.addTestSuite(TestEmptyAutomat.class);
        //$JUnit-END$
       
        return suite;
      }
    }
     
    編譯成功,運行AutomatTests,紅色進度條。TestEmptyAutomat綠色,TestAutomatWithOnePepsi紅色。呵呵,看來要讓add做點事情了。
     
    // class Automat
    public void add(Pepsi pepsi)
    {
      goods.add(pepsi);
    }
     
    // 添加一個裝Pepsi的數組列表
    private final ArrayList<Pepsi> goods = new ArrayList<Pepsi>();

     
    // 修改isEmpty方法
    public boolean isEmpty()
    {
      return goods.isEmpty();
    }

     
    再次運行AutomatTests,呵呵,綠色!我們喜歡!
     
    好,我們再看看Automat的余額:
     
    // class TestAutomatWithOnePepsi
    public void testBalance()
    {
      assertEquals("the balance should be 0.", 0, automat.balance);
    }
     
    運行一遍測試,Ok。
     
     
    我們還沒有投硬幣,當然不能買百事可樂了:
     
    // class TestAutomatWithOnePepsi
    public void testCanBuyWithoutBalance()
    {
      assertFalse("we cannot buy pepsi without money.", automat.canBuy(pepsi));
    }
    // class Automat
    public boolean canBuy(Pepsi pepsi)
    {
      return false;
    }
     
    我們太喜歡運行測試了,于是又忍不住運行了所有的自動測試(呵呵,實際上我們只需要點擊一個運行按鈕)。又是綠色~
     
    好,如果Pepsi的價格是2元,我們投1塊錢試試~
     
    // class Pepsi
    public static final int PRICE = 2;
     
    // class TestAutomatWithOnePepsi
    public void testCanBuyWithOneYuan()
    {
      automat.put(1);
      assertFalse("we cannot buy pepsi with one yuan.", automat.canBuy(pepsi));
    }
     
    // class Automat
    public void put(int yuan)
    {
     
    }
     
    運行測試,綠色。顯然1塊錢買不到百事可樂。那就投2塊錢吧:
     
    // class TestAutomatWithOnePepsi
    public void testCanBuyWithTwoYuan()
    {
      automat.put(2);
      assertTrue("we can not buy pepsi with two yuan.", automat.canBuy(pepsi));
    }
     
    運行測試,紅色進度條,JUnit提示“we can not buy pepsi with two yuan.” 天啊,這不公平。
    想起來了,Automat.put什么也沒做。于是我們添了幾筆:
     
    // class Automat
    public void put(int yuan)
    {
      balance += yuan;
    }
     
    public boolean canBuy(Pepsi pepsi)
    {
      return balance >= Pepsi.PRICE;
    }
     
    public int balance = 0;  // 去掉了final
     
    迫不及待地點擊了運行按鈕,yeah!終于能買到喜歡的Pepsi了(因為看到了綠色進度條~)。
     
    于是急忙買了一瓶Pepsi:
    // class TestAutomatWithOnePepsi
    public void testBuyPepsiWithTwoYuan()
    {
      automat.put(2);
      automat.buy(pepsi);
      assertTrue("the automat should be empty.", automat.isEmpty());
    }
    // class Automat
    public void buy(Pepsi pepsi)
    {
     
    }
     
    Run Tests, Failed. So,
    // class Automat
    public void buy(Pepsi pepsi)
    {
      goods.remove(pepsi);
    }
    Run Tests again, OK.
     
     
    // class TestAutomatWithOnePepsi
    public void testBuyPepsiWithTwoYuan()
    {
      automat.put(2);
      automat.buy(pepsi);
      assertTrue("the automat should be empty.", automat.isEmpty());
      assertEquals("the balance should be 0.", 0, automat.balance);
    }
    Run Tests, failed, so
     
    // class Automat
    public void buy(Pepsi pepsi)
    {
      goods.remove(pepsi);
      balance -= Pepsi.PRICE;
    }
    Run Tests again, OK.
     
    那如果沒有投幣就直接買Pepsi呢?再添加一個測試:
     
    // class TestAutomatWithOnePepsi
    public void testBuyPepsiWithNoBalance()
    {
      try
      {
        automat.buy(pepsi);
        fail("a NoBalanceException was expected when buying a pepsi with no balance.");
      }
      catch (NoBalanceException e)
      {
      }
    }
     
     
    // class NoBalanceException
    public class NoBalanceException extends Exception
    {
      public NoBalanceException(String arg0)
      {
        super(arg0);
      }
    }
     
     
    // class TestAutomatWithOnePepsi
    public void testBuyPepsiWithTwoYuan()
    {
      automat.put(2);
      try
      {
        automat.buy(pepsi);
      }
      catch (NoBalanceException e)
      {
        fail("a NoBalanceException was throwed.");
      } 
      assertTrue("the automat should be empty.", automat.isEmpty()); 
      assertEquals("the balance should be 0.", 0, automat.balance);
     }

     
    // class Automat
    public void buy(Pepsi pepsi) throws NoBalanceException
    {
      if (balance == 0)
        throw new NoBalanceException("No balance");
      else if (balance >= Pepsi.PRICE)
      {
        goods.remove(pepsi);
        balance -= Pepsi.PRICE;
      }
    }
     
    運行測試,綠色!
    接下來投1塊錢買買看:
     
    // class TestAutomatWithOnePepsi
    public void testBuyPepsiWithOneYuan()
    {
      automat.put(1);
      try
      {
        automat.buy(pepsi);
        fail("a LackOfBalanceException was expected when buying a pepsi without enough balance.");
      }
      catch (LackOfBalanceException e)
      {
      }
      catch (NoBalanceException e)
      {
        fail("a NoBalanceException was not expected.");
      }
    }
    接下來,為了能使編譯通過,做了一下修改:
    // class LackOfBalanceException
    public class LackOfBalanceException extends Exception
    {
      public LackOfBalanceException(String arg0)
      {
        super(arg0);
      }
    }
    // class Automat
    public void buy(Pepsi pepsi) throws NoBalanceException, LackOfBalanceException
    {
      // ...
    }
     
    // class TestAutomatWithOnePepsi
    public void testBuyPepsiWithNoBalance()
    {
      // ...
      catch (LackOfBalanceException e)
      {
        fail("a LackOfBalanceException was not expected.");
      }

    }
     
    public void testBuyPepsiWithTwoYuan()
    {
      // ...
      catch (LackOfBalanceException e)
      {
        fail("a LackOfBalanceException was not expected.");
      } 
      // ...
    }

    好,沒有錯誤提示了。編譯,運行測試,紅色進度條。
    testBuyPepsiWithOneYuan()提示“a LackOfBalanceException was expected when buying a pepsi with no enough balance."
     
    我們修改一下Automat.buy():
    // class Automat
    public void buy(Pepsi pepsi) throws NoBalanceException, LackOfBalanceException
    {
      if (balance == 0)
      {
        throw new NoBalanceException("No balance");
      }
      else if (balance >= Pepsi.PRICE)
      {
        goods.remove(pepsi);
        balance -= Pepsi.PRICE;
      }
      else
        throw new LackOfBalanceException("Lack of Balance");
    }
    測試通過了!小小慶祝一下~
     
    那如果我們投了三塊錢呢?好,試試看:
     
    // class TestAutomatWithOnePepsi
    public void testBuyPepsiWithThreeYuan()
    {
      automat.put(3);
      assertTrue("we can buy pepsi.", automat.canBuy(pepsi));  
      try
      {
        automat.buy(pepsi);
        assertTrue("the automat should be empty.", automat.isEmpty());
        assertEquals("the balance should be 1.", 1, automat.balance);
      }
      catch (Exception e)
      {
        fail("Exception was not expected.");
      }  
    }
     
    編譯,運行測試,成功!Yeah!
     
    To be continued...
     
     
    下一集將會更加精彩,敬請期待。
    posted on 2006-02-25 23:12 Baoquan Inside 閱讀(1762) 評論(3)  編輯  收藏 所屬分類: TDD

    評論

    # re: 測試驅動開發案例之自動售貨機(第1集) 2006-02-27 02:56 dev  回復  更多評論   

    你的這個例子存在并發問題,一個是金額,另一個是商品都有這個問題;另外,一個自動售貨機可能會有幾種商品,這個沒有考慮到

    # re: 測試驅動開發案例之自動售貨機(第1集) 2006-02-27 10:32 Baoquan Inside  回復  更多評論   

    @dev
    謝謝你的關注!我在并發編程方面幾乎沒有經驗,所以例子可能存在你說提到的并發問題。為了寫這篇文章,我一直在Blog和Eclipse之間換來換去。不過故事才剛剛開始,所以,你提到的“多種商品”問題將在下一集中被解決。

    # re: 測試驅動開發案例之自動售貨機(第1集) 2007-09-16 16:48 魚上游  回復  更多評論   

    第2集呢?

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 久久无码av亚洲精品色午夜| 成人毛片视频免费网站观看| 亚洲精品国产电影| 国产精品亚洲一区二区三区久久 | 成年人免费观看视频网站| 亚洲女人影院想要爱| 久久精品免费大片国产大片| 亚洲精品无码激情AV| sihu国产精品永久免费| 国产亚洲精品成人a v小说| 免费一区二区无码东京热| 亚洲今日精彩视频| 美女视频黄是免费的网址| 亚洲成a人无码亚洲成www牛牛| 日本免费无遮挡吸乳视频电影| 日木av无码专区亚洲av毛片| 99re在线精品视频免费| 亚洲av成人综合网| 国产免费人视频在线观看免费| 亚洲第一香蕉视频| 日本道免费精品一区二区| 亚洲网站在线免费观看| 在线jyzzjyzz免费视频| 成人国产网站v片免费观看| 四虎在线免费播放| free哆拍拍免费永久视频| 久久精品亚洲中文字幕无码网站 | h视频在线观看免费| 亚洲AV日韩AV永久无码免下载| 精品福利一区二区三区免费视频 | 亚洲人成在线免费观看| 久久午夜羞羞影院免费观看 | 亚洲永久精品ww47| 日本黄网站动漫视频免费| 日韩成人精品日本亚洲| 亚洲成在人线av| 国产美女a做受大片免费| 在线观看亚洲电影| 色拍自拍亚洲综合图区| 亚洲国产成人影院播放| 久久精品国产免费观看 |