skip to content

When a case denies a capability permission, what should it assert beyond the application not crashing?

level: seniorimportance: should knowfreq 46%

answer

  1. Not crashing is not passing
  2. The fallback must finish the job
  3. Assert what must not happen too
  4. Reopen the screen and check for nagging
  5. Silence is the worst degraded behaviour

basics

~20 s

Assert the whole degraded journey: the fallback appears, explains what is unavailable, and can be completed to a real end state. Then assert what must not happen — no repeated prompting, no empty screen passed off as success, no dead controls.

solid answer

~50 s

"It stayed open" is not an oracle. A denial case has two halves. The **positive half** follows the alternative route to the end: the explanation renders in the product's own words, the fallback affordance is present, and using it reaches the same outcome the granted path would have reached — or, where no alternative exists, the denial is stated plainly with the one action that would fix it. The **negative half** asserts the things that must not happen: the application does not ask again every time the screen is reopened, no blank panel is presented as though it were a result, no control is visible but inert, and nothing sits waiting. Stopping at the banner proves only that a message renders; the value of the case is proving the person can still get what they came for.

code

pseudocode · 21 lines
pseudocode
case "check-in completes with position capability denied":
  arrange:
    set_stored_decision(capability = "position", value = DENIED)
    launch_app()

  act + assert (positive: the alternative finishes the job):
    open_check_in()
    expect_visible(explanation_banner)
    expect_visible(manual_place_picker)
    select_place("Central Branch")
    submit()
    expect_visible(check_in_confirmed)        # same end state as the granted path

  assert (negative: what must not happen):
    leave_screen(); open_check_in()
    expect_request_count(capability = "position") == 0
    expect_not_visible(empty_results_placeholder)
    expect_all_visible_controls_enabled()

  cleanup:
    clear_stored_decision(capability = "position")

go deeper

for a junior

Know that a denied capability still has to lead somewhere usable, and that "the application stayed open" is not a passing result. Be able to say what a good fallback screen tells the person.

for a middle

Explain the two halves of the assertion — the fallback renders and works, and the things that must not happen do not. Describe how you would prove the alternative route reaches the same outcome as the granted path.

for a senior

Show judgement about depth: how far to follow the degraded journey, which negative assertions repay their upkeep, and how the denied branch stays a first-class case rather than a smoke check bolted onto the granted one.

for a principal

Own the standard itself — what degraded behaviour every capability-gated feature must offer — so the assertion is written against an agreed product rule rather than against whatever each team happened to build.

## "It did not crash" is not an oracle The most common denial case in a real suite denies the capability, opens the screen, and asserts that some error text is visible. It goes green for years while the feature behind it is unusable. Absence of a crash is the floor of acceptable behaviour, not evidence of correct behaviour, and a case whose only assertion is that the process survived is a smoke check wearing a functional case's name. The reason this matters more for a denial than for most branches is that nobody uses the denial path by hand. The person who built the screen granted the capability on their own device months ago and has not seen the other branch since. Whatever the suite asserts about it is the only pressure the branch will ever receive. ## What the degraded branch owes the person A denial case is really asserting a product standard, so it helps to say the standard out loud: - **An explanation in the product's own words**, at the place the person was looking, naming what is unavailable rather than what failed internally. - **A route forward** — either an alternative that reaches the same outcome without the capability, or an honest statement that the outcome is not available, with the single action that would change that. - **Nothing inert.** No control that is visible but does nothing, no panel that looks like a result but is empty. - **No nagging.** A denial that produces a fresh request every time the screen is opened is the defect people complain about most, and it is invisible to a case that checks only that the fallback renders once. - **Containment.** One withdrawn capability degrades one feature; it does not brick the surrounding journey. ## Two halves of the assertion | | Granted branch asserts | Denied branch asserts | |---|---|---| | Positive | The feature works and the capability is genuinely used | The alternative route renders **and can be completed to the same end state** | | Negative | No fallback copy is shown when it is not needed | No repeat requesting, no inert controls, no blank panel presented as a result | | Where it stops | At the outcome the person came for | At the same outcome, reached another way — or an honest denial with a fix | The negative half is what distinguishes a case worth its upkeep. Positive assertions catch a missing fallback; negative assertions catch the fallback that exists but is a dead end, and the request that fires on every screen entry. ## Carry the assertion to the end of the journey The single biggest upgrade to a denial case is to stop asserting the banner and start finishing the task. If the capability was a shortcut — scanning a code rather than typing it, using position rather than choosing a place, picking a contact rather than entering a name — then the alternative should let the person reach the same end state, and the case should prove it by reaching it: 1. Arrange the stored decision as denied. 2. Open the feature and assert the explanation and the alternative affordance are present. 3. **Use the alternative** — type the code, pick the place, enter the name. 4. Assert the same end-state the granted path asserts: the confirmation, the created record, the completed journey. 5. Assert the negatives: reopen the screen once and assert no new request is made, and that nothing is visible-but-inert. Step 3 is the one usually missing, and it is the one that catches a fallback that renders but does not submit, or an alternative whose result the backend rejects because it lacks a value only the capability could supply. ## Where to stop Not every denial has an alternative. Where the outcome genuinely cannot be produced without the capability, the standard shifts but does not vanish: assert that the denial is stated in a way a non-technical person can act on, that it names the one step that would fix it, and that the rest of the screen still functions. What you must not accept as correct is silence — an empty list, a zero count, a result panel with nothing in it. Silence is indistinguishable from the product being broken, and it produces support contacts that nobody can diagnose because the application never recorded that a capability was missing. ## Keeping it maintainable Two habits stop these cases from rotting. First, share the standard rather than the assertions: if every capability-gated feature is required to offer explanation, alternative and no-nagging, the assertion helper can be written once and each case supplies its own screen. Second, name the case for its branch and its end state — the alternative reached, not the message shown — so the next person to read a red run knows immediately whether the fallback disappeared or merely stopped working.

  • How far does the degraded journey have to be followed before the case can stop?
    Follow it to the outcome the person came for, or to the point where the product honestly says that outcome is unavailable. Stopping at the explanation proves only that a message renders; the value of the case is proving the alternative route still reaches a real end state. Where no alternative exists, assert the denial is actionable and that the rest of the screen still works.
  • Which negative assertion catches the most common defect on this branch?
    That the application does not ask again every time the screen is opened. Repeated requesting after a denial is the most frequent and most irritating defect here, and a case that checks only that the fallback renders will never see it. Leaving the screen, returning, and asserting no new request was made catches it cheaply.
  • Why is an empty result the worst possible degraded behaviour?
    Because it is indistinguishable from the product being broken. The person sees nothing, cannot tell whether there is genuinely no data or whether something is missing, and has no action to take. It also produces support contacts nobody can diagnose, since the application recorded a normal empty response rather than a missing capability.

A lift marked out of service is only properly handled when the sign points at the stairs and the stairs actually reach the floor you wanted. A sign on its own is a message, not a route.

saying these in an interview costs you the question

  • Calls the case passing because the application did not crash
  • Stops at the error banner instead of finishing the journey
  • Asserts only the granted branch and calls the feature covered
  • Accepts an empty screen as correct degraded behaviour
  • Ignores a fresh request firing on every screen entry
  • Asserts an internal flag rather than what the person sees