// education and e-learning

A new course module goes live the week before exams. We test what a student loses first.

Learning software fails in ways students notice before you do: a grade that never reaches the gradebook, progress overwritten by another device, a lesson a screen reader cannot open. We test those on every release.

Start a two-week pilot
ISO 27001:2022a UK dental-training app4.9 Clutch, 65+ reviews$25-45/hour
On a clinic portal I check isolation the slow way: two test clinics, the page source, the network responses, direct URLs typed by hand. The screen usually hides other clinics' data. The question is whether the server does.
Carmen Lapusan, QA tester on e-learning and clinic portal apps
// what keeps you up at night, and what you get
"Students can't finish a lesson on the laptop or tablet they actually own."
Every lesson tested on the devices and browsers your students use.Flows
"A content update wipes someone's progress the night before an exam."
Progress, grades and certificates checked after every content release.BugBoard
"A university buyer rejects us over accessibility."
An accessibility report a university procurement team will accept.Auditi
// four places learning software breaks
01 · lti 1.3

The grade never reaches the gradebook

The tool sends a score to the LMS with an access token it cached earlier. The token has expired, the LMS answers 401, and the tool logs a warning and marks the grade as synced. The student sees 8/10 in the app. The gradebook cell stays empty.

LTI Advantage, Assignment and Grade Services
studenttoollmssubmits quiz, 8 of 10POST /scores, cached token401 invalid_tokenlog warning, mark syncedQuiz complete: 8/10gradebook cell empty
FAILthe student sees 8/10, the teacher sees nothing, and no error is raised
how we test itStub the LMS to fail the score call with 401, 429 and 5xx. Assert the score is retried with a fresh token and arrives, and that nothing is marked synced before the LMS accepts it.
lms.failNext('scores', 401)              // expired token
await student.finishQuiz({ score: 8, max: 10 })
await lms.clock.advance('10m')
expect(lms.gradebook(student.id, lineItem)).toBe(8)  // got empty
expect(tool.outbox.pending()).toBe(0)
02 · offline sync

Two devices, and the older one loses

A student finishes lessons 4 and 5 on a phone with no signal, while the tablet still holds lessons 1 to 3 and syncs later. When the phone reconnects, the server keeps the record with the newer time stamp and discards the whole phone record. Two lessons show as unseen.

whole-record last-write-wins; a wrong device clock makes it worse
phoneoffline
tabletonline
phonereconnect
servermerge
studentexam eve
2 LOSTlessons 4 and 5 show unseen
how we test itDrive two real clients through offline, online and reconnect orders. Progress must merge per lesson, never replace as one record.
await phone.goOffline(); await phone.complete('L4', 'L5')
await tablet.complete('L3')
await phone.goOnline(); await sync.settle()
expect(server.completed(student)).toEqual(['L1','L2','L3','L4','L5'])
// got L1 to L3
03 · accessibility

The screen reader sees one empty block

The lessons render inside a canvas or game-engine view. To a screen reader that is a single node with no name and no children, so lesson titles, buttons and quiz options do not exist. A visual check passes, and a blind student cannot start the course.

WCAG 2.1 SC 1.1.1, 2.1.1, 4.1.2; EN 301 549
accessibility tree, screen reader view <main> <canvas id="lesson-view"> name: (empty) children: 0 Lesson 4, Next, Quiz: not exposed
1 NODEa blind student can reach nothing
how we test itDump the accessibility tree for every screen and assert that each control has a name and a role. Then a manual pass with a real screen reader and the keyboard alone.
const tree = await page.locator('#lesson').ariaSnapshot()
expect(tree).toContain('- button')
expect(unnamed(tree, ['button', 'link', 'radio'])).toEqual([])
await keyboard.tabThrough('lesson')  // every control reachable
04 · exam timer

The screen says 15 minutes the server has closed

The exam clock counts down from the device time. If the device clock steps back, through a manual change or a time correction, the screen shows time that is not left. The server closes at its own deadline and rejects the last answers.

also: a backgrounded tab slows timers, a tablet that sleeps
exam length3600 sserver closes10:00:00device clock steps back-900 sscreen at 10:00 server time900 s leftanswers sent after 10:00REJECTED
server deadlinemonotonic elapsed timeresync on reconnect
FAILscreen and server disagree on time left
PASStimer derived from server time
how we test itMove the device clock and background the tab during a timed exam. The time shown must match the server, and the final answers must be accepted until it closes.
await exam.start({ minutes: 60 })
await page.clock.setSystemTime(minus(15, 'min'))   // device steps back
await server.advanceTo(exam.deadline)
expect(await ui.timeLeft()).toBe(0)                 // got 900
expect(await ui.canSubmit()).toBe(false)

Illustrative examples built from defect patterns we test for. Not client data.

// on every pull request

Your code is written in hours. Our pipeline keeps up.

Pull requestfrom your coding agent
Flowsjourneys and fault injection
Security Toolkit15 scanners + 20 own checks
QA engineerconfirms every finding
BugBoardrelease report, Jira sync
// the regulation, in testing terms
regimewhere it actually breakswhat we run
WCAG 2.1 AA, EN 301 549Content inside a canvas or game view exposed as one empty nodeaccessibility tree checks, keyboard-only and screen reader passes, Auditi scans
Web Accessibility DirectivePublic universities must meet EN 301 549, so buyers ask suppliers for evidencea written conformance report with the failing criteria and the fixes
LTI 1.3 and AdvantageToken expiry and failed score calls handled only on the happy pathlaunch and passback tests against LMS sandboxes, with injected 401, 429 and 5xx
GDPR and COPPAAge checks passed by guessing; student identifiers in analytics and exportsage-boundary tests for each country, payload scans on analytics and exports
// proof

Accessibility found by testing, not by complaint

1 empty blockscreen reader reached 0 lessons
  • A UK dental-training app: independent testing of the app and its courses. We found the app showed screen readers a single empty block, so blind students could reach none of the content.
  • A language-learning app, tested in 2024 and 2025.
  • A school coaching and booking platform, tested in 2026.

One client, name withheld.

BUG-0462 · quiz / accessibility · examplehigh

Quiz options cannot be chosen with the keyboard

  1. Open any quiz and press Tab through the page
  2. The answer options are skipped; focus jumps to Submit
  3. Press Enter on Submit: it records a blank answer

Fix: Use native radio inputs, or give each option a role, a tab stop and key handling.

WCAG 2.1.1REQ-A11Y-03TC-0462 addedJira EDU-3317

Two weeks on your learning platform. Free if you're not happy.

Start a pilot