簡體   English   中英

Java中調用私有方法等對象的公有方法單元測試

[英]Unit Test public method that calls private method and other objects in Java

我是單元測試的新手,最近嘗試了 JUnit 測試和 Mockito。

我正在嘗試對調用多個私有方法並創建其他類的私有對象的方法進行單元測試。

我如何對該方法進行單元測試。

例如,如果我有以下代碼:

class ClassToTest {
    private OuterClass outerClass;
    public Boolean function() {
        outerClass = new OuterClass(20);
        return innerFunction(outerClass.getValue());
    }

    private Boolean innerFunction(int val) {
        if (val % 2 == 0)
            return true;
        return false;
    }
}

我很困惑如何測試公共 function。

方法的實現方式無關緊要。 該方法應遵循的合同, 就是您要通過測試驗證的內容。 在此示例中,如果outerClass的值是偶數, function()應該返回true 做到這一點的一種方式是注入(通入ClassToTest構造函數)的實例outerClass這樣就可以控制值進行測試時:

@Test
public void trueWhenEven() {
    var outer = new OuterClass(2);
    var ctt = new ClassToTest(outer);
    assertTrue(ctt.function());
}

有時,契約只是方法調用其他一些對象上的方法。 在這種情況下,您可以使用Mockito或類似的庫來驗證交互。

您正在直接進行一些非平凡的問題來開始單元測試。

問題1:如何處理實現細節/私有功能? 答:單元測試是關於發現代碼中的錯誤,這是單元測試(以及大多數其他類型的測試)的主要目標之一。 另一個主要目標是通過在軟件更改時充當回歸測試來防止引入錯誤。 錯誤存在於實現中-不同的實現帶有不同的錯誤。 因此,請務必測試實現細節。 此處提供支持的一個重要工具是覆蓋率分析,它可以向您顯示測試已達到實現代碼的哪些部分。

您甚至可以測試功能約定以外的方面:a)負面測試是有意檢查無效/未指定輸入行為的測試,對於確保系統安全非常重要。 因為,即使提供了無效的輸入,系統也不應因為例如讀取或寫入越界內存而被黑客入侵。 但是,這可能不適用於您的示例,因為您的方法最有可能被指定為實現“總計函數”而不是“部分函數”。 b)甚至可以執行超出當前實現所需的測試(如果可以訪問)的實現細節。 這樣做可以防止在即將對組件進行的更改中出現錯誤,例如API擴展。

但是,還有單元測試的次要目標。 其中之一是避免在實現細節更改時不必要地破壞測試。 達到次要目標的一種方法是通過公共API測試實現細節。 這樣,對實現細節的某些重新設計不會破壞您的測試:重命名,拆分或合並私有功能不會影響測試。 但是,切換到其他算法可能會需要您重新考慮測試:對於fibonacci函數的迭代/遞歸實現的測試與使用Moivre / Binet的閉合形式表達式的實現看上去不同,或者查找表的實現。

對於您的示例,這意味着您應該嘗試通過公共API測試私有功能的功能。

問題2:如何處理對軟件其他部分的依賴性? 單元測試的重點是發現小的孤立代碼段中的錯誤。 當這些代碼段與其他代碼段具有依賴性時,這可能會對您正確進行單元測試的能力產生負面影響。 但是,是否確實如此取決於實際的依賴性。 例如,如果您的代碼使用Math.sin()函數,那么這也是對其他代碼部分的依賴,但是這種依賴通常不會損害您正確測試代碼的能力。

在以下情況下,對其他組件的依賴關系使您感到困擾:使用其他組件使在被測代碼中激發所有有趣的場景變得困難。 或者,使用其他組件會導致不確定的行為(時間,隨機性,...)。 或者,使用其他組件會導致構建或執行時間過長。 或者,其他組件有故障或什至不可用。

如果不滿足所有這些條件(通常是Math.sin()函數的情況),則通常可以將其他組件作為測試的一部分來使用。 但是,您應該記住,在單元測試中,您仍將重點放在代碼中的錯誤上,並且不要開始編寫實際測試其他組件的測試:請記住,其他組件具有自己的測試。

在您的示例中,您選擇了Outerclass具有一些看似微不足道的功能。 在這種情況下,您可以只在其余測試中使用Outerclass。 但是,從您的角度來看,這只是一個例子-根據上述標准,真正的其他課堂實際上可能會令人不安。 如果真是這樣,那么您將必須以某種方式來管理該依賴關系,而這一切都需要以某種方式實現易於測試的設計。

這里有一整套方法,因此您最好在網上搜索“可測試性設計”和“控制反轉”。 並且,您還應該嘗試了解單元測試和集成測試的區別:這將幫助您避免嘗試將單元測試應用於應該通過集成測試進行測試的代碼部分。

通常,對於Mockito,這將需要使用依賴注入,然后您將為測試注入OuterClass的模擬。

如果您真的想在不添加Spring類型框架的情況下進行測試,則可以考慮3種選擇:

1)使其成為集成測試並測試所有內容的真實實例

2)更改您的代碼,以便通過Setter或構造函數中傳入的Object創建OuterClass,然后為測試傳遞模擬

3)更改private OuterClass outerClass; protected OuterClass outerClass; 並確保您的測試包結構與實際代碼包結構相同,然后可以執行outerClass = Mockito.mock(OuterClass); 在您的測試設置中。

我能夠測試我的公共方法,該方法又調用了一個私有方法。 我使用了 springframework 的 ReflectiontestUtils。 找到下面的例子。

@Service
public class ClassTobeTested{
    
   @Autowired
   private SomeService someService;

   @override
   public String methodToTest(String arg){
      // some task
      someLogic(arg);
      //some task
      return "Success";
   } 

   private Boolean someLogic(String arg){
      return someService.performLogic(arg);
   }

}

測試

import org.springframework.test.util.ReflectionTestUtils;

public Testing{

   @InjectMocks
   ClassTobeTested classTobeTested;
   @Mock
   SomeService someService;

   @Test
   public testmethod{
      when(ReflectiontestUtils.invokeMethod(classTobeTested, "someLogic", "abc")).thenReturn(true);
      String str = classTobeTested.methodToTest("abc");
      assertEquals(str,"Success");
   }

}

暫無
暫無

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

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