2010-05-11 273 views
3

我是一個單元測試與模擬對象的新手。我使用EasyMock。我試圖理解這個例子:模擬對象和接口

import java.io.IOException; 

public interface ExchangeRate { 

    double getRate(String inputCurrency, String outputCurrency) throws IOException; 

} 

import java.io.IOException; 


public class Currency { 

    private String units; 
    private long amount; 
    private int cents; 


    public Currency(double amount, String code) { 
     this.units = code; 
     setAmount(amount); 
    } 

    private void setAmount(double amount) { 
     this.amount = new Double(amount).longValue(); 
     this.cents = (int) ((amount * 100.0) % 100); 
    } 

    public Currency toEuros(ExchangeRate converter) { 
     if ("EUR".equals(units)) return this; 
     else { 
      double input = amount + cents/100.0; 
      double rate; 
      try { 
       rate = converter.getRate(units, "EUR"); 
       double output = input * rate; 
       return new Currency(output, "EUR"); 
      } catch (IOException ex) { 
       return null; 
      } 
     } 
    } 

    public boolean equals(Object o) { 
     if (o instanceof Currency) { 
      Currency other = (Currency) o; 
      return this.units.equals(other.units) 
        && this.amount == other.amount 
        && this.cents == other.cents; 
     } 
     return false; 
    } 

    public String toString() { 
     return amount + "." + Math.abs(cents) + " " + units; 
    } 

} 

import junit.framework.TestCase; 
import org.easymock.EasyMock; 
import java.io.IOException; 

public class CurrencyTest extends TestCase { 

    public void testToEuros() throws IOException { 
     Currency testObject = new Currency(2.50, "USD"); 
     Currency expected = new Currency(3.75, "EUR"); 
     ExchangeRate mock = EasyMock.createMock(ExchangeRate.class); 
     EasyMock.expect(mock.getRate("USD", "EUR")).andReturn(1.5); 
     EasyMock.replay(mock); 
     Currency actual = testObject.toEuros(mock); 
     assertEquals(expected, actual); 
    } 

} 

所以,我不知道如何使用貨幣在EXCHANGERATE方法toEuros(..)

rate = converter.getRate(units, "EUR"); 

未指定getRate(..)方法的行爲,因爲ExchangeRate是一個接口。

/********************************************************************************/ 

所以我嘗試做我自己的例子。以下是我的代碼:

/** 
*Interface to access data 
*/ 
public interface Dao { 
    public boolean getEntityById(int id) throws SQLException; 
} 

/** 
*Business class do something in business layer 
*/ 
public class Bussiness { 
    private Dao dao; 

    public Dao getDao() { 
     return dao; 
    } 

    public void setDao(Dao dao) { 
     this.dao = dao; 
    } 

    public boolean doSomeThing(int id) throws SQLException { 
     if(dao.getEntityById(id)) { 
      return true; 
     } else { 
      return false; 
     } 
    } 

    public static void main(String[] args) throws SQLException { 
     Bussiness b = new Bussiness(); 
     b.doSomeThing(3); 
    } 
} 


package tunl; 

import java.sql.SQLException; 

import org.easymock.EasyMock; 
import org.testng.Assert; 
import org.testng.annotations.BeforeTest; 
import org.testng.annotations.Test; 

    /** 
    * This is my unit Test 
    */ 
    @Test 
    public class MyUnitTest { 
     private Bussiness bussiness; 
     private Dao mock; 

     @BeforeTest 
     public void setUp() { 
      bussiness = new Bussiness(); 
      mock = EasyMock.createMock(Dao.class);// interface not class 
      bussiness.setDao(mock); 
     } 

     public void testDoSomeThing() throws SQLException { 
      EasyMock.expect(mock.getEntityById(3)).andReturn(true); 
      EasyMock.replay(mock); 
      Assert.assertTrue(bussiness.doSomeThing(3)); 
     } 
    } 

因此,該單位苔絲正確運行

但是,當我想運行在業務對象主要方法:

public static void main(String[] args) throws SQLException { 
      Bussiness b = new Bussiness(); 
      b.doSomeThing(3); 
} 

我要補充的業務構造。

public Bussiness() { 
    dao = new DaoImpl(); 
} 

所以,我的業務類:

package tunl; 

import java.sql.SQLException; 

public class Bussiness { 
    private Dao dao; 

    public Bussiness() { 
     dao = new DaoImpl(); 
    } 
    public Dao getDao() { 
     return dao; 
    } 

    public void setDao(Dao dao) { 
     this.dao = dao; 
    } 

    public boolean doSomeThing(int id) throws SQLException { 
     if(dao.getEntityById(id)) { 
      return true; 
     } else { 
      return false; 
     } 
    } 

    public static void main(String[] args) throws SQLException { 
     Bussiness b = new Bussiness(); 
     b.doSomeThing(3); 
    } 
} 

而且我要實現DAO接口:

package tunl; 

import java.sql.SQLException; 

public class DaoImpl implements Dao { 

    @Override 
    public boolean getEntityById(int id) throws SQLException { 
     if(id == 3) { 
      System.out.println("System input 3 "); 
      return true; 
     } 
     System.out.println("You have to input 3 "); 
     return false; 
    } 

} 

在外觀設計上,你總是對所有類的創建界面,將進行測試(如DaoImpl)! 那它正確嗎?

回答

3

EasyMock根據界面創建模擬對象。模擬對象實現了接口的所有方法以及您指定的方法(例如,使用expect),它會在調用指定行爲時「重放」指定的行爲。

當創建模擬對象時,它在錄製模式。該生產線

EasyMock.expect(mock.getRate("USD", "EUR")).andReturn(1.5); 

指定當mock.getRate被稱爲具有給定參數,它應返回1.5。那麼該對象被放入重放模式與呼叫

EasyMock.replay(mock); 

所有這一切,更詳細的解釋documentation

更新:到您的評論 - Currency傳遞這裏的ExchangeRate一個實例:

public Currency toEuros(ExchangeRate converter) { ... } 

它所關心的是,它會實現該接口的對象,從而使

rate = converter.getRate(units, "EUR"); 

可以調用。然後,測試方法將創建的模擬對象傳遞給貨幣對象:

Currency actual = testObject.toEuros(mock); 

希望這有助於;如果沒有,也許你可以閱讀關於面向對象,接口和繼承的一些介紹性文字,以獲得更好的理解。

在你的答案中的代碼示例中,Dao對象應該傳遞給Bussiness而不是內部創建,因爲後者有效地防止了單元測試。

public static void main(String[] args) throws SQLException { 
     Bussiness b = new Bussiness(); 
     b.setDao(new DaoImpl()); 
     b.doSomeThing(3); 
} 

你也可以添加一個參數的構造函數來Bussiness使初始化一個步驟,而不是兩個。

+0

感謝PéterTörök先生, EasyMock在CurrencyTest中指定了接口的方法。 但我仍然不明白如何貨幣使用接口的方法(不CurrencyTest)。 因爲你總是必須在EasyMock中使用接口,所以如何對業務類進行響應。 – user338027 2010-05-12 01:39:08

+0

@tunl,看我的更新。 – 2010-05-12 11:18:28

+0

現在,我明白了。我的弱點是我不知道如何將DaoImpl傳遞給Business。 Mock總是和Interface一起工作。 再次感謝您對我的錯誤感到抱歉。 – user338027 2010-05-12 17:33:15