模拟对象和接口

发布于 2024-09-01 18:40:04 字数 5366 浏览 5 评论 0原文

我是使用模拟对象进行单元测试的新手。我用的是 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 对象中运行 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)! 那么它正确吗?

I'm a newbie in Unit Test with Mock Object. I use EasyMock. I try to understand this example:

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

}

So, i wonder how to Currency use ExchangeRate in toEuros(..) method.

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

The behavior of getRate(..) method is not specified because ExchangeRate is an interface.

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

So I try do myself example. Following is my codes:

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

So, The unit Tess run correctly

But when i want to run main method in Business Object:

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

I have to add the constructor for Business.

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

So, my business class is:

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

Also I have to implement Dao interface:

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

}

In design, you always create interface for all of the classes which will be tested (like DaoImpl) !!!
So is it correct?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

弃爱 2024-09-08 18:40:04

EasyMock 基于接口创建一个模拟对象。模拟对象实现了接口的所有方法,并且对于您指定的那些方法(例如使用expect),它在调用它们时“重放”指定的行为。

当模拟对象被创建时,它处于记录模式。该行

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

指定当使用给定参数调用 mock.getRate 时,它将返回 1.5 。然后通过调用将对象置于重播模式

EasyMock.replay(mock);

所有这些都在 文档

更新:到您的评论 - 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 添加一个参数化构造函数,以便一步完成初始化,而不是两步。

EasyMock creates a mock object based on the interface. The mock object implements all the methods of the interface and for those methods you specify (e.g. with expect), it "replays" the specified behaviour when they are called.

When a mock object is created, it is in recording mode. The line

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

specifies that when mock.getRate is called with the given parameters, it shall return 1.5 . Then the object is put into replay mode with the call

EasyMock.replay(mock);

All this is explained in more details in the documentation.

Update: to your comment - Currency is passed an instance of ExchangeRate here:

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

All it cares is that it gets an object implementing that interface, so that

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

can be called. The test method, then, passes the mock object it created to the currency object:

Currency actual = testObject.toEuros(mock);

Hope this helps; if not, maybe you could read some introductory text on OOP, interfaces and inheritance to get a better understanding.

In the code example in your answer, the Dao object should be passed to Bussiness rather than created internally, since the latter effectively prevents unit testing.

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

You could also add a parameterized constructor to Bussiness to make the initialization in one step instead of two.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文