OOP的一个很好的机制是使用抽象类,抽象类是不能被实例化的,只能提供给派生类一个接口。设计人员通常使用抽象类来强迫实现人员从基类派生,这样可以确保新的类包含一些期待的功能。
在Junit对抽象类的测试中再次引入工厂设计模式,其测试思想是:抽象类不能被实例化,所以使用具体类测试抽象类是不可以的。因此,构造抽象类的测试类必须也是抽象的。该类需要强制声明两种类型的抽象方法。第一类抽象方法即工厂方法,返回所有被测试抽象类的具体子类实例,第二类定义抽象方法返回所有被测试抽象类的具体子类行为期望值。如下面代码,Commodity类是一个商品抽象类,该类的抽象方法描述具体如下:
getCommodityName():取商品名称
changerName():修改商品名称
getCommodityPrice():取商品价格
changerPrice():修改商品价格
Commodity类具体代码如下
package com.fastpiont;
public abstract class Commodity {
public abstract String getCommodityName();// 取得商品名称
public abstract void changerName(String newName);// 修改商品名称
public abstract double getCommodityPrice();// 取得商品价格
public abstract void changerPrice(double newPrice);// 修改商品价格
}
CommodityTestCase是Commodity抽象类的测试类,同样该类被声明为抽象的。Commodity抽象类包含了getCommodity()工厂方法返回具体类实例,因为这才是真正的被测试对象。PrepareAndGetExpectedName()、PrepareAndGetExpectedPrice()、PrepareAndChangerExpectedName()、PrepareAndChangerExpectedPrice()四组方法分别返回具体类实例行为的期望值,即该实例行为的判断基准,具体代码如下:
package com.fastpiont;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public abstract class CommodityTest {
Commodity comm;
public abstract Commodity getCommodity();// 工厂方法,返回具体商品类
public abstract String prepareAndGetExpectedName();// 返回期望值
public abstract double prepareAndGetExpectedPrice();
public abstract String repareAndChangerExpectedName();// 返回更改期望值
public abstract double prepareAndChangerExpectedPrice();
@Before
public void setUp() throws Exception {
comm = getCommodity();
}
@After
public void tearDown() throws Exception {
}
@Test
public void testGetCommodityName() {
String expected = prepareAndGetExpectedName();
String received = getCommodity().getCommodityName();
assertEquals(expected, received);
}
@Test
public void testChangerName() {
comm.changerName(repareAndChangerExpectedName());
assertEquals(repareAndChangerExpectedName(), comm.getCommodityName());
}
@Test
public void testGetCommodityPrice() {
double expected = prepareAndGetExpectedPrice();
double received = getCommodity().getCommodityPrice();
assertEquals(expected, received);
}
@Test
public void testChangerPrice() {
comm.changerPrice(prepareAndChangerExpectedPrice());
assertEquals(prepareAndChangerExpectedPrice(), comm.getCommodityPrice());
}
}
现在根据抽象类测试思想构造一个集成了Commodity抽象类的具体商品子类Commodity_Book,Commodity_Book类的构造方法有两个传参,即根据传入的书籍名称值和书籍单价值生成一本书实例。Commodity_Book类不鼓励直接修改书籍的属性,但是可以通过连歌公共方法changerName()和changerPrice()来实现,具体代码如下:
package com.fastpiont;
public class Commodity_Book extends Commodity {
private String book_name;
private double book_price;
public Commodity_Book(String bookname, double bookprice) {
book_name = bookname;
book_price = bookprice;
}
public void changerName(String newName) {
setBook_name(newName);
}
public void changerPrice(double newPrice) {
setBook_price(newPrice);
}
public String getBook_name() {
return book_name;
}
private void setBook_name(String book_name) {
this.book_name = book_name;
}
public double getBook_price() {
return book_price;
}
private void setBook_price(double book_price) {
this.book_price = book_price;
}
@Override
public String getCommodityName() {
// TODO Auto-generated method stub
return getBook_name();
}
@Override
public double getCommodityPrice() {
// TODO Auto-generated method stub
return getBook_price();
}
}
Commodity_BookTestCase类继承了CommodityTestCase抽象类,整个类显得非常简单,包括工厂方法返回一个具体的Commodity实例和四个针对该实例的期望值设定,具体代码如下:
package com.fastpiont;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
public abstract class CommodityTest {
Commodity comm;
public abstract Commodity getCommodity();// 工厂方法,返回具体商品类
public abstract String prepareAndGetExpectedName();// 返回期望值
public abstract double prepareAndGetExpectedPrice();
public abstract String repareAndChangerExpectedName();// 返回更改期望值
public abstract double prepareAndChangerExpectedPrice();
@Before
public void setUp() throws Exception {
comm = getCommodity();
}
@After
public void tearDown() throws Exception {
}
@Test
public void testGetCommodityName() {
String expected = prepareAndGetExpectedName();
String received = comm.getCommodityName();
assertEquals(expected, received);
}
@Test
public void testChangerName() {
comm.changerName(repareAndChangerExpectedName());
assertEquals(repareAndChangerExpectedName(), comm.getCommodityName());
}
@Test
public void testGetCommodityPrice() {
double expected = prepareAndGetExpectedPrice();
double received = comm.getCommodityPrice();
assertEquals(expected, received, 0.001);
}
@Test
public void testChangerPrice() {
comm.changerPrice(prepareAndChangerExpectedPrice());
assertEquals(prepareAndChangerExpectedPrice(),
comm.getCommodityPrice(), 0.001);
}
}
这种针对抽象类的测试方法是Junit推导者所主张的,好处在于该抽象类的所有具体子类都不用在测试抽象类中的所有抽象发发(抽象类中的具体方法除外,因为该方法可能被具体子类覆盖),符合XP测试的接口测试定义。