Java EE单元测试笔记

1. 单元测试 

1.1  测试对象。

1) OracleResource 是一个 Enterprise JavaBeans (EJB) 3.1 bean,用(REST) 来公开他的预言。

@Path("javafuture")
@Stateless
public class OracleResource {

   @Inject
   Instance<Consultant> company;
   @Inject
   Event<Result> eventListener;

   @GET
   @Produces(MediaType.TEXT_PLAIN)
   public String predictFutureOfJava(){

     checkConsultantAvailability();
     Consultant consultant = getConsultant();
     Result prediction = consultant.predictFutureOfJava();
     eventListener.fire(prediction);
     if(JAVA_IS_DEAD.equals(prediction)){
       throw new IllegalStateException("Please perform a sanity / reality check");

     }
     return prediction.name();
   }

   void checkConsultantAvailability(){
     if(company.isUnsatisfied()){
       throw new IllegalStateException("No consultant to ask!");
     }
   }

   Consultant getConsultant(){
     for (Consultant consultant : company) {
       return consultant;
     }
     return null;
   }

}
2)  Consultant 是一个 Java 接口, Blogger是一个实现。

public class Blogger implements Consultant{
   @Override
   public Result predictFutureOfJava() {
     return Result.JAVA_IS_DEAD;
   }
}
3)  PredictionAudit 也是EJB 3.1 bean,保存所有成功的和失败的预言。

@Stateless

public class PredictionAudit { 

  @PersistenceContext

  EntityManager em; 

  public void onSuccessfulPrediction(@Observes(during= TransactionPhase.AFTER_SUCCESS) Result result){
    persistDecision(result,true);
   }

  public void onFailedPrediction(@Observes(during= TransactionPhase.AFTER_FAILURE) Result result){
     persistDecision(result,false);
   }

  void persistDecision(Result result,boolean success) {
     Prediction prediction = new Prediction(result,success);
     em.persist(prediction);
  } 

  public List<Prediction> allDecisions(){
     return this.em.createNamedQuery(Prediction.findAll).getResultList();
  }

}
4) Prediction是JPA 2 实体。

@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@NamedQuery(name=Prediction.findAll,query="Select d from Prediction d")
public class Prediction {

 public final static String PREFIX = "com.abien.testing.oracle.entity.Prediction.";
 public final static String findAll = PREFIX + "findAll";

 @Id
 @GeneratedValue
 @XmlTransient
 private long id; 

 @Column(name="prediction_result")
 @Enumerated(EnumType.STRING)
 private Result result; 

 @Temporal(TemporalType.TIME)
 private Date predictionDate;

 private boolean success;

 public Prediction() {
   this.predictionDate = new Date();
 } 

 public Prediction(Result result, boolean success) {
    this();
    this.result = result;
    this.success = success;
 }

//bookkeeping methods omitted

}
1.2 单元测试。

1) 选用Mockito来“模拟”各种难以实现的类、资源或服务,原理是它能够从类或接口创建“智能代理”(也称为模拟)

2) 要精确测试 PredictionAudit 类,必须“模拟”EntityManager。之后测试 EntityManager#persist 方法是否接收正确参数,但这里我们不测试是否真正保存 Prediction 实体。

import static org.mockito.Mockito.*;

public class PredictionAuditTest {

 private PredictionAudit cut; 

 @Before
 public void initializeDependencies(){
    cut = new PredictionAudit();
    cut.em = mock(EntityManager.class);
 } 

 @Test
 public void savingSuccessfulPrediction(){
    final Result expectedResult = Result.BRIGHT;
    Prediction expected = new Prediction(expectedResult, true);
    this.cut.onSuccessfulPrediction(expectedResult);
    verify(cut.em,times(1)).persist(expected);
 }

 @Test
 public void savingRolledBackPrediction(){
    final Result expectedResult = Result.BRIGHT;
    Prediction expected = new Prediction(expectedResult, false);
    this.cut.onFailedPrediction(expectedResult);
    verify(cut.em,times(1)).persist(expected);
 }
}
3)精确测试 OracleResource类,需要模拟 javax.enterprise.event.Event 和 javax.enterprise.inject.Instance。

