Most test suites check that the screens render and the forms submit. The bugs that actually hurt a business are of another kind: an invoice total that is a pound off, a clerk who can open a manager's report, a button pressed twice that creates two payments, an export that opens as symbols in Excel.

Business applications fail in a few predictable ways, and a small set of tests aimed at those ways is worth more than a thousand tests of the happy path. These are the ten checks we reach for first. Each one comes with what to test and an example you can adapt (written in Pest, for Laravel, but the idea is the same in any stack).

1. Money and rounding

Money bugs are small and expensive. Test the arithmetic on awkward numbers, not round ones.

  • A total with a percentage discount that does not divide evenly (33.33% of 100.01).
  • Tax on an odd subtotal, and tax applied to a discounted total.
  • Totals that must equal the sum of their lines. Test that they do.
  • Stored amounts: integers or decimals, never floats.
it('keeps invoice totals equal to the sum of their lines', function () {
    $invoice = Invoice::factory()->withLines([19.99, 0.01, 33.33])->taxed(14)->create();

    expect($invoice->total)->toBe($invoice->lines->sum('total'));
});

2. Permissions, tested from the outside

The most serious bug in a business application is a person seeing or changing something they should not. Test permissions by acting as each role and trying everything it must not do.

  • For each role, a list of endpoints it must be refused, expecting a refusal (403 or 404).
  • Object-level access: a user opens another user's record by changing the id in the URL. This is one of the most common real-world flaws, and it is never caught by a test that only logs in as an admin.
  • A user who has lost a role must lose the access immediately.
it('refuses a clerk who opens another branch\'s invoice', function () {
    $invoice = Invoice::factory()->for(Branch::factory())->create();
    $clerk = User::factory()->clerkOf(Branch::factory()->create())->create();

    actingAs($clerk)->getJson("/api/invoices/{$invoice->id}")->assertForbidden();
});

Write these tests from a table of roles and actions, so a new endpoint without an entry stands out.

3. State changes that must not happen

An order can go from "new" to "paid" to "shipped". It must not go from "shipped" to "new", or from "cancelled" to "paid". Test the forbidden transitions as carefully as the allowed ones: every illegal move should fail loudly, and leave the data as it was.

4. The button pressed twice

A slow connection, a nervous double-click, a mobile app that retries: the same request arrives twice. Test that it creates one thing.

  • Submit the same payment, order or transfer twice in a row; expect one record.
  • Run two at the same moment (in a test, with a lock or a parallel request) and expect the stock or the balance to stay consistent.
  • Make sure the interface disables the button, and that the server does not rely on that.

5. Concurrency on limits

Anything with a limit is a race: the last item in stock, the hundredth coupon use, one booking per room per hour. A test that runs the operation in parallel and then checks the totals finds bugs that no single-user test can.

6. Dates, times and time zones

  • A record created at 23:30 and shown in another time zone.
  • Month-end and year-end: reports must include the last day and exclude the next one.
  • Daylight saving, where a country observes it, and days with 23 or 25 hours.
  • A week that starts on Saturday or Sunday, depending on the user.
  • Expiry at "midnight": which midnight, in whose time zone?

Store timestamps in UTC, convert only for display, and have at least one test that runs with a non-UTC application time zone.

7. Arabic and mixed-direction input

If your users write Arabic, your tests should too.

  • Names and addresses with Arabic letters, diacritics and Arabic-Indic digits.
  • Search that finds "أحمد" when the user types "احمد".
  • Long strings, and a string that mixes Arabic with an English product name.
  • Import and export: a CSV with Arabic that opens correctly in Excel.
  • A PDF or an email with Arabic, opened and read by a person.

8. Imports, exports and bad data

Real data is dirty, and users will upload the file you did not expect.

  • An import with an empty row, a missing column, a duplicate, a formula in a cell, and a number written as text.
  • A huge file: does it finish, or time out half-way and leave half the rows?
  • An import that fails must change nothing, or report exactly what it did.
  • An export filtered by a user must contain only what that user may see.

9. Lists, search and volume

A system that is fast with ten records can be unusable with ten thousand. Seed realistic volume in a test or staging database and check the screens that grow: lists, reports, dashboards and search.

  • Does each list paginate and sort on the server?
  • Are there queries that run once per row (the N+1 problem)? Fail a test when a page runs more queries than expected.
  • Does the heaviest report still finish in a reasonable time?

10. Failure and recovery

Business software has to behave well when something goes wrong.

  • The payment gateway, the SMS or the email service is down: the user sees a clear message, and nothing is half-saved.
  • An exception in the middle of an operation rolls back everything it did.
  • A background job fails: it retries safely and does not duplicate its effect.
  • A backup that has been restored. Once a quarter, restore a backup into an empty environment and check that the application works with it.

Make it a routine

  • Tests run on every change, before anything is released. A failing test stops the release.
  • A bug becomes a test first. When a defect is reported, write the test that fails because of it, then fix it. The suite grows exactly where your product is fragile.
  • Test with realistic data, not "Test 1" and "Test 2".
  • Keep a short list of manual checks for what machines do badly: how the screen feels, whether the wording makes sense, whether a real person can finish the task.

A checklist

  • Money is tested on awkward numbers, and totals match their lines.
  • Each role is tested for what it must not do, including other people's records.
  • Forbidden state changes fail loudly.
  • Repeated and simultaneous requests create one thing.
  • Limits are tested under parallel use.
  • Dates are tested across time zones and period ends.
  • Arabic, mixed text and exports are tested like everything else.
  • Imports survive dirty data and fail cleanly.
  • Volume is tested, and the slowest report is known.
  • Failure paths and restores are tested, not assumed.

If you want a team that builds these checks in from the first release, or a second pair of eyes on a system that is already live, talk to us. Quality assurance is one of the six disciplines BENAA works in.