Skip to content
Skilled.dev

Interactive tools

Edge-case practice: find tests that expose algorithm bugs

Practice choosing counterexamples for missing index zero, invalid BST descendants, cyclic graphs, Unicode assumptions, mutation, and boundary searches.

Published · 4 min read

Find the test that exposes the bug

Question 1 of 8. Explain your choice before checking.

A Two Sum implementation uses “if (seen.get(complement))” to decide whether an index exists. Which input exposes the missing result? Values are unique.

0 correct across 0 checked prompts. This records this session’s latest answer per prompt.

Read all prompts and worked answers

A Two Sum implementation uses “if (seen.get(complement))” to decide whether an index exists. Which input exposes the missing result? Values are unique.

[2, 7], target 9. The complement 2 was stored at index 0. Zero is falsy, so a truthiness check drops a valid pair. Use an explicit presence test or compare the lookup result with the missing-value sentinel.

Related practice

Product except self returns correct answers without zeros and with one zero. Which case most directly tests whether the implementation divides the total product by each value?

[0, 2, 0]. Two zero positions force every output to zero. Dividing a total product of zero by a zero entry is undefined. Prefix and suffix products handle this input without division or a special zero branch.

Related practice

A BST checker validates only immediate children. Which tree passes that local test but violates the strict global BST rule?

Root 10; left 5 with right child 12. The value 12 is greater than its parent 5 but lies in the left subtree of 10. Carry an upper bound of 10 through that whole subtree. The third choice is already rejected by an immediate-child check.

Related practice

A sum window shrinks whenever its sum exceeds target 3. Which input shows why that rule needs a nonnegative-value assumption?

[5, -2]. After seeing 5 the rule discards it, even though appending -2 would produce sum 3. Negative values break the monotone relationship between expanding the window and increasing its sum.

Related practice

A BFS forgets to record visited nodes. Which smallest directed graph can make it run forever?

A → A. A self-loop repeatedly enqueues A. Mark a node when it is first discovered, before enqueueing, so cycles and multiple incoming edges do not create unbounded repeated work.

Related practice

A first-true search works when a transition exists. Which case checks its “not found” boundary?

[false, false, false]. An all-false input must return the declared sentinel, often n for a half-open search. A test that always contains a true value cannot establish correct behavior for a missing boundary. Also test n = 0 separately.

Related practice

A function promises the number of Unicode code points in a string, but returns JavaScript string.length. Which input distinguishes UTF-16 units from code points?

"😀". This emoji occupies two UTF-16 code units and one Unicode code point. A combining sequence raises a further distinction between code points and displayed grapheme clusters. State the promised unit before selecting iteration APIs.

Related practice

A pair-search helper promises to preserve the caller’s array. Its returned indexes look valid for a sorted example. Which additional assertion checks the preservation promise?

Keep an input copy and compare the array after calling. An implementation may sort in place and still return plausible-looking values. Compare the original input with a saved copy. Then separately check distinct indexes, their original values, and the target sum.

Related practice

An edge-case test should expose a specific way an implementation could be wrong. Another random array can increase the test count without challenging a new assumption. This practice tool asks you to choose an input or assertion that distinguishes the intended contract from a plausible bug.

The eight prompts cover zero-valued indexes, zeros in products, global tree constraints, negative values in windows, graph cycles, binary-search sentinels, Unicode units, and input mutation. Check an answer, read the reasoning, and follow the related lesson. You can also open the complete prompt-and-answer list without finishing the interactive round.

Name the assumption that could fail

Begin with a statement precise enough to contradict. A map lookup might confuse a present index zero with a missing entry. A BST validator might inspect only a node's children. A search might assume its target exists. Each assumption suggests a smaller and more informative test than “try a large input.”

For Two Sum, store a matching value at position zero. If the implementation checks the retrieved index with a truthiness condition, it misses that pair. The useful property is the index, not a particular choice of large or unusual values.

Write the expected result before running the implementation. If you decide what “correct” means only after seeing its output, the test can quietly adopt the same mistaken assumption. When output order is flexible, normalize that order while preserving the required multiplicity and original-index meaning.

Distinguish local checks from global rules

A strict BST requires every value in a node's left subtree to be smaller, and every value in its right subtree to be larger. Checking only immediate children misses an invalid descendant that crosses an older ancestor's boundary.

Build a root of 10 with a left child 5 and a right child 12 beneath that 5. The child 12 is locally greater than 5, but it still belongs to the left subtree of 10. This test points directly to the missing inherited upper bound.

The same pattern appears in other tasks. A bracket sequence can have matching total counts while nesting incorrectly. A route can use valid individual edges while repeating a node indefinitely. Test the property that spans the whole structure rather than only the easiest nearby relationships.

Test boundaries that change the control flow

Empty input, a singleton, a missing answer, and a target at the first or last valid position often send execution through different branches. They are useful because of those branches, not because every function needs the same ritual checklist.

An all-false first-true predicate tests the not-found sentinel. A self-loop tests discovery marking in graph traversal. Two zeros test whether a product implementation relies on division. A single bracket tests the unfinished-stack rule. State the branch or invariant each case checks.

Inputs outside the stated contract need a separate policy. A routine promised only nonnegative values may reject negatives, or document them as unsupported. Adding an unsupported input to a test suite and expecting arbitrary behavior does not improve contract coverage. Change the contract deliberately when broader handling is useful.

Include language representation in the test model

JavaScript string length counts UTF-16 code units. A supplementary emoji occupies two such units but one Unicode code point. A displayed character can also contain several code points, as with a combining sequence. “Character count” needs an explicit unit before the expected answer can be established.

Fixed-width subtraction, multiplication, and comparison helpers can fail at numeric boundaries even when ordinary examples pass. Mutable slices and arrays can share storage in ways a copied-looking expression hides. A language-specific test should exercise the documented representation rather than borrow expectations from a different runtime.

The language preparation guides explain those concerns in each supported language. Use them to select a focused boundary case and explain why it matters to the implementation you actually wrote.

Check side effects as well as return values

A helper can return an acceptable answer and still mutate input it promised to preserve. Save an input copy, call the function, then compare the caller's original collection with that copy. Separately validate the returned answer.

For linked structures, checking only displayed values can miss identity or reachability errors. A newly allocated node with the same value may violate a task that requires a node from the original tree. For a queue, an undefined stored payload must remain distinguishable from an empty queue if both are supported.

Use the practice worksheet to record the contract, a baseline, and three assumption-driven cases. Then write the smallest failing input you can explain. After fixing the bug, keep that case as a regression test and add an independent reference for small generated inputs where practical. A quiz answer is a starting point; applying the test to your own implementation is the skill being practiced.