2017-09-18 49 views
5

我試圖測試服務類(負責調用庫層,如果需要做一些操作),基本上,這是我想要測試模擬與我測試的同一類中的方法調用,它真的是代碼味道嗎?

class CarServiceImpl{ 
    public Car findById(String id){ 
     //call repository layer to find a car 
    } 

    public void deleteById(String id){ 
     Car car = this.findById(id); 
     if(car != null){ 
      //Call repository layer to update the car 
     }else{ 
      Throw NotFOundException(); 
     } 
    } 
} 

正如你可以看到我的班調用deleteById方法的findById方法,所以我的問題是。

  1. 真的是代碼異味調用同一類的方法嗎?我不認爲我應該創建一個單獨的課程來通過id找到一輛車。

  2. 我怎麼能嘲笑的「deleteById」方法調用「findById」,如果我用Mockito.when(carServiceImpl.findById("car1")).thenReturn(carModel); 它劇照調用該方法,所以我就需要模擬調用程序存儲庫通過ID也發現,即使當我已經測試了findById方法。

+0

把'findById'操作留給'deleteById'的調用者怎麼辦?如果any1傳遞一個不存在的id,倉庫將會拋出一個合適的異常,並且你的'CarServiceImpl'可以在你的應用程序理解的東西中捕獲和轉換(如果需要)這個異常。 – pedromss

回答

4

這不一定是一種氣味,你可以模擬部分像Car如此:

String carId = "..."; 
Car car = ...; 

CarServiceImpl car = mock(CarServiceImpl.class); 
when(car.findById(carId)).thenReturn(car);  
when(car.deleteById(carId)).thenCallRealMethod(); 

,如果你可以讓deleteById()執行「真正的方法」,那麼您的測試必須已有一個倉庫,在這種情況下讓findById()成爲「真正的呼叫」非常簡單,並且可以在不增加成本的情況下提高測試覆蓋的質量。您已經測試findById()的事實並不意味着您不應該不應該作爲deleteById()的一部分間接再次進行測試。

我會建議你做一個或兩個以下:給予它嘲笑repository和使用嘲笑的期望和驗證,以測試它的所有方法

  • 功能/驗收

    • 單元測試Car測試Car通過給它一個真實的存儲庫,並使用底層商店的真實調用來聲明其每種方法的實際結果

    On a單獨的說明,我猜想將註冊庫注入域對象的想法是故意使用「活動記錄」模式,您的實體知道如何刪除自己。這可能被認爲是一種代碼味道;它違反了SRP並且可能被認爲是一個糟糕的問題分離,因爲域對象知道兩件事:它自己的狀態和如何堅持自己。

  • +0

    不錯的答案,並留下一點空間,我也;-) – GhostCat

    1

    您希望您的測試設置和測試代碼儘可能「簡約」。從這個意義上說,另一個答案是正確的說:如果你可以測試deleteById()沒有一個特殊的設置,那就去那。

    它會變成那個findById()是本身需要大量的測試設置的一個「巨大」的事情 - 那我寧願退一步考慮把這種方法的內容轉換成一個不同類 - 某種的CarIdentityService

    含義:很多時候我們開始製作測試代碼更復雜 - 更好的答案是退一步,改變我們的生產代碼的設計。在你的情況下,你可能想要將整個findById()代碼推入一個不同的類 - 現在你可以簡單地模擬找到您的CarService類中的對象。

    而只是爲了記錄:CarIdentityService可能是CarService的本地或內部類。但是介紹它可能會讓你簡化實施並避免進入間諜行業。

    +0

    好的詳細說明如何重構查找器調用。 – glytching

    +0

    感謝您的快速復出;-) – GhostCat

    +0

    感謝您的答案夥計們,當我需要將代碼分離到另一個類時的一個很好的例子,但認爲我堅持只爲這個例子的一個類 –

    相關問題