Let’s look at a few of the primitive (simple, raw values that you can use in variables without constructing an object to house them) types that Java supports. Then, let’s add our new byte-adding method in test-driven-development fashion.
The integer-based (no decimal point value) primitive Java types are:
| Primitive Type | Minimum Value (Inclusive) | Maximum Value (Inclusive) |
|---|---|---|
| byte | -128 | 127 |
| short | -32,768 | 32,767 |
| int | -2^31 | 2^31 – 1 |
| long | -2^63 | 2^63 – 1 |
Additional Info:
Let’s update our application so that it can add two byte values, returning the result.
Parameterized Unit Test – CsvSource
If we add the import org.junit.jupiter.params.provider.CsvSource; to the imports section at the top of the codingchica.java101.AppTest class, then we can provide comma separated values (csv) in a String for each individual test. This allows us to specify more than one input parameter for the test.
Let’s add a new nested class to test the new method we plan to add to the main code:
/**
* Unit tests for the add method.
*
* @see App#add(byte, byte)
*/
@Nested
class AddBytesTest {
@ParameterizedTest
@CsvSource({"1,2,3", "0,0,0", "-1,1,0"})
void add_whenInvoked_thenReturnsExpectedResult(byte value1, byte value2, byte expectedResult) {
// Setup
// Execution
byte actualResult = App.add(value1, value2);
// Validation
assertEquals(expectedResult, actualResult, () -> String.format("%s+%s=%s", value1, value2, expectedResult));
}
@ParameterizedTest
@CsvSource({"127,1,-128", "127,2,-127",})
void add_whenInvokedWithOverflow_thenReturnsIntegerWrapAroundValues(byte value1, byte value2, byte expectedResult) {
// Setup
// Execution
byte actualResult = App.add(value1, value2);
// Validation
// TODO - Add logic for error condition rather than returning successfully.
assertEquals(expectedResult, actualResult, () -> String.format("%s+%s=%s", value1, value2, expectedResult));
}
@ParameterizedTest
@CsvSource({"-128,-1,127", "-128,-2,126",})
void add_whenInvokedWithUnderflow_thenReturnsIntegerWrapAroundValues(byte value1, byte value2, byte expectedResult) {
// Setup
// Execution
byte actualResult = App.add(value1, value2);
// Validation
// TODO - Add logic for error condition rather than returning successfully.
assertEquals(expectedResult, actualResult, () -> String.format("%s+%s=%s", value1, value2, expectedResult));
}
}
In the following snippet, there will be 2 separate tests (those in the quoted strings) and call to the test method will provide 3 input parameters (the comma-separated values within the quoted string):
@CsvSource({"-128,-1,127", "-128,-2,126",})
void add_whenInvokedWithUnderflow_thenReturnsIntegerWrapAroundValues(byte value1, byte value2, byte expectedResult) {
The input parameters are provided to the test method in the same order as defined in the CsvSource annotation, so we need to be consistent.
The Test Scenarios
This is the first time we have more than one test inside our Nested test class for a given method. What scenarios are we testing?
- add_whenInvoked_thenReturnsExpectedResult – These are the normal addition problems you would expect in a math class. This is a happy-path scenario, where everything goes according to plan.
- add_whenInvokedWithOverflow_thenReturnsIntegerWrapAroundValues – In this case, the value that results from the addition is too large to store in the space allowed, so when we return the result, it looks successful, but the value returned represents the answer if we wrapped around and started counting up over again at the minimum byte value. Although we expect the method to complete successfully (for now) this is an error-path scenario, as we are returning a value that doesn’t make sense.
- add_whenInvokedWithUnderflow_thenReturnsIntegerWrapAroundValues – In this case, the value that results from the addition is too small to store in the space allowed. When we return the result, although it appears to have run successfully, the value returned is as if we wrap around to the largest value and keep counting down from there. This is another error-path scenario. We will also revisit it in the future. For now, we have a couple of TODO comments, so we don’t forget.
Making the App Compile
Let’s add the new method to the codingchica.java101.App code, but let’s setup a failure scenario for the time being, so we can test our new unit test’s failure output.
/**
* Add two byte values.
*
* @param byteValue1 The first value to add.
* @param byteValue2 The second value to add.
* @return A byte value representing the two values added together.
*/
public static byte add(byte byteValue1, byte byteValue2) {
return 0;
}
Confirming Test Failure
One of the reasons I like to use the Maven build for these tests is so I make sure:
- That Maven sees the test and runs it as part of the build, and
- The error message returned would be sufficient to do any troubleshooting needed if this test ever fails in in a build server.
If we run the Maven build now, we should see output like:
[ERROR] Failures:
[ERROR] AppTest$AddBytesTest.add_whenInvokedWithOverflow_thenReturnsIntegerWrapAroundValues:88 127+1=-128 ==> expected: <-128> but was: <0>
[ERROR] AppTest$AddBytesTest.add_whenInvokedWithOverflow_thenReturnsIntegerWrapAroundValues:88 127+2=-127 ==> expected: <-127> but was: <0>
[ERROR] AppTest$AddBytesTest.add_whenInvokedWithUnderflow_thenReturnsIntegerWrapAroundValues:101 -128+-1=127 ==> expected: <127> but was: <0>
[ERROR] AppTest$AddBytesTest.add_whenInvokedWithUnderflow_thenReturnsIntegerWrapAroundValues:101 -128+-2=126 ==> expected: <126> but was: <0>
[ERROR] AppTest$AddBytesTest.add_whenInvoked_thenReturnsExpectedResult:75 1+2=3 ==> expected: <3> but was: <0>
Not all of the tests failed. A couple of the add_whenInvoked_thenReturnsExpectedResult scenarios expected 0, so they succeeded, while the “1,2,3” scenario fails, as expected. This is good enough to ensure that Maven is running our tests and the failure messages like: 1+2=3 look good.
Casting
For now, we want to see the wrapping behavior in action, so we’re going to force Java to return the byte type, even though it wants to return the larger int type. This is called casting. We can do so with compatible primitive types by surrounding the desired type in parenthesis.
Here, we also surround the addition problem in parenthesis to force it to happen first and then we do the casting of the result.
Let’s update the add method’s logic:
return (byte) (byteValue1 + byteValue2);
This is what causes the error scenarios in our unit tests, but again, we want to see that behavior in action, so we understand it…at least for now.
Maven Build
Now, if we rerun the Maven build, it should show success.
Commit
Let’s commit our current updates. They’re not perfect, we know we have work left to do, but we will be building upon them in the next post.

Leave a comment