2014-09-06 166 views
2

在我的應用程序中,我們的設計中有兩層:API和操作。單元測試 - 我應該如何測試這種設計?

1. 操作執行代碼的「真實」邏輯,例如:驗證用戶,檢索書籍信息,通知用戶他的書已被查看。

許多API都可以使用相同的操作。

2. APIs由用戶執行:它們接收參數,然後根據API的邏輯執行各種操作。

例如:ViewBookAPI:

class BookApis 
{ 
/** 
    * authenticateUserOperation, retreiveBookOperation, informUserBookViewOperation 
    * are injected to this class. (Dependency Injection) 
*/ 
public function viewBookApi($bookId, $accessToken) 
{ 
    $internalUserId = $this->authenticateUserOperation($accessToken); 

    $book = $this->retrieveBookOperation($bookId, $internalUserId); 

    $this->informUserBookWasViewedOperation($book->getOwnerUserId(), $bookId); 

     return $book->getContent(); 
    } 
} 

我應該如何測試這個設計?

1.如果我測試API,那麼我將不得不重複使用相同操作的API的相同測試。

2.如果我測試操作,我所要做的就是驗證API是否正確使用操作。

但是如果一個錯誤的對象被注入到API會怎麼樣?那麼沒有測試會失敗。

非常感謝。

回答

2

您的設計很常見(也是如此),所以我有點驚訝這個問題不斷出現。

有兩種類型的測試,你需要在這裏:

  1. 集成測試 - 確保與該API調用啓動,並與業務層做它的工作結束時,流程工作正常
  2. 單元測試 - 測試每個API層中的類,以及作爲操作層的

集成測試是相當自解釋性的(如果沒有,讓我知道,我會詳細說明),所以我猜你指的是單元測試。兩個不同的圖層需要進行不同的測試。

的操作層:

在這裏,我們嘗試檢查,這樣做實際工作的類工作。這意味着你應該實例化你正在測試的類,爲它提供輸入,並檢查它提供的輸出是否符合你的期望。

假設你有一類這個排序:

public class OperationA { 
    public int multiply(int x, int y) { 
     return x * y; 
    } 
} 

檢查它,你希望將意味着編寫一個測試,如(測試用例本身只是一個例子,不必太認真)什麼:

public class OperationATest { 
    @Test 
    public void testMultiplyZeroByAnyNumberResultsInZero() { 
     OperationA op = new OperationA(); 

     assertEquals(0, op.multiply(0, 0)); 
     assertEquals(0, op.multiply(10, 0)); 
     assertEquals(0, op.multiply(-10, 0)); 
     ... 
    } 

    @Test 
    public void testMultiplyNegativeByNegativeResultsInPositive() { 
     ... 
    } 

    ... 
} 

的API層:

在這裏,我們試圖檢查類使用權來自操作層的類以正確的順序執行正確的操作。爲了做到這一點,你應該使用mock,並使用嘲笑的verify操作。

假設你有一類這個排序:

public class API_A { 

    private OperationA op; 

    public API_A(OperationA op) { 
     this.op = op; 
    } 

    public int multiplyTwice(int a, int b, int c) { 
     int x = op.multiply(a, b); 
     int y = op.multiply(x, c); 

     return y; 
    } 
} 

檢查它你希望(使用的Mockito語法)編寫一個測試,如將意味着什麼:

public class API_A_Test { 
    @Test 
    public void testMultiplyTwiceMultipliesTheFirstNumberByTheSecondAndThenByTheThird() { 
     OperationA op = mock(OperationA.class); 
     when(op.multiply(12, -5)).thenReturn(60); 
     API_A api = new API_A(op); 

     api.multiply(12, -5, 0); 

     verify(op).multiply(12, -5); 
     verify(op).multiply(-60, 0); 
    } 

} 
+0

爲什麼沒有評論投票呢? – eitanfar 2014-10-12 05:41:25

1

一般來說,好像你需要兩層測試。

首先,您需要測試基本構建塊 - 操作。例如,你應該用一個有效的標記測試authenticateUserOperation,使用一個無效的標記和一個NULL

測試完所有操作後,您可以繼續測試API的邏輯。這裏的想法並不是將測試代碼加倍,而是要測試只有的商業邏輯。這可以通過使用已知行爲的mockinginjecting操作來實現,並檢查API如何處理它們。 例如,您可以創建一個總是失敗的模擬authenticateUserOperation,並在使用它時測試viewBookApi返回NULL(或引發異常)。這樣你只測試API如何處理操作的結果而不用重複測試。

2

我覺得這裏有矛盾。 看來你有

  • 操作=邏輯的可重複使用的塊
  • 的API =委託運營+腳本的操作調用

從這一點,我會說你需要自從 開始測試*操作本身可能正確執行。僅僅進行API測試可能會導致多個客戶端/測試失敗,造成單個錯誤 *即使操作測試可能通過,API也可能不會調用所需的(和已實現的)操作。

所以:

  1. 試驗操作 - 驗證所述可重複使用塊
  2. 試驗的API - 驗證邏輯內部到的API +檢查是否調用正確的操作(可以使用嘲笑)。您唐't複製/測試這裏的操作內部的邏輯。
  3. 我通常不會測試依賴注入 - 因爲不太可能有人僞造合作者。 (除非是滲透測試儀)。儘管如此,你也可以爲它寫一個測試。
1

據我瞭解你的第一個問題,你不可能只通過單元測試來覆蓋這個(就像你'標記'它)。也許你必須考慮使用Pairwise testing爲你的測試邏輯創建。這將支持對操作& API之間的交互的依賴性。

有關的錯誤對象被注入到涉及DI之後的API,這使得您的單元測試更容易一些。由於組件是鬆耦合,您可以使用虛擬模擬類來充當所需的組件。通過這樣做 - 您的代碼將非常靈活。也許這張圖將幫助您:

enter image description here

但我不認爲這是你正在尋找的唯一案例。所以我可以建議你在測試代碼中包含Decorator design pattern,並且它的概念爲API-操作關係動態地爲測試對象附加額外的職責。

1

您應該執行兩套測試在這裏。單元測試和集成測試

1)單元測試 - 應該爲每個操作方法編寫這組測試,以確保特定的方法按預期工作。現在方法可以有不同的路徑(如果其他/異常)。您應該編寫多個單元測試來驗證每條路徑。由於它是一個單元測試,你應該模擬所有外部調用(調用發生在其他類),這將確保你在該函數中所做的所有事情都是正確的。由於您在api類中沒有任何邏輯,因此無需爲它們編寫單元測試。

2)集成測試 - 編寫集成測試以進行實際api調用。這些測試將驗證您的端到端場景(api,操作)。

如果您有任何持久性/存儲庫類,您應該爲它們編寫單獨的測試。