模拟对象和接口
我是使用模拟对象进行单元测试的新手。我用的是 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
EasyMock 基于接口创建一个模拟对象。模拟对象实现了接口的所有方法,并且对于您指定的那些方法(例如使用
expect
),它在调用它们时“重放”指定的行为。当模拟对象被创建时,它处于记录模式。该行
指定当使用给定参数调用
mock.getRate
时,它将返回 1.5 。然后通过调用将对象置于重播模式所有这些都在 文档。
更新:到您的评论 -
Currency
在这里传递一个ExchangeRate
实例:它所关心的是它获取一个实现该接口的对象,所以 可以这样
称呼。然后,测试方法将其创建的模拟对象传递给货币对象:
希望这有帮助;如果没有,也许您可以阅读一些有关 OOP、接口和继承的介绍性文本,以便更好地理解。
在您答案的代码示例中,
Dao
对象应传递给Bussiness
而不是在内部创建,因为后者有效地阻止了单元测试。您还可以向
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
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 callAll this is explained in more details in the documentation.
Update: to your comment -
Currency
is passed an instance ofExchangeRate
here:All it cares is that it gets an object implementing that interface, so that
can be called. The test method, then, passes the mock object it created to the currency object:
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 toBussiness
rather than created internally, since the latter effectively prevents unit testing.You could also add a parameterized constructor to
Bussiness
to make the initialization in one step instead of two.