testing

  • Extracting a service without a rewrite

    The reporting code had one caller and a six-hour test run. Finding the seam, dealing with the shared schema honestly, and routing traffic gradually rather than switching.

  • Integration tests want a transaction, not a truncate

    Resetting the database between tests by truncating every table is correct and slow, and it re-runs the seed data on each one. Wrapping each test in a transaction…

  • A deployment pipeline that runs the tests first

    Deploying by rsync on a Friday afternoon, and what replaced it: lint, test, build, stage, promote — with a rollback that is the same mechanism rather than a…

  • Anonymous classes make a one-off test double

    Testing a class that takes an interface usually means either a mocking framework or a small stub class in its own file, which then lives forever and is…

  • artisan tinker is a REPL with the application booted

    Checking what a relation actually returns usually means writing a throwaway route, hitting it, and deleting it — or adding a dd() somewhere and reloading. Both are slower…

  • expectException replaced the annotation

    The @expectedException annotation and setExpectedException() are both deprecated in favour of methods, which is more than a rename — the annotation asserted only that the exception was thrown…

  • The N+1 queries you did not write

    1,400 queries on a page showing 40 rows. Eager loading, the difference between with() and load(), and the accessor that no eager load can fix.

  • Deep clone an object in JavaScript

  • A test double for the clock, and one for randomness

    Two things make a test unrepeatable, and both are usually called directly from the code under test: the current time and a random number. Neither can be asserted…

  • A TypeError is something you can now catch

    Passing a string where an object was hinted used to be a recoverable fatal, which in practice meant an unrecoverable one. It is an exception now, which makes…