使用 OAT 技術的灰盒測試
一、概述
灰盒測試幫助我們建立足夠的測試覆蓋率,而無需測試所有可能的場景。
在本教程中,我們將研究該方法以及如何使用正交陣列測試 (OAT) 技術來練習它。
最後,我們將確定使用灰盒測試的優點和缺點。
2. 什麼是灰盒測試?
首先,讓我們比較白盒和黑盒測試方法,然後了解灰盒測試。
白盒測試是指測試我們完全已知的算法的一部分。因此,我們可以測試該算法的所有路徑。因此,白盒測試可能會產生大量測試場景。
黑盒測試意味著測試應用程序的外部視角。換句話說,我們對實現的算法一無所知,而且更難測試它的所有路徑。因此,我們專注於驗證有限數量的測試場景。
灰盒測試使用有限的信息,通常在白盒測試中可用。然後,它使用黑盒測試技術來生成具有可用信息的測試場景。
因此,我們最終得到的測試場景少於白盒測試。但是,這些場景涵蓋的功能比黑盒測試更多。
因此,灰盒測試是**黑盒測試技術和白盒測試知識的結合**。
3. 進行灰盒測試
在本節中,我們將使用 OAT 技術對佣金計算器演示應用程序進行灰盒測試。
3.1.創建被測系統
在測試之前,我們先創建一個應用程序,根據四個屬性來計算銷售人員的平均佣金:
- 銷售人員級別 – L1、L2 或 L3
- 合同類型——全職委託、承包商或自由職業者
- 資歷——初級、中級、高級
- 銷售額的影響——低、中、高
為此,讓我們創建SalaryCommissionPercentageCalculator
類來滿足上述要求:
public class SalaryCommissionPercentageCalculator {
public BigDecimal calculate(Level level, Type type,
Seniority seniority, SalesImpact impact) {
return BigDecimal.valueOf(DoubleStream.of(
level.getBonus(),
type.getBonus(),
seniority.getBonus(),
impact.getBonus(),
type.getBonus())
.average()
.orElse(0))
.setScale(2, RoundingMode.CEILING);
}
public enum Level {
L1(0.06), L2(0.12), L3(0.2);
private double bonus;
Level(double bonus) {
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
}
public enum Type {
FULL_TIME_COMMISSIONED(0.18), CONTRACTOR(0.1), FREELANCER(0.06);
// bonus field, constructor and getter
}
public enum Seniority {
JR(0.8), MID(0.13), SR(0.19);
// bonus field, constructor and getter
}
public enum SalesImpact {
LOW(0.06), MEDIUM(0.12), HIGH(0.2);
// bonus field, constructor and getter
}
}
上面的代碼定義了四個enum
來映射銷售人員的屬性。每個enum
包含一個bonus
字段,表示每個屬性的佣金百分比。
calculate()
方法使用雙原始流來計算所有百分比的平均值。
最後,我們使用BigDecimal
類中的setScale()
方法將平均結果四捨五入到小數點後兩位。
3.2. OAT技術簡介
OAT 技術基於Genichi Taguchi 博士提出的田口設計實驗。該實驗允許我們僅考慮所有輸入組合的一個子集來測試變量之間的相互作用。
這個想法是在建立實驗時只考慮變量值之間的雙因素相互作用,而忽略重複的相互作用。這意味著每個變量的值與實驗子集中的另一個變量的值精確交互一次。當我們構建測試場景時,這將變得清晰。
變量及其值用於構造正交數組。正交數組是一個數字數組,其中每一行代表一個唯一的變量組合。這些列表示可以採用多個值之一的單個變量。
我們可以將正交數組表示為val^var,
其中val
是它們假定的值的數量, var
是輸入變量的數量。在我們的例子中,我們有四個變量,每個變量假設三個值。因此, val
等於3
, var
等於4
。
最後,正確的正交陣列是3^4,
在 Taguchi 的設計中也稱為“L9:3 級 4 因子”。
3.3.獲取正交數組
正交陣列的計算可能過於復雜且計算量大。出於這個原因,OAT 測試的設計者通常使用已映射數組的列表。因此,我們可以使用該數組目錄來找到正確的數組。在我們的例子中,提供的目錄中的正確數組是 L9 3 級 4 因子數組:
設想 # | 變量1 | var 2 | var 3 | 變種4 |
1個 | val 1 | val 1 | val 1 | val 1 |
2個 | val 1 | val 2 | val 3 | val 2 |
3個 | val 1 | val 3 | val 2 | val 3 |
4個 | val 2 | val 1 | val 3 | val 3 |
5個 | val 2 | val 2 | val 2 | val 1 |
6個 | val 2 | val 3 | val 1 | val 2 |
7 | val 3 | val 1 | val 2 | val 2 |
8個 | val 3 | val 2 | val 1 | val 3 |
9 | val 3 | val 3 | val 3 | val 1 |
上表包含我們添加到3^4
正交數組的兩個額外標題。第一行的標題定義變量,而第一列定義測試場景編號。
值得注意的是,在所有場景中,兩個值只交互一次。我們不會在其他場景中重複同一對值。例如, var1=val1
和var2=val1
這對僅出現在第一個測試場景中。
3.4.映射變量及其值
現在,我們必須按照它們在代碼中出現的相同順序將變量及其值替換到我們的正交數組中。因此,例如, var 1
對應於定義的第一個enum
Level
,其中Level
下面的val 0
是它的第一個值L1
.
映射所有變量後,我們得到下面的填充表:
設想 # | 等級 | 類型 | 資歷 | 銷售影響 |
1個 | L1 | FULL_TIME_COMMISSIONED | JR | 低的 |
2個 | L1 | 承包商 | SR | 中等的 |
3個 | L1 | 自由職業者 | 中 | 高的 |
4個 | L2 | FULL_TIME_COMMISSIONED | SR | 高的 |
5個 | L2 | 承包商 | 中 | 低的 |
6個 | L2 | 自由職業者 | JR | 中等的 |
7 | L3 | FULL_TIME_COMMISSIONED | 中 | 中等的 |
8個 | L3 | 承包商 | JR | 高的 |
9 | L3 | 自由職業者 | SR | 低的 |
上表的每一行對應一個測試場景,使用每個對應單元格的值。
3.5.配置 JUnit 5
本文的主要重點是使用 OAT 灰盒技術進行灰盒測試。因此,為簡單起見,我們將使用簡單的單元測試來說明它。
首先,我們需要在項目中配置 JUnit 5。為此,讓我們將其最新的依賴項junit-jupiter-engine添加到我們的pom.xml
文件中:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
3.6.創建測試類
讓我們定義SalaryCommissionPercentageCalculatorUnitTest
類:
class SalaryCommissionPercentageCalculatorUnitTest {
private SalaryCommissionPercentageCalculator testTarget = new SalaryCommissionPercentageCalculator();
@ParameterizedTest
@MethodSource("provideReferenceTestScenarioTable")
void givenReferenceTable_whenCalculateAverageCommission_thenReturnExpectedResult(Level level,
Type type, Seniority seniority, SalesImpact impact, double expected) {
BigDecimal got = testTarget.calculate(level, type, seniority, impact);
assertEquals(BigDecimal.valueOf(expected), got);
}
private static Stream<Arguments> provideReferenceTestScenarioTable() {
return Stream.of(
Arguments.of(L1, FULL_TIME_COMMISSIONED, JR, LOW, 0.26),
Arguments.of(L1, CONTRACTOR, SR, MEDIUM, 0.12),
Arguments.of(L1, FREELANCER, MID, HIGH, 0.11),
Arguments.of(L2, FULL_TIME_COMMISSIONED, SR, HIGH, 0.18),
Arguments.of(L2, CONTRACTOR, MID, LOW, 0.11),
Arguments.of(L2, FREELANCER, JR, MEDIUM, 0.24),
Arguments.of(L3, FULL_TIME_COMMISSIONED, MID, MEDIUM, 0.17),
Arguments.of(L3, CONTRACTOR, JR, HIGH, 0.28),
Arguments.of(L3, FREELANCER, SR, LOW, 0.12)
);
}
}
要了解發生了什麼,讓我們分解代碼。
測試方法使用 JUnit 5 Parameterized Tests 和@MethodSource
註釋來使用方法作為輸入數據提供者。
provideReferenceTestScenarioTable()
在3.4.
作為參數Stream
。每個Argument.of()
調用對應一個測試場景和calculate()
調用的預期結果。
最後,我們使用提供的參數調用calculate()
並使用assertEquals()
將actual
結果與expected
結果進行比較。
4. 灰盒測試的優缺點
在我們的示例中, calculate()
輸入的排列總數為81
。我們使用 OAT 將這個數字減少到 9,同時保持良好的測試覆蓋率。
如果輸入大小變得太大,嘗試所有輸入組合可能會變得困難。例如,在具有 10 個變量和 10 個值的系統中,場景總數為 10 e10。測試如此多的場景是不切實際的。我們可以通過使用 OAT 來減少這個數字,避免輸入的組合爆炸。
因此, OAT 技術的主要優點是在不損失測試覆蓋率的情況下提高了測試代碼的可維護性和開發速度。
另一方面, OAT 技術和灰盒測試通常具有無法涵蓋所有可能的輸入排列的缺點。因此,我們可能會錯過一個基本的測試場景或一個有問題的邊緣案例。
5.結論
在本文中,我們研究了 OAT 技術以了解灰盒測試。
使用這種技術,我們大大減少了測試場景的數量。但是,我們必須正確評估何時使用它,因為我們可能會錯過重要的邊緣情況。
與往常一樣,示例代碼在 GitHub 上可用。