eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Course – Summer Sale 2026 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

Course – Summer Sale 2026 – NPI (cat=Baeldung)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

Course – LJU – NPI (tag = JUnit)
announcement - icon

Master the most popular testing framework for Java, through the Learn JUnit course:

>> LEARN JUNIT

1. Overview

Gray box testing helps us build adequate test coverage without testing every possible scenario.

In this tutorial, we’ll examine that approach and how to exercise it using the Orthogonal Array Testing (OAT) technique.

Finally, we’ll identify the advantages and disadvantages of using gray box testing.

2. What Is Gray Box Testing?

First, let’s compare white vs. black box testing approaches, and then understand gray box testing.

White box testing refers to testing parts of an algorithm fully known by us. Hence, we can test all paths of that algorithm. And because of that, white box testing might produce a high number of test scenarios. 

Black box testing means testing the external perspective of the application. In other words, we don’t know anything about the algorithm implemented, and it’s harder to test all paths of it. Thus, we focus on validating a limited number of test scenarios.

Gray box testing uses limited information, typically available in white box testing. Then, it uses a black box testing technique to produce the test scenarios with the available information.

Thus, we end up with fewer test scenarios than white box testing. However, those scenarios cover more functionalities than black box testing.

Thus, gray box testing is a mix of a black box testing techniques and white box testing knowledge.

3. Exercising Gray Box Testing

In this section, we’ll exercise gray box testing using the OAT technique on a commission calculator demo application.

3.1. Create the System Under Test

Before testing, let’s first create an application to calculate a salesperson’s average commission based on four attributes:

  • Salesperson Level – L1, L2, or L3
  • Contract Type – Full-time Commissioned, Contractor, or Freelancer
  • Seniority – Junior, Mid, Senior
  • Impact of the sales made – Low, Medium, High

To achieve that, let’s create the SalaryCommissionPercentageCalculator class to address the above requirements:

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
    }
}

The code above defines four enums to map the salesperson’s attributes. Each enum contains a bonus field representing the commission percentage of each attribute.

The calculate() method uses a double primitive stream to compute the average of all percentages.

Finally, we round the average result to two decimal places using the setScale() method from the BigDecimal class.

3.2. Brief Introduction to the OAT Technique

The OAT technique is based on Taguchi’s Design experiment proposed by Dr. Genichi Taguchi. That experiment allows us to consider only a subset of all input combinations to test interactions between variables. 

The idea is to consider only two-factor interactions between variables’ values and ignore repeated interactions when building experiments. That means each variable’s value interacts precisely once with another variable’s value in the subset of experiments. This will become clear when we build the test scenarios.

The variables and their values are used to construct Orthogonal Arrays. An Orthogonal Array is an array of numbers where each row represents a unique combination of variables. The columns represent the individual variable that can take on one of several values.

We can represent Orthogonal Arrays as val^var, where val is the number of values they assume, and var is the number of input variables. In our case, we have four variables where each assumes three values. Thus, val equals 3, and var equals 4.

Finally, the correct Orthogonal Array is the 3^4, also known as “L9: 3-level 4-factor” in Taguchi’s Design.

3.3. Get the Orthogonal Array

The computation of an Orthogonal Array can be too complex and computationally expensive. For that reason, designers of OAT tests typically use a list of already mapped arrays. Hence, we can use that catalog of arrays to find the correct one. In our case, the correct array in the catalog provided is the L9 3-level 4-factor array:

Scenario # var 1 var 2 var 3 var 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

The table above contains two additional headers we’ve added to the 3^4 orthogonal array. The header in the first row defines the variables, whereas the first column defines the test scenario number.

Notably, among all scenarios, two values only interact with each other once. We don’t repeat the same pair of values in other scenarios. For instance, the pair where var1=val1 and var2=val1 only appears in the first test scenario.

3.4. Map the Variables and Their Values

Now, we must substitute the variables and their values to our orthogonal array in the same order they appear in the code. So, for example, the var 1 corresponds to the first enum defined, Level, where the val 0 below Level is its first value, L1.

After mapping all variables, we get the filled-in table below:

Scenario # Level Type Seniority SalesImpact
1 L1 FULL_TIME_COMMISSIONED JR LOW
2 L1 CONTRACTOR SR MEDIUM
3 L1 FREELANCER MID HIGH
4 L2 FULL_TIME_COMMISSIONED SR HIGH
5 L2 CONTRACTOR MID LOW
6 L2 FREELANCER JR MEDIUM
7 L3 FULL_TIME_COMMISSIONED MID MEDIUM
8 L3 CONTRACTOR JR HIGH
9 L3 FREELANCER SR LOW

Each row of the table above corresponds to one test scenario using the values of each corresponding cell.

3.5. Configuring JUnit 5

The main focus of this article is to exercise gray box testing using the OAT gray box technique. So, for simplicity, we’ll use straightforward unit tests to illustrate it.

First, we need to configure JUnit 5 in our project. To do so, let’s add its latest dependency, junit-jupiter-engine, to our pom.xml file:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.11.0-M2</version>
    <scope>test</scope>
</dependency>

3.6. Create the Test Class

Let’s define the SalaryCommissionPercentageCalculatorUnitTest class:

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)
        );
    }
}

To understand what’s happening, let’s break down the code.

The test method uses JUnit 5 Parameterized Tests with the @MethodSource annotation to use a method as an input data provider.

provideReferenceTestScenarioTable() provides the same data in the Orthogonal Array of section 3.4. as a Stream of arguments. Each Argument.of() call corresponds to a test scenario and the calculate() call’s expected result.

Finally, we call calculate() using the provided parameters and use assertEquals() to compare the actual with the expected result.

4. Pros and Cons of Gray Box Testing

In our example, the total number of permutations for the input of calculate() is 81. We used OAT to reduce that number to 9 while maintaining good test coverage. 

Trying all input combinations may become difficult if the input size becomes too big. For example, in a system with 10 variables and 10 values, the total number of scenarios would be 10 e10. Testing such a high number of scenarios is impractical. We can reduce that number by using OAT and avoid a combinatorial explosion of the input.

Therefore, the main advantage of the OAT technique is that it improves test code maintainability and development speed without losing test coverage. 

On the other hand, the OAT technique and gray box testing generally have the downside of not covering all possible input permutations. Thus, we might miss an essential test scenario or a problematic edge case.

5. Conclusion

In this article, we’ve examined the OAT technique to understand gray box testing.

Using this technique, we drastically reduced the number of test scenarios. However, we must properly evaluate when to use it as we might miss important edge cases.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – Summer Sale 2026 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

Course – Summer Sale 2026 – NPI (All)
announcement - icon

Yes, we're now running our only Summer Sale. All Courses are 30% off until 20th July, 2026:

>> EXPLORE ACCESS NOW

eBook Jackson – NPI EA – 3 (cat = Jackson)