2010-05-11 4 views
3

Я новичок в модульном тесте с помощью Mock Object. Я использую EasyMock. Я пытаюсь понять этот пример:Mock Object and Interface

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.

Update: на ваш комментарий - 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

Thanks mr Péter Török, Метод интерфейса указан в CurrencyTest от EasyMock. Но я до сих пор не понимаю, что в качестве валюты используются методы интерфейса (а не CurrencyTest). Потому что вам всегда нужно использовать интерфейс в EasyMock, чтобы как реагировать на бизнес-классы. – user338027

+0

@tunl, см. Мое обновление. –

+0

Теперь я понял. Моя слабость в том, что я не знаю, как передать DaoImpl в бизнес. И макет всегда идет вместе с интерфейсом. Еще раз спасибо и жалею о своих ошибках. – user338027