簡體   English   中英

Spock測試中的模擬Java類未執行

[英]Mocked java class in spock test is not being executed

我正在嘗試使用spock框架對一些Java類進行單元測試。 結構如下:

  • com.myorg.requests (類名: RequestProcessor
  • com.myorg.query (類名: DatabaseQuery

第一類看起來像這樣:

public class RequestProcessor {

   private String request;

   public RequestProcessor(String aRequest) {
      this.request = request;
   }

   public String processRequest() {
       String response ;
       //do something here

       try {
           if(condition meets) {
              response = executeRequest();
           }
       } catch ( various exceptions... ) {
           System.out.println("something went wrong...");
       }
   }

   private String executeRequest() throws <<exceptions thrown by DatabaseQuery>> {
       //do something here
       DatabaseQuery queryResult = new DatabaseQuery(request)
   }
}

我正在嘗試為此RequestProcessor類編寫一個Spock測試,它依賴於DatabaseQuery 我正在考慮模擬DatabaseQuery類,以便簡單地RequestProcessor測試RequestProcessor類。

正在調用RequestProcessorprocessRequest()方法,該方法依賴於另一個私有方法。 該方法將使用DatabaseQuery獲取實際的查詢結果。 這是我的Spock測試的樣子:

class RequestProcessorSpec extends Specification {

    //Class to be tested
    RequestProcessor requestProcessor

    //Dependencies
    DatabaseQuery dbquery

    def "Given a valid request, dbquery's executeQuery method is called" () {
        given: "a valid request"
            def queryRequest = '{"info1":"value1","info2":"value2","query":"select * from users"}'

        and: "mock the DBQuery class"
            dbquery = Mock(DatabaseQuery)

        and: "create a new request"
            requestProcessor = new RequestProcessor(queryRequest)

        when: "the request is processed"
            requestHandler.processRequest()

        then: "dbquery executeQuery method is called"
            1 * dbquery.executeQuery(_ as String)
    }
 }

這並不適合我。 我收到一個錯誤:

  • 調用太少
  • 無與倫比的調用

當我使用gradlew test --info運行測試以獲取更多結果時,我看到控制台上打印了一條日志,該日志由processRequest方法中的try-catch語句捕獲。

我在這里做錯了什么?

示例代碼中的問題

首先,即使我簡化了示例代碼並創建了自己的虛擬DatabaseQuery類,您的示例代碼也無法正常工作,因為這里至少有三個錯誤:

  • 在構造函數中,您具有this.request = request (自我分配),但應為this.request = aRequest;
  • 在測試中,您具有requestHandler.processRequest() ,但應為requestProcessor.processRequest()
  • 方法executeRequest()不返回指定的String 所以我只能推測,實際上它在DatabaseQuery調用了另一個方法,以便將查詢結果轉換為String

為什么模擬不起作用?

弄清楚了這一點之后,讓我們看看您的測試真正存在的根本錯誤。

我在這里做錯了什么?

假定本地變量中的模擬對應用程序代碼中的其他本地變量某種程度上有效。 為了使用模擬,您需要將其注入到測試中的類中。 但是,與許多開發人員一樣,您並不是通過依賴注入來設計去耦和可測試性,而是在內部創建依賴關系(在本例中為DatabaseQuery對象)。

測試還有什么問題?

我認為您的測試只會遭受規格過高的困擾。 您為什么要檢查是否從私有方法中調用了另一個類中的特定方法(間接)? 您沒有直接測試私有方法,而是通過調用公共方法覆蓋了它們的代碼,而您的測試已經做到了。

如果您想掩蓋catch ,只需確保您的請求導致正確的例外。 也許您需要為此模擬數據庫連接,並確保它將預期結果返回到DatabaseQuery 為了准確地說,我看不到足夠多的代碼。

原始問題的技術解決方案

現在,讓我們假設無論我之前說什么,您都絕對要檢查此交互。 您需要執行的操作取決於情況(您的代碼中未顯示):

無論如何,您都需要使DatabaseQuery可注入。 您可以將一個成員和另一個設置器添加到您的班級。

現在,您將有一個分支,這取決於從以下位置進行交互dbquery.executeQuery(_ as String)位置(稱為):

  1. 如果從DatabaseQuery外部調用該方法,則可以注入普通的Mock
  2. 如果從DatabaseQuery內部調用該方法,則您需要注入一個Spy因為模擬不會調用其他內部方法(如原始對象),因為-好的,它只是一個模擬。

情況1:從外部調用executeQuery(String)

package de.scrum_master.query;

public class DatabaseQuery {
  private String request;

  public DatabaseQuery(String request) {
    this.request = request;
  }

  public String executeQuery(String request) {
    return request.toUpperCase();
  }

  public String getResult() {
    return executeQuery(request);
  }
}
package de.scrum_master.requests;

import de.scrum_master.query.DatabaseQuery;

public class RequestProcessor {
  private String request;
  private DatabaseQuery databaseQuery;

  public RequestProcessor(String aRequest) {
    this.request = aRequest;
    databaseQuery = new DatabaseQuery(request);
  }

  public String processRequest() {
    return executeRequest();
  }

  private String executeRequest() {
    return databaseQuery.executeQuery(request);
    //return databaseQuery.getResult();
  }

  public void setDatabaseQuery(DatabaseQuery databaseQuery) {
    this.databaseQuery = databaseQuery;
  }
}
package de.scrum_master.requests

import de.scrum_master.query.DatabaseQuery
import spock.lang.Specification

class RequestProcessorTest extends Specification {
  //Class to be tested
  RequestProcessor requestProcessor

  //Dependencies
  DatabaseQuery dbquery

  def "Given a valid request, dbquery's executeQuery method is called" () {
    given: "a valid request"
    def queryRequest = '{"info1":"value1","info2":"value2","query":"select * from users"}'

    and: "mock the DBQuery class"
    dbquery = Mock(DatabaseQuery)
    //dbquery = Spy(DatabaseQuery, constructorArgs: [queryRequest])

    and: "create a new request"
    requestProcessor = new RequestProcessor(queryRequest)
    requestProcessor.databaseQuery = dbquery

    when: "the request is processed"
    requestProcessor.processRequest()

    then: "dbquery executeQuery method is called"
    1 * dbquery.executeQuery(_ as String)
  }
}

現在,該測試工作包括交互檢查。

情況2:從自己的類內部調用executeQuery(String)

您是否在RequestProcessorRequestProcessorTest看到兩條注釋掉的行? 只需使用它們並注釋掉其他兩個即可,如下所示:

  private String executeRequest() {
    //return databaseQuery.executeQuery(request);
    return databaseQuery.getResult();
  }
    and: "mock the DBQuery class"
    //dbquery = Mock(DatabaseQuery)
    dbquery = Spy(DatabaseQuery, constructorArgs: [queryRequest])

該測試仍然有效,包括交互檢查。

當然,我必須偽造一些東西,並填寫您沒有提供的缺少的拼圖塊,但這基本上就是它的工作原理。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM