天天看點

持續內建之路—服務層的單元測試

  一、一般邏輯的單元測試。

  這裡采用的方式和資料通路層幾乎是一樣的,主要包含三步:

  1. 通過@databasesetup指定測試用資料集

  2. 執行被測試方法

  假設要被測試的代碼方法是:

@service

@transactional(readonly = true)

public class shopserviceimpl extends baseservice implements shopservice{

private logger logger = loggerfactory.getlogger(shopserviceimpl.class);

@transactional(readonly = false)

public floor addfloor(string buildingname, int floornum, string layout) {

//如果已經存在對應的樓層資訊,則抛出已經存在的異常資訊

floor floor = floordao.findbybuildingnameandfloornum(buildingname, floornum);

if (floor != null) {

throw new onlineshopexception(exceptioncode.shop_floor_existed);

}

//如果不存在對應的商場資訊,則添加新的商場

building building = buildingdao.findbyname(buildingname);

if (building == null) {

building = new building();

building.setname(buildingname);

buildingdao.save(building);

//添加并傳回樓層資訊

floor = new floor();

floor.setbuilding(building);

floor.setfloornum(floornum);

floor.setmap(layout);

floordao.save(floor);

return floor;

  其對應的接口是:

  public interface shopservice {

  public floor addfloor(string buildingname, int floornum, string layout);

  }

這段邏輯代碼的意思十分簡單和直白,那麼要編寫的單元的測試必須要包含所有分支情況:a. 商場和樓層資訊都存在的,抛出異常 b. 商場存在,而樓層不存在, 樓層資訊都被添加的。 c.  商場和樓層都不存在,全部新增。這裡就以第一種情況為例,先準備測試資料:

  <?xml version="1.0" encoding="utf-8"?>

  <dataset>

  <building id="1" name="new house"/>

  <floor id="1" building="1" floor_num="2"/>

  </dataset>

  接着編寫測試用例,注意要必須得注解不能忘掉:

@runwith(springjunit4classrunner.class)

@contextconfiguration("classpath:applicationcontext-test.xml")

@transactional

@testexecutionlisteners({

dependencyinjectiontestexecutionlistener.class,

dirtiescontexttestexecutionlistener.class,

customtransactiondbunittestexecutionlistener.class,

foreignkeydisabling.class})

public class shopservicetest {

@autowired

private shopservice shopservice;

@test

@databasesetup("shop/shopservice-addfloorexistexception-dataset.xml")

public void testaddfloorexistexception(){

try {

shopservice.addfloor("new house", 2, "");

fail();

} catch(exception e){

asserttrue(e instanceof onlineshopexception);

assertequals(exceptioncode.shop_floor_existed.code(), ((onlineshopexception)e).getcode());

  這個測試和資料通路層的測試看起來沒有什麼兩樣。

  二、使用mock對象隔離第三方接口

  軟體開發中一般都存在和第三方內建的情況,比如調用新浪的認證、百度的地圖等等。那麼在編寫測試的時候,基于效率的考慮,一般情況不會真的去調用這些遠端api(當然應該有其他測試可以及時發現第三方接口的變化),而是假定它們一直會傳回預期的結果。這個時候就需要用到mock對象,來模拟這些api産生相應的結果。

  在這裡,我是用了mockito,使用十分友善。假如現在使用者登入時,需要去第三方系統驗證,那麼現在來看如何對這個場景進行測試。還是先來看被測試的方法:

  private boolean validateuser(string inputname, string inputpassword) {

  return thirdpartyapi.authenticate(inputname, inputpassword);

  其中thirdpartyapi就是第三方用來認證的api。下面來看測試代碼:

public class userservicetest {

private userservice userservice;

private thirdpartyapi mockthirdpartyapi = mock(thirdpartyapi.class);

public void testlogin(){

//指定mock對象特定操作的傳回結果

when(mockthirdpartyapi.authenticate("jiml", "jiml")).thenreturn(true);

//通過setter用mock對象替換由spring初始化的第三方依賴

((userserviceimpl)userservice).setthirdpartyapi(mockthirdpartyapi);

boolean loginstatus = userservice.login("jiml", "jiml");

asserttrue(loginstatus);

  其實服務層的測試并沒有太多的新東西,而最關鍵的問題是如何把邏輯中各個分支都能測試到,使測試真正起到為軟體品質保駕護航的作用。

最新内容請見作者的github頁:http://qaseven.github.io/