Principle:DevExpress Testcafe Assertion Retry Logic
| Knowledge Sources | |
|---|---|
| Domains | Testing, Web_Automation |
| Last Updated | 2026-02-12 04:00 GMT |
Overview
Assertion Retry Logic is the concept of repeatedly evaluating test assertions over time until they pass or timeout, handling the asynchronous and dynamic nature of web applications.
Description
Web applications are inherently asynchronous. State changes don't happen instantly:
- API responses arrive after network delays
- React/Vue/Angular update DOM after state changes
- CSS animations and transitions complete over time
- User interactions trigger delayed effects
Traditional synchronous assertions fail immediately if the condition isn't met at the exact moment of evaluation. This forces test authors to manually insert wait statements, predict timing, and handle race conditions. This approach is:
- Brittle (breaks when timing changes)
- Verbose (requires explicit waits everywhere)
- Error-prone (easy to wait too little or too much)
Assertion retry logic solves this by:
- Detecting re-executable values: Identifying when assertion operands need re-evaluation
- Retry loop: Repeatedly checking the assertion with delays between attempts
- Timeout management: Failing after a maximum duration
- Efficient execution: Only retrying when values can change
- Smart delay: Using fixed intervals (e.g., 200ms) to balance responsiveness and CPU usage
This pattern makes tests:
- More reliable (adapt to timing variations)
- More readable (no manual wait logic)
- More maintainable (timing changes don't break tests)
- Faster (no unnecessary fixed delays)
Usage
Use assertion retry logic when:
- Asserting on Selector properties (automatic)
- Verifying conditions after async operations
- Testing real-time updates or polling scenarios
- Working with animations or timed transitions
- Dealing with eventually-consistent systems
- Avoiding manual wait statements
Theoretical Basis
Assertion retry logic follows these principles:
Value Classification:
// Pseudocode
function isReExecutable(value) {
return value instanceof SelectorPromise ||
value instanceof ClientFunctionPromise ||
value instanceof Promise
}
Retry Algorithm:
// Pseudocode
async function executeAssertion(actual, expected, assertFn, options) {
const startTime = now()
const timeout = options.timeout || defaultTimeout
const retryDelay = 200 // milliseconds
while (true) {
// Evaluate operands
const actualValue = await evaluate(actual)
const expectedValue = await evaluate(expected)
// Try assertion
try {
assertFn(actualValue, expectedValue)
return // Success
} catch (error) {
// Check if we should retry
const canRetry = isReExecutable(actual) || isReExecutable(expected)
const timeRemaining = timeout - (now() - startTime)
if (!canRetry || timeRemaining <= 0) {
throw error // Final failure
}
// Wait before retry
await sleep(Math.min(retryDelay, timeRemaining))
}
}
}
Optimization Strategies:
- Fixed retry delay: Balance between responsiveness and CPU usage
- Early exit: Stop retrying on non-re-executable failures
- Timeout tracking: Ensure total duration doesn't exceed limit
- Last attempt: Always try at least once, even if timeout expired
Integration with Test Framework:
// Retry logic is transparent to test author
await t.expect(selector.count).gte(5)
// Framework internally:
// 1. Detects selector.count is re-executable
// 2. Enters retry loop
// 3. Re-queries DOM on each iteration
// 4. Stops when count >= 5 or timeout
Failure Reporting:
- Report final actual and expected values
- Include assertion type and message
- Show timeout duration
- Provide stack trace to assertion line