public class OracleResourceTest {
 private OracleResource cut;
 @Before
 public void initializeDependencies(){
    this.cut = new OracleResource();
    this.cut.company = mock(Instance.class);
    this.cut.eventListener = mock(Event.class);
 }
如果未找到 Consultant 实现,我们希望生成 IllegalStateException。
@Test(expected=IllegalStateException.class)
 public void checkConsultantAvailabilityWithoutConsultant(){
    when(this.cut.company.isUnsatisfied()).thenReturn(true);
    this.cut.checkConsultantAvailability();

 }
模拟 javax.enterprise.inject.Instance 返回的 Iterator:
 Iterator mockIterator(Consultant consultant) {
    Iterator iterator = mock(Iterator.class);
    when(iterator.next()).thenReturn(consultant);
    when(iterator.hasNext()).thenReturn(true);
    return iterator;
 }

通过上面的Iterator,枚举到Consultant 接口的 Blogger 实现则会返回 JAVA_IS_DEAD,这将引发 IllegalStateException。

@Test(expected=IllegalStateException.class)
 public void unreasonablePrediction(){
    Consultant consultant = new Blogger();
    Iterator iterator = mockIterator(consultant);
    when(this.cut.company.iterator()).thenReturn(iterator);
    this.cut.predictFutureOfJava();
 }
模拟 Event 实例,验证方法调用。

@Test
 public void unreasonablePredictionFiresEvent(){
    Consultant consultant = new Blogger();
    Result expectedResultToFire = consultant.predictFutureOfJava();
    Iterator iterator = mockIterator(consultant);
    when(this.cut.company.iterator()).thenReturn(iterator);
    try{
        this.cut.predictFutureOfJava();
    }catch(IllegalStateException e){}
    verify(this.cut.eventListener,times(1)).fire(expectedResultToFire);
 }
下面 PredictionArchiveResource 使用了直接注入的 PredictionAudit 类。

@Path("predictions")
@Stateless
public class PredictionArchiveResource {
 @EJB
 PredictionAudit audit;

 @GET
 @Produces(MediaType.APPLICATION_JSON)
 public List<Prediction> allPredictions(@DefaultValue("5") @QueryParam("max") int max) {
    List<Prediction> allPredictions = audit.allPredictions();
    if (allPredictions.size() <= max) {
        return allPredictions;
    } else {
        return allPredictions.subList(0, max);
    }
 }
}
尽管 PredictionAudit 是一个类而不是一个接口,但也可以轻松地模拟它。为了测试返回列表的大小限制,对 PredictionAudit 类进行了全面模拟
public class PredictionArchiveResourceTest { 
 PredictionArchiveResource cut; 

 @Before
 public void initialize(){
    this.cut = new PredictionArchiveResource();
    this.cut.audit = mock(PredictionAudit.class);
 }

 @Test
 public void allDecisionsWithMaxLesserReturn() throws Exception {
    int expectedSize = 2;
    List<Prediction> prediction = createDecisions(expectedSize);
    when(this.cut.audit.allPredictions()).thenReturn(prediction); 
    List<Prediction> allDecisions = this.cut.allPredictions(3);
    assertThat(allDecisions.size(), is(expectedSize));
 }

 @Test
 public void allDecisionsWithMaxGreaterReturn() throws Exception {
    int max = 5;
    int expected = 3;
    List<Prediction> prediction = createDecisions(max);
    when(this.cut.audit.allPredictions()).thenReturn(prediction); 
    List<Prediction> allDecisions = this.cut.allPredictions(expected);
    assertThat(allDecisions.size(), is(expected));
 }

 @Test
 public void allDecisionsWithMaxEqualReturn() throws Exception {
 //obvious code omitted
 }

 List<Prediction> createDecisions(final int nr) {
    return new ArrayList<Prediction>(){{

        for (int i = 0; i < nr; i++) {
           add(new Prediction(Result.BRIGHT, true));
        }
     }};
 }
}

原帖(http://www.oracle.com/technetwork/cn/articles/java/unittesting-455385-zhs.html)

原帖代码:https://kenai.com/projects/javaee-patterns/sources/hg/show/TestingEJBAndCDI





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值