0
0
JUnittesting~15 mins

@NullSource and @EmptySource in JUnit - Deep Dive

Choose your learning style9 modes available
Overview - @NullSource and @EmptySource
What is it?
@NullSource and @EmptySource are annotations in JUnit 5 used to provide special input values to parameterized tests. @NullSource supplies a null value as a test argument, while @EmptySource provides empty values like empty strings, empty collections, or arrays. They help test how code behaves with null or empty inputs without writing separate test cases manually.
Why it matters
Testing with null or empty inputs is crucial because many bugs happen when code does not handle these cases properly. Without these annotations, developers might forget to test these edge cases or write repetitive code. Using @NullSource and @EmptySource makes tests cleaner, easier to write, and ensures important scenarios are covered automatically.
Where it fits
Learners should first understand basic JUnit tests and parameterized tests using @ValueSource or @CsvSource. After mastering these, they can use @NullSource and @EmptySource to handle special edge cases. Later, they can explore more complex sources like @MethodSource or custom argument providers.
Mental Model
Core Idea
These annotations automatically inject null or empty values into parameterized tests to check how code handles missing or empty inputs.
Think of it like...
It's like testing a door by trying to open it with no key (null) or an empty keychain (empty) to see if it still works safely.
Parameterized Test Input Sources
┌───────────────┐
│ @ValueSource   │ → normal values (e.g., strings, ints)
├───────────────┤
│ @CsvSource     │ → multiple values per test
├───────────────┤
│ @NullSource    │ → null value only
├───────────────┤
│ @EmptySource   │ → empty values ("", [], {})
└───────────────┘

Test runs once per input from these sources.
Build-Up - 7 Steps
1
FoundationBasics of Parameterized Tests
🤔
Concept: Parameterized tests run the same test method multiple times with different inputs.
In JUnit 5, you can write a test method and annotate it with @ParameterizedTest. Then, you provide inputs using annotations like @ValueSource. For example, testing if strings are not empty by passing several strings.
Result
The test runs once for each input value, checking the same logic repeatedly.
Understanding parameterized tests is key because @NullSource and @EmptySource are special input providers that extend this concept.
2
FoundationHandling Null and Empty Inputs Manually
🤔
Concept: Before these annotations, testers had to write separate tests for null or empty inputs.
You might write one test method for normal inputs and separate ones for null or empty strings. This leads to repetitive code and risks missing edge cases.
Result
Tests become longer and harder to maintain, and some edge cases might be forgotten.
Recognizing this pain point explains why @NullSource and @EmptySource were introduced.
3
IntermediateUsing @NullSource for Null Input Testing
🤔Before reading on: do you think @NullSource can be combined with other sources like @ValueSource? Commit to your answer.
Concept: @NullSource injects a single null argument into a parameterized test, allowing easy testing of null inputs.
Annotate a parameterized test method with @NullSource. The test will run once with null as the argument. You can combine it with other sources like @ValueSource to test null and normal values together.
Result
The test method runs with null input, helping verify null handling without extra test methods.
Knowing that @NullSource can combine with other sources helps write concise tests covering both normal and null cases.
4
IntermediateUsing @EmptySource for Empty Input Testing
🤔Before reading on: do you think @EmptySource works only for strings or also for collections and arrays? Commit to your answer.
Concept: @EmptySource provides empty values like empty strings, empty collections, or empty arrays as test inputs.
Annotate a parameterized test with @EmptySource. The test runs once with an empty string if the parameter is String, or empty collection/array if the parameter type matches. This tests how code handles empty inputs.
Result
The test method runs with empty inputs, ensuring code handles these edge cases gracefully.
Understanding that @EmptySource adapts to parameter types prevents confusion and helps test various empty cases easily.
5
IntermediateCombining @NullSource and @EmptySource
🤔Before reading on: do you think combining @NullSource and @EmptySource runs tests for both null and empty inputs separately? Commit to your answer.
Concept: You can use both annotations together to run tests with null and empty inputs in one method.
Annotate a parameterized test with both @NullSource and @EmptySource. The test runs twice: once with null and once with empty input. This covers two important edge cases in one place.
Result
Tests run for both null and empty inputs, improving coverage and reducing code duplication.
Knowing this combination simplifies testing edge cases and keeps tests clean and maintainable.
6
AdvancedLimitations and Type Constraints
🤔Before reading on: do you think @EmptySource can provide empty values for any parameter type? Commit to your answer.
Concept: @EmptySource only works with parameter types that have a clear empty value like String, Collection, Map, or Array. It cannot provide empty values for custom types automatically.
If you use @EmptySource on unsupported types, tests will fail to run. You must ensure the parameter type matches supported empty values or use custom argument providers.
Result
Tests fail with type mismatch errors if used incorrectly, guiding you to correct usage.
Understanding type constraints prevents confusing errors and helps choose the right source for your tests.
7
ExpertCustomizing Null and Empty Inputs in Complex Tests
🤔Before reading on: do you think @NullSource and @EmptySource can be extended or customized for complex objects? Commit to your answer.
Concept: For complex or custom types, you cannot use @NullSource or @EmptySource directly; instead, you create custom argument providers or combine with @MethodSource for tailored inputs.
You write a method that returns null or empty instances of your custom class and annotate the test with @MethodSource. This approach gives full control over test inputs beyond simple null or empty values.
Result
Tests cover complex edge cases with precise control, improving robustness in real-world scenarios.
Knowing when and how to extend beyond built-in sources is essential for professional-grade testing.
Under the Hood
@NullSource and @EmptySource are meta-annotations that provide special arguments to parameterized tests by implementing the ArgumentsProvider interface internally. When JUnit runs a parameterized test, it collects all argument sources, calls their providers, and runs the test method once per argument. @NullSource returns a single null argument, while @EmptySource returns an empty value matching the parameter type. They integrate seamlessly with other sources by merging argument streams.
Why designed this way?
These annotations were designed to reduce boilerplate and human error in testing null and empty inputs, which are common edge cases. Instead of forcing developers to write separate tests or custom providers, JUnit offers these simple, declarative annotations. The design favors composability, allowing them to combine with other sources for flexible test coverage.
Test Execution Flow
┌─────────────────────────────┐
│ Parameterized Test Method    │
│ with @NullSource/@EmptySource│
└─────────────┬───────────────┘
              │
              ▼
┌─────────────────────────────┐
│ JUnit collects argument sets │
│ from all sources             │
└─────────────┬───────────────┘
              │
              ▼
┌─────────────────────────────┐
│ @NullSource provides [null]  │
│ @EmptySource provides [empty]│
│ Other sources provide values │
└─────────────┬───────────────┘
              │
              ▼
┌─────────────────────────────┐
│ Test runs once per argument  │
│ (null, empty, others)        │
└─────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does @NullSource provide multiple null values or just one? Commit to your answer.
Common Belief:Some think @NullSource provides multiple null arguments or repeats null multiple times.
Tap to reveal reality
Reality:@NullSource provides exactly one null argument per test run, not multiple nulls.
Why it matters:Expecting multiple nulls can lead to confusion about test coverage and cause incorrect assumptions about how many times tests run.
Quick: Can @EmptySource provide empty values for any parameter type? Commit to your answer.
Common Belief:Many believe @EmptySource works for all parameter types, including custom classes.
Tap to reveal reality
Reality:@EmptySource only works for String, Collection, Map, and Array types with known empty values.
Why it matters:Using @EmptySource on unsupported types causes test failures, wasting time debugging type errors.
Quick: Can @NullSource and @EmptySource be used without @ParameterizedTest? Commit to your answer.
Common Belief:Some think these annotations can be used on regular tests to inject null or empty values.
Tap to reveal reality
Reality:They only work with @ParameterizedTest because they provide arguments for repeated test runs.
Why it matters:Misusing them leads to tests that don't run or fail silently, causing false confidence.
Quick: Does combining @NullSource and @EmptySource run tests with both inputs separately? Commit to your answer.
Common Belief:Some believe combining them merges null and empty into a single test run argument.
Tap to reveal reality
Reality:JUnit runs separate test invocations for each argument: one with null, one with empty.
Why it matters:Understanding this prevents confusion about test counts and coverage.
Expert Zone
1
When combining @NullSource and @EmptySource with other sources, the order of arguments is the concatenation of all sources, which can affect test reporting and debugging.
2
@EmptySource adapts its empty value based on the parameter type, so the same test method can test empty strings, empty lists, or empty arrays without code changes.
3
Using @NullSource and @EmptySource together helps catch subtle bugs where code treats null and empty inputs differently, which is a common source of production errors.
When NOT to use
Avoid using @NullSource and @EmptySource when testing complex objects or when you need specific empty states beyond default empty values. Instead, use @MethodSource or custom ArgumentsProvider implementations to supply tailored test data.
Production Patterns
In professional codebases, these annotations are often combined with @ValueSource to cover normal, null, and empty inputs in one test method. Teams use them to enforce consistent edge case testing and reduce duplicated test code. They are also used in continuous integration pipelines to catch regressions related to null or empty inputs early.
Connections
Defensive Programming
Builds-on
Testing null and empty inputs with @NullSource and @EmptySource supports defensive programming by ensuring code safely handles unexpected or missing data.
Input Validation
Same pattern
Both concepts focus on handling edge cases in inputs to prevent errors, making tests with these annotations a practical way to verify validation logic.
Boundary Value Analysis (Software Testing)
Builds-on
Null and empty inputs are classic boundary values; using these annotations automates testing these critical boundaries.
Common Pitfalls
#1Using @NullSource on a test method without @ParameterizedTest.
Wrong approach:@NullSource void testMethod(String input) { // test code }
Correct approach:@ParameterizedTest @NullSource void testMethod(String input) { // test code }
Root cause:Misunderstanding that @NullSource only works with parameterized tests that run multiple times with different inputs.
#2Applying @EmptySource to a parameter type that is not supported, like Integer.
Wrong approach:@ParameterizedTest @EmptySource void testMethod(Integer input) { // test code }
Correct approach:@ParameterizedTest @ValueSource(ints = {0}) void testMethod(Integer input) { // test code }
Root cause:Assuming @EmptySource can provide empty values for all types, ignoring its limited supported types.
#3Expecting @NullSource to provide multiple null values in one test run.
Wrong approach:@ParameterizedTest @NullSource @ValueSource(strings = {"a", "b"}) void testMethod(String input) { // test code }
Correct approach:@ParameterizedTest @NullSource @ValueSource(strings = {"a", "b"}) void testMethod(String input) { // test code }
Root cause:Misunderstanding that @NullSource adds exactly one null argument, not multiple.
Key Takeaways
@NullSource and @EmptySource simplify testing of null and empty inputs in parameterized tests, reducing boilerplate and improving coverage.
They only work with @ParameterizedTest and support specific parameter types, so understanding their constraints is essential.
Combining these annotations with other sources allows concise tests covering normal, null, and empty cases together.
For complex or custom types, you need custom argument providers or @MethodSource instead of these annotations.
Using these annotations helps catch common bugs related to missing or empty data early in development.