dom lizarraga

    dominiclizarraga@hotmail.com

    Mental model for writing tests

    4 minutes to read

    With my recent journey of gaining more testing experience with chatGPt help, and seeing some kind of pattern that after writing the exercises from the book Efective testing with RSpec by Myron Marston and Ian Dees I shared the idea with the LLM, and it helped me build the following.

    First thing that poped my my was the Arrange, Act, Assert so i did used that one to guide the LLM to what type of output I wanted.

    LLM response:

    You’re thinking in exactly the right direction! What you described is essentially the AAA pattern in testing: Arrange, Act, Assert.

    To help you memorize and internalize it — especially when working with test doubles and APIs — here’s a simple mental checklist or mantra you can use, along with a mnemonic:

    🔁 The 6-Step “Double-Driven Test” Flow 🧠 Mnemonic: “DUPRAP” (like “Do a PRAP” — rhymes with “wrap”)

    Double — Use test data — Program the mock — Request — Analyze (parse) — Prove (assert)

    ✅ 1. Double Set up your test double (mock the collaborator)

    let(:ledger) { instance_double('ExpenseTracker::Ledger') }
    

    💡 Think: “Who am I faking?” — usually the object under the API, like ledger.

    ✅ 2. Use Test Data Define the test data your mock should return

    expenses = [
      { 'payee' => 'Zoo', 'amount' => 15.25, 'date' => '2017-06-10' },
      { 'payee' => 'Starbucks', 'amount' => 5.75, 'date' => '2017-06-10' }
    ]
    

    💡 Think: “What result do I want my mock to send back?”

    ✅ 3. Program the Mock Tell your double how to behave

    allow(ledger).to receive(:expenses_on)
      .with('2017-06-10')
      .and_return(expenses)
    

    💡 Think: “When the test calls this method, return my test data.”

    ✅ 4. Request Trigger the route via Rack/Test

    get '/expenses/2017-06-10'
    

    💡 Think: “Now I run the actual API code with the test in place.”

    ✅ 5. Analyze Parse the response so you can assert on it

    parsed = JSON.parse(last_response.body)
    

    💡 Think: “What did the API give me back?”

    ✅ 6. Prove Assert the expected response

    expect(parsed).to eq(expenses)
    

    💡 Think: “Does the response match what I expected?”

    🧠 Bonus: Visual Reminder

    # 1. Double
    let(:ledger) { instance_double('ExpenseTracker::Ledger') }
    
    # 2. Use Test Data
    expenses = [ { 'payee' => 'Zoo', 'amount' => 15.25, 'date' => '2017-06-10' } ]
    
    # 3. Program the Mock
    allow(ledger).to receive(:expenses_on)
      .with('2017-06-10')
      .and_return(expenses)
    
    # 4. Request
    get '/expenses/2017-06-10'
    
    # 5. Analyze
    parsed = JSON.parse(last_response.body)
    
    # 6. Prove
    expect(parsed).to eq(expenses)
    

    📌 Final Tip: Turn It into Muscle Memory Every time you write a controller- or API-level test, mentally repeat:

    “Double, Data, Allow → Request, Parse, Expect”

    Write 3–5 of these for different routes, and it’ll stick — just like git add, git commit, git push stuck after a week or two.

    Let me know if you want to turn this into a printable or flashcard!

    Even though tools like LLM can help us write more code (and tests!) faster than ever, understanding the basics is still on us.

    At the end of the day, we’re the ones responsible for making sure the code works and is maitainable. So the better we get at thinking through these steps, the more useful the LLM becomes.

    Don’t skip the thinking.