Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
130: Property-Based Testing Concepts in Java
Most of us have a habit of writing tests based on the examples we already have in our heads. We think, "If I pass 'Hello World' into my slug generator, it should return 'hello-world'." We write that test, it passes, and we move on. I've spent a fair amount of my career shipping code that passed every single single-case test I wrote, only to have it crash in production because a user entered a string containing a newline character or a non-breaking space.
public String toSlug(String input) {
// A simple implementation to turn a title into a URL-friendly slug
return input.toLowerCase().trim().replaceAll("\\s+", "-");
}
The Illusion of Coverage
If I were testing this the traditional way, I'd probably write three or four tests: a standard string, a string with multiple spaces, and maybe an empty string. They would all pass. I'd feel confident. But here is the problem: we aren't testing the logic; we're testing our own ability to guess where the code might break. We are missing the "unknown unknowns."
What happens if the input is null? What happens if the string contains emojis, punctuation, or control characters? In the code above, a null input triggers a NullPointerException immediately. A string like "Hello World!!!" becomes "hello-world!!!", which isn't actually a clean URL slug. My examples were too "happy."
Letting the Machine Find the Edge Cases
This is where Property-Based Testing (PBT) changes the game. Instead of picking specific inputs, we define a property—a rule that must hold true for any possible input. For our slug generator, a property might be: "The output should never contain spaces, regardless of the input string."
In Java, using a library like jqwik, we don't write a @Test; we write a @Property. We tell the framework, "Generate a thousand random strings, including the weird ones, and try to break this rule."
@Property
void slugShouldNeverContainSpaces(@ForAll String input) {
String result = toSlug(input);
Assertions.assertFalse(result.contains(" "), "Slug should not contain spaces");
}
The moment I run this, the framework doesn't just give me a "Pass." It hammers the method with nulls, tabs, carriage returns, and Unicode whitespace. It will find that null input in milliseconds and fail the test. It forces me to stop thinking about "examples" and start thinking about "invariants."
Hardening the Slug Generator
To fix this, I need to stop relying on a simple regex and start handling the actual constraints of the property. I need to ensure the input is handled safely and that any character that isn't a lowercase letter, a number, or a hyphen is stripped out.
public String toSlug(String input) {
if (input == null) {
return "";
}
String normalized = input.toLowerCase().trim();
// Replace all non-alphanumeric characters with hyphens
String slug = normalized.replaceAll("[^a-z0-9]+", "-");
// Remove leading or trailing hyphens that might have been created
return slug.replaceAll("^-+|-+$", "");
}
Now, when the PBT tool runs a thousand random strings—including strings of just punctuation or strings with mixed whitespace—the property "should never contain spaces" holds true. More importantly, the property "should only contain lowercase letters, numbers, and hyphens" also holds true.
The Power of Shrinking
One thing I love about PBT that you won't get with random fuzzing is shrinking. If the tool finds a massive, 500-character string that breaks your code, it doesn't just hand you that giant mess. It automatically tries to find the smallest possible input that still causes the failure.
If a string with a weird combination of a Tab, a Newline, and a Cyrillic character breaks your logic, the tool will "shrink" it down. It might tell you, "I found a failure with this 500-character string, but the simplest version that still fails is actually just a single \t (tab)." This saves you from spending an hour debugging a massive input string to figure out which specific character caused the crash.
📋 Practical Task
Implement a Robust Symmetric Array Shuffler
You are tasked with writing a method that shuffles an array of integers. While "randomness" is hard to test, a key property of a shuffle is that the resulting array must contain exactly the same elements as the original array, just in a different order (this is called a permutation).
Your Task:
- Create a class
ArrayUtilswith a methodpublic int[] shuffle(int[] input). - Write a property-based test (conceptually or using a framework like jqwik) that asserts the following property: "For any integer array, the sorted version of the shuffled array must be identical to the sorted version of the original array."
- Ensure your
shufflemethod handlesnullinputs and empty arrays without crashing. - Verify that your implementation doesn't accidentally lose or duplicate elements during the shuffle process.
There are no comments for now.