【问题标题】:Mock Object and Interface模拟对象和接口
【发布时间】:2010-05-11 08:35:51
【问题描述】:

我是使用模拟对象进行单元测试的新手。我使用 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);
    }

}

所以,我想知道货币如何在toEuros(..) 方法中使用 ExchangeRate。

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));
        }
    }

所以,Tess 单元运行正常

但是当我想在 Business Object 中运行 main 方法时:

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

我必须为 Business 添加构造函数。

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)!!! 那么正确吗?

【问题讨论】:

    标签: java unit-testing easymock


    【解决方案1】:

    EasyMock 基于接口创建一个mock 对象。模拟对象实现了接口的所有方法,并且对于您指定的那些方法(例如使用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);
    

    希望这会有所帮助;如果没有,也许您可​​以阅读一些关于 OOP、接口和继承的介绍性文本以更好地理解。

    在您回答的代码示例中,Dao 对象应传递给Bussiness,而不是在内部创建,因为后者有效地阻止了单元测试。

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

    您还可以向Bussiness 添加参数化构造函数,以一步完成初始化,而不是两步。

    【讨论】:

    • 感谢 Péter Török 先生,接口的方法由 EasyMock 在 CurrencyTest 中指定。但我仍然不明白 How to Currency 使用接口的方法(不是 CurrencyTest)。因为你总是要在 EasyMock 中使用接口所以如何对业务类进行响应。
    • 现在,我明白了。我的弱点是我不知道如何将 DaoImpl 传递给 Business。并且 Mock 总是与 Interface 一起使用。再次感谢您,并对我的错误深表歉意。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-30
    • 2012-02-13
    • 1970-01-01
    相关资源
    最近更新 更多