All articles
Everything published so far
Grouped by track, in reading order. Not sure where to start? Choose a track instead.
- You Already Think Like a ProgrammerFor manual testers moving into automation: the test case you've written a hundred times is already a program in Java — the only thing that changes is who executes it.
- How Java Actually RunsA .java file becomes OS-neutral bytecode; the JVM runs it on your laptop and on Jenkins the same way — which is why JDK, JRE, and JVM show up in every automation interview.
- Set Up Your JDK and IDETwo installs make you able to run Java: OpenJDK 17 and an IDE. Then create the project you will type — not paste — your first automated check into.
- Your First Program Is an Automated CheckSkip Hello World. Your first Java program compares expected vs actual and prints PASS or FAIL — the same shape as the last step of every test case you already write.
- Break It on PurposeA compiler error is a defect report — where, what, and position. Finding it is good news, not a verdict on your ability. That is the skill that keeps people in automation.
- Choosing a Type Is Writing a SpecDEF-8801 looks like a wallet bug: 0.1 + 0.2 is not 0.3. Reproduce it in five lines of Java — the root cause is the type you chose, not broken application code.
- A Variable Is a Labeled BoxDeclaration, initialization, and assignment are three different moves. Java refuses to let you read a local variable you never set — the same policy a good QA lead has for untrusted test data.
- Five Types That MatterJava has eight primitives. Automation work mostly needs five — plus String for text. Four questions choose the type; int is the everyday default when every answer is no.
- Boundary-Test Your TypesYou do not trust a spec until you test its edges. int overflows in silence; int divided by int can zero a pass-rate report. Boundary analysis applies to the language too.
- Where Data Lives — and Why null Crashes SuitesPrimitives hold values; references hold arrows. null is an arrow to nothing — and following it is the NullPointerException every automation console has shown you a thousand times.
- Booleans Are the Shape of Every VerdictEvery test case ends in a judgment. In Java that judgment is a boolean — true or false, nothing else — and every assertion library is built on that one fact.
- if, else, and the = vs == TrapAn if only accepts a boolean. = orders; == asks. Put = inside an if on a boolean and your suite can run on a red build — with no compiler error and no stack trace.
- And, Or, Not — and Why Order Saves You from NPEReal verdicts combine conditions. && short-circuits: check null first, then use the thing. Reverse the order and you get the NullPointerException Chapter 2 taught you to read.
- Chains, Order, and Decimal ToleranceAn else if chain stops at the first true branch — wrong order creates dead code and wrong ratings. And never compare doubles with ==; compare the difference against a tolerance.
- switch, Fall-Through, and the Arrow FormA classic switch without break launches three browsers from one name. The arrow form cannot fall through — and Bug Hunt #3 shows three silent defects that still print PASS.
- Why Loops ExistForty-seven bad passwords, one tired glance at attempt thirty-four — that is why automation exists. A for loop is how you tell Java to do the check again without copying the steps.
- Looping Over Real Test DataA counter is useful; the everyday job is walking passwords, browsers, and rows. Start at zero, stop before .length, and use for-each when you do not need the index.
- while and do-whileWhen you only know 'until what,' use while — with a safety limit so a retry cannot become a hang. Use do-while when you must check at least once, like polling a service.
- Off-by-One, Infinite Loops, break and continuei <= length reaches a box that is not there. A while with no update never stops. break leaves the loop; continue skips one pass — fail-fast versus skip-row.
- Nested Loops and the Silent Average BugA loop inside a loop is a test matrix: browsers across environments. Then a hunt with two believable numbers, three silent defects, and zero crashes.
- Arrays Are Fixed BoxesFor four chapters you've trusted String[] and int[] on faith. Here's the whole definition — fixed-size, numbered, one type — and why that rigidity is the point.
- The Running-Value PatternSumming, counting, and finding the slowest page load are the same loop, walked once — and it's the pattern that fixes the average bug from Chapter 4.
- Why Arrays Can't Grow, and Lists CanCollecting failing test IDs during a run means you don't know the count in advance — the exact situation an array can't handle, and a list was built for.
- List Operations, and the Choice That Matterssize, get, contains, remove, isEmpty — the five list operations you'll use most — plus the one-line rule for choosing an array over a list, or the reverse.
- The Hidden Zero and the Case-Sensitive SearchOne program, two clean-looking results, both wrong: a padded array quietly skews an average, and a contains check fails on a value that's clearly there.
- What == Really ComparesChapter 3's == trap and Chapter 5's case-sensitive miss were both warnings. Here's the full picture: == asks whether two variables point at the same object.
- new String and the Real TrapAlmost every string your tests actually judge is built at runtime — read from a file, returned by an API — and none of those come from the pool that made == look safe.
- Put the Known Value FirstCalling .equals on a variable that might be null crashes with a NullPointerException. Calling it on a literal never can — so the literal goes first, always.
- equalsIgnoreCase, and When Case MattersChapter 5 left a defect open: a search for "logintest" missed "LoginTest". Use .equals when case matters and .equalsIgnoreCase when it doesn't — a real decision.
- The Hidden Crash and the Wrong VerdictOne program, two confident verdicts, both wrong: a false failure from ==, and a false denial from a case-sensitive equals nobody meant to be exact.
- One Object Instead of Three ArraysA test result is a name, a status, and a duration that belong together. Three parallel arrays hold them apart, drifting silently; a class keeps each result whole.
- Constructors, this, and Your First MethodA constructor builds an object fully formed at new. this is how you tell a field apart from a parameter that shares its name — get it wrong, and the field stays empty.
- toString and Private FieldstoString turns a memory code into a readable result log. private plus a getter turns an open field into a door you control — the exact shape of every Page Object.
- equals and hashCode for Your Own TypesTwo Defect objects with identical data still count as unequal until you say otherwise — the promise opened in Chapter 6, and the reason a defect list can catch duplicates.
- Basing equals on Identity, Not EverythingCompare every field in equals and an edited defect stops being recognized as the same defect. Base it on what defines identity — usually a stable id — not on what changes.
- The Missing this, and the Silent NullA constructor that compiles and runs, prints one line, and quietly loses two of its three fields to null — because of a bug that has bitten every Java programmer once.
- Naming a Job So You Can Reuse ItYou've typed public static void main(String[] args) since page one on faith. A method gives a name to one job, written once — the same idea as a reusable test step — and this is where that line starts to make sense.
- Return Values, and the Early ExitA void method does something; a method with a return type answers something — and judging is answering, which is exactly what makes return values useful for testers.
- What a Method Can (and Cannot) ChangeJava always passes a copy of what's in the box. A helper that collects failures into a list you passed in will work; one that tries to reset a counter you passed in never will.
- static, and One Name for Several Shapesstatic means belongs to the class, not any one object — which is why Counter.total is shared while each object counts only its own. Java also lets several methods share one name, as long as their parameters differ.
- Promise Note #1, Paid in Fullpublic static void main(String[] args) — the line you typed on faith in Chapter 1 — decoded one word at a time, now that every piece of it is something you already know.
- The Discarded Return and the Reset That Never HappensOne program, two clean-looking lines, both silently wrong: a cleaned string that was never saved, and a reset that can't reset a primitive its caller passed in.
- An Exception Is a Defect Report, Not a CrashAn environment goes down mid-run, a data file has a blank cell, an API returns 503 — these aren't rare, they're Tuesday. An exception is Java's controlled report when one happens, and a stack trace is that report's best evidence.
- try, catch, and Where You Wrap ItWrapping the smallest unit of work in try/catch lets a data-driven run survive one bad row and keep checking the rest — but only if the try sits inside the loop, not around it.
- Catching the Right Thing, in the Right OrderMultiple catch blocks run most-specific-first, exactly like an else-if chain. And catching plain Exception everywhere isn't tidy error handling — it's error hiding.
- finally: The Cleanup That Always RunsA finally block runs on both paths, success or failure, which is why every automation framework closes its browser there. Skip it, and one failed test leaves a process running that dooms the next forty.
- Throwing Your Own: Checked and UncheckedThrowing your own exception with an expected-versus-actual message is the core of every assertion library. Checked vs. unchecked is just outside-your-control versus a bug in your own logic.
- The Empty Catch That Still Printed PASSThis program runs cleanly and reports a healthy result. It's lying, and it's the most dangerous lie in the chapter: a run that skipped a third of its data and called it a pass.
- Map: Look Up a Value by Its KeyA config maps an environment name to a URL. A run maps each test name to its result. A Map looks up a value by a unique key — and a missing key quietly returns null instead of raising an error.
- Keys Are Unique, and put OverwritesThere is no 'add a second entry under the same key.' Putting a key that already exists silently replaces its value — and getOrDefault is the clean way to handle a key that might be missing.
- Walking a Map, and the Order You Can't Rely OnA HashMap gives no order guarantee at all, so an assertion that depends on it is testing an internal detail of Java, not your application — and it will fail one day for reasons you can't reproduce.
- Set: A Collection That Refuses DuplicatesA Set holds each item once, and add returns false to report a duplicate — exactly what a crawler or a dedup check needs, without a separate lookup first.
- Why hashCode Was Not OptionalThe same equals, the same two defects, and one missing hashCode method is the entire difference between a Set size of 1 and a duplicate defect filed twice.
- The Overwritten Entry and the Case-Sensitive KeyThis program prints three lines, and every one is wrong — and not one of the three mistakes is new. They're Chapter 5's uniqueness, Chapter 6's equality, and Chapter 6's identity, resurfacing in a new container.
- extends: One Class Built on Anotherclass LoginTest extends BaseTest — the first line of nearly every test class in the world. A subclass receives the fields and methods of its parent and may use them as its own.
- super, and the Parent That Goes FirstConstructors aren't inherited, so a subclass constructor must arrange for the parent's constructor to run first. That's super — and interviewers ask about the order for a reason.
- Overriding, and Your First Sight of PolymorphismJava picks a method from the object's actual class, not the variable's declared type. That's polymorphism — and @Override is what turns a broken attempt at it into a compile error instead of a silent bug.
- Overriding Versus OverloadingThese two words look alike and mean different things. Change a parameter type even slightly and you haven't overridden a method — you've silently added a second one, and the original still answers every call.
- protected, and the Object Behind Every Classprotected is the access level a base class needs to share with its children without opening itself to everyone. And every class you've ever written already extends Object, whether you typed extends or not.
- The Overload That Never OverrodeThis subclass wrote three things meant to replace the parent's behavior. Not one of them took effect — and the object is a CheckoutTest in every single line.
- An Interface Is a ContractAn interface lists method signatures with no bodies — the promise without the implementation. Sign the contract and skip a method, and the compiler refuses to build.
- One Script, Every BrowserA loop that calls methods on a Browser never mentions Chrome, Firefox, or Edge by name — because the method that runs is chosen from the actual object at the moment of the call, not the declared type.
- Why List on the Left Finally Makes SenseList<String> x = new ArrayList<>() is the exact same idea as the famous WebDriver line, copied on faith since Chapter 5 and now fully understood.
- Default Methods, and the Abstract Class ChoiceA default method gives every implementer a body for free, but an interface still can't hold fields or a constructor — which is exactly the line that separates it from an abstract class.
- The Real Selenium HierarchySearchContext, WebDriver, RemoteWebDriver, and ChromeDriver are ordinary Java — two interfaces and two classes. Reading that hierarchy answers 'why is WebDriver an interface?' for good.
- The instanceof Ladder That Broke PolymorphismThis program compiles and runs, and prints two lines instead of three. One whole browser is missing from the report — and both defects quietly convert a flexible design back into a rigid one.
- Packages: Folders With MeaningA package becomes part of a class's real name, and grouping classes by what they do — tests, pages, utils, models — is what lets a newcomer guess where anything lives.
- The Four Access Levelsprivate, package-private, protected, and public are four widening rings. The habit worth building is starting at private and widening only when something needs the access.
- Constants: Naming the Values You Keep Retypingstatic final fixes a value's name to one place, and a final class with a private constructor tells the compiler that a constants holder is never meant to become an object.
- Enums: A Fixed Set the Compiler Can CheckA String accepts any text, so a typo compiles and falls silently to a default. An enum has a fixed set the compiler checks, and a bad value is rejected loudly instead of guessed at.
- The Magic String That Matched by CoincidenceThis program prints four lines, and three of them expose a defect — one per idea in this chapter. Organization isn't decoration; every one of these is a bug that structure would have prevented.
- The String Methods You Will Actually UseThe page says Total: Rs 1,299.00. Your expected value is the number 1299. Cleaning a scraped value into something you can assert on is usually a short pipeline of chained calls, not one method.
- Strings Never ChangeA String is immutable, so every method that looks like it modifies one actually returns a new one — which is why an ignored trim() does nothing, and why a loop wants a StringBuilder instead of +.
- split, and the Dot That Consumes Everythingsplit takes a regular expression, not a plain separator, so splitting on an unescaped dot matches every character and leaves nothing behind. It fails quietly — no error, just an array that's wrong.
- Light Regular Expressionsmatches and replaceAll cover most of what a tester needs from regular expressions — validating an id's shape, or stripping everything that isn't a digit.
- Converting Text SafelyA safe conversion checks for blank, catches the parse failure, and reports every value it couldn't use. The same lesson from a different angle: parse text into the type you actually mean, and compare that.
- The Ignored trim() and the Silent ZeroThis program prints four lines. Three are wrong, and the fourth is right for the wrong reason — none of the four are new mistakes, they're every text habit in this chapter, used carelessly once each.
- Reading and Writing a Filetry-with-resources closes anything AutoCloseable automatically, on both the normal and exception paths — so there's no finally block left to forget.
- Configuration in a Properties FileA properties file supplies the values that change per environment, and a missing key returns null exactly like a Map — which is why the two-argument fallback form is almost always what you want.
- CSV Rows Into ObjectsA CSV loader is Chapters 5, 7, and 14 meeting at once — split the row, build the object, collect the list. The payoff is data.getPassword() instead of data[1].
- The Missing File That Fails LoudlyA missing test-data file is a setup failure, not an empty list. A suite that reports zero rows and prints green has checked nothing — and that's worse than a suite that fails.
- Excel and JSON: Honest NotesWhatever the format — properties, CSV, Excel, JSON — the discipline is the same: read it inside try-with-resources, convert values carefully, turn rows into objects, and fail loudly when the file is missing.
- The Empty List That Reported GreenThis program prints three calm lines, and the last one is the most dangerous output in this book: no data, suite passes.
- A Lambda Is Behavior You Can Pass AroundUntil now, everything you passed to a method was data. A lambda lets you pass behavior — a small block of code — as an argument, and its type is always a functional interface.
- Where You Have Already Met ThisA hand-written waitUntil that takes a condition as a lambda and re-evaluates it until true isn't an analogy for a Selenium explicit wait — it's the actual mechanism, built from scratch.
- Streams: A Pipeline Over a CollectionA stream pipeline with no terminal operation does nothing at all — no error, no output. filter and map only describe a step; nothing runs until collect, count, or another terminal operation asks for a result.
- Optional: A Value That Might Not Be ThereOptional makes a possibly-missing value visible to the compiler and the reader instead of letting it arrive as null at some distant, unrelated line.
- When a Loop Is Still BetterStreams reward small, named steps and punish long anonymous ones. A one-line pipeline a colleague must decode at midnight is worse than four honest lines of loop.
- The Filter That Never RanThis program compiles and runs, and reports zero failures when there was one — because a filter with no terminal operation isn't a bug that crashes, it's a pipeline that silently never executes.
- Generics: Type Safety Before Run TimeList<String> rejects a wrong-typed value at compile time. The raw List it replaced accepts anything and fails as a ClassCastException at 3 a.m. instead.
- Writing Your Own Generic MethodA type parameter like <T> lets one method serve any type with no casting at the call. Type erasure is the honest limit: that type information is gone by run time.
- The Bug: One Static Driver, Two TestsA static field is one box for the whole program, not one per test. Two parallel tests writing to it corrupt each other with no error, no exception, and a symptom that looks exactly like flakiness.
- ThreadLocal: One Value Per ThreadThreadLocal keeps the field shared but gives each thread its own value inside it. Skipping remove() in teardown hands the next pooled test a dead reference and a slow memory leak.
- Thread Safety Is a Design PropertyShared mutable state is what makes code unsafe under threads. Break either half — make it unshared with ThreadLocal, or make it immutable — and put the driver behind an accessor before you ever need to.
- The Shared Driver and the Lost IncrementThe last Bug Hunt in the book. A DriverManager that looks like the real thing, with four defects — one traceable to a chapter you've already read.
- Interview Questions: Java Basics and SyntaxEight questions every switcher-from-manual interview asks in some form. For each: a weak answer that isn't wrong, a strong answer that names the mechanism, and where in this book to go re-run the code.
- Interview Questions: Objects and ClassesSix questions on classes, constructors, encapsulation, this, equals/hashCode, and toString — the weak answer everyone gives, and the strong one that names the mechanism and cites a real bug you've seen.
- Interview Questions: CollectionsEight questions on arrays vs lists, List/Set/Map, HashMap order, and contains() — the answer that names the mechanism (buckets, equals, iteration order) rather than just naming the class.
- Interview Questions: StringsSix questions on String comparison, immutability, StringBuilder, safe number conversion, and the split(".") trap — the strong answers a tester gives after actually running the code once.
- Interview Questions: OOPSeven questions on the four OOP concepts, extends, overloading vs overriding, why WebDriver is an interface, and access modifiers — with a real framework example for each, not just a definition.
- Interview Questions: Exceptions, Modern Java, and the Switch ItselfTen closing questions: checked vs unchecked, finally, empty catch blocks, missing data files, lambdas, streams, Optional — and the two questions every manual-to-automation switcher gets asked directly.
- How HashMap actually works internallyBuckets, hashing, collisions and treeification — and why the follow-up question catches people out.