Test Runner
Run multi-case test suites against any Apify actor. Define test cases with inputs and expected outputs, then run all cases and get pass/fail results with diffs.
Maintenance Pulse
90/100Cost Estimate
How many results do you need?
Pricing
Pay Per Event model. You only pay for what you use.
| Event | Description | Price |
|---|---|---|
| test-suite-run | Charged per test suite execution. | $0.35 |
Example: 100 events = $35.00 · 1,000 events = $350.00
Documentation
Run any Apify actor through a suite of test cases with different inputs and validate output against assertions. Actor Test Runner executes each test case sequentially, checks result counts, required fields, field types, duration limits, and empty values, then produces a structured pass/fail report. Build confidence in your actors before publishing, after code changes, or as part of a CI/CD pipeline.
Actor Test Runner takes a target actor ID and an array of test cases. Each test case has a name, an input object, and an assertion set. The runner executes the actor once per test case, fetches the output dataset, evaluates all assertions, and reports pass/fail with details. One PPE charge ($0.35) covers the entire suite -- not per test case.
What data can you extract?
| Data Point | Source | Example |
|---|---|---|
| Suite result | Aggregated test results | 3/4 passed |
| Per-case result | Individual test execution | Basic search: PASS (12.1s, 5 results) |
| Assertion details | Evaluated checks per case | minResults >= 3: PASS (actual: 5) |
| Duration per case | Measured start-to-finish | 8.3 seconds |
| Result counts | Dataset item count | 5 items |
| Total suite duration | Sum of all cases | 45.2 seconds |
| Error messages | Failed run details | Run status: TIMED-OUT |
Why use Actor Test Runner?
Manual testing means running an actor once with one input and eyeballing the output. This misses edge cases: empty queries that should return zero results, special characters that break parsing, large result sets that trigger pagination bugs, inputs with missing optional fields. These edge cases break in production, not in your quick manual check.
Actor Test Runner lets you define multiple test scenarios in a single suite and run them all automatically. Each scenario has its own input and its own assertions. The suite tells you exactly which cases pass and which fail, with specific details about every failed assertion.
Why an actor instead of a local test script?
- Runs in the same cloud environment -- catches Docker, memory, and network issues that don't appear locally
- No local dependencies -- works from the Apify console, API, or CI/CD without any setup
- Scheduling -- run test suites on a schedule to catch upstream changes
- Single charge per suite -- $0.35 regardless of how many test cases (target actor compute billed separately)
- Structured output -- machine-readable JSON report for CI/CD integration
Features
- Multiple test cases per suite with independent inputs and assertions -- test your actor's behavior across different scenarios in a single run
- Six assertion types: minimum results (
minResults), maximum results (maxResults), required fields (requiredFields), field type checking (fieldTypes), duration limits (maxDuration), and empty value detection (noEmptyFields) - Sequential execution to avoid overwhelming the target actor with parallel runs -- each test case completes before the next starts
- Per-case error isolation -- if one test case crashes, the remaining cases still run and produce results
- Structured pass/fail report with assertion-level detail for each test case
- Single PPE charge ($0.35) per suite run -- not per test case, making multi-case suites economical
- Configurable timeout and memory applied consistently to all test case runs
Use cases for actor test suites
Edge case coverage
Define test cases for boundary conditions: empty input, maximum result counts, special characters, very long strings, missing optional fields. Verify your actor handles each gracefully.
Input validation testing
Test that your actor rejects invalid inputs appropriately: wrong types, missing required fields, out-of-range values. Each test case can assert specific error behaviors.
Multi-scenario smoke tests
Before publishing, run a suite covering your actor's main use cases: different search queries, different filter combinations, different output modes. A passing suite means confidence to publish.
CI/CD integration
Trigger a test suite after every apify push. Parse the JSON report in your CI pipeline. If any test case fails, block the deployment and alert the team.
Performance regression detection
Use maxDuration assertions to catch performance regressions. If a test case that normally completes in 10 seconds starts taking 30, the assertion fails and you investigate before users notice.
Data completeness checks
Use noEmptyFields assertions on critical output fields. If your scraper starts returning empty strings for email or empty arrays for reviews, the test suite catches it immediately.
How to run a test suite
- Enter the target actor -- Provide the actor ID or
username/actor-nameslug. - Define test cases -- Create a JSON array where each object has
name,input, andassertions. - Configure runtime -- Set timeout and memory for each test case run.
- Run the suite -- Click "Start" and wait for all test cases to complete.
- Review the report -- Check per-case results and assertion details in the Dataset tab.
Input parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
targetActorId | string | Yes | -- | Actor ID or username/actor-name slug to test. |
testCases | array | Yes | -- | Array of test case objects (see below). |
timeout | integer | No | 120 | Maximum seconds per test case run. |
memory | integer | No | 512 | Memory in MB for each test case run. |
Test case format
Each test case object has three fields:
{
"name": "Descriptive test name",
"input": { "query": "test value", "maxResults": 5 },
"assertions": {
"minResults": 1,
"maxResults": 100,
"requiredFields": ["name", "url"],
"fieldTypes": { "name": "string", "url": "string", "rating": "number" },
"maxDuration": 60,
"noEmptyFields": ["name", "url"]
}
}
Assertion types
| Assertion | Type | What it checks |
|---|---|---|
minResults | number | Dataset has at least N items |
maxResults | number | Dataset has at most N items |
requiredFields | string[] | At least one item has a non-null value for each field |
fieldTypes | object | All non-null values for each field match the declared type (string, number, boolean, object, array) |
maxDuration | number | Test case completes within N seconds |
noEmptyFields | string[] | No items have null, undefined, empty string, or empty array for the listed fields |
Input example
{
"targetActorId": "ryanclinton/website-contact-scraper",
"testCases": [
{
"name": "Basic website scan",
"input": {
"urls": ["https://example.com"],
"maxPagesPerDomain": 3
},
"assertions": {
"minResults": 1,
"requiredFields": ["url", "domain", "emails"],
"fieldTypes": { "emails": "array", "url": "string" },
"maxDuration": 60
}
},
{
"name": "Multiple URLs",
"input": {
"urls": ["https://example.com", "https://httpbin.org"],
"maxPagesPerDomain": 2
},
"assertions": {
"minResults": 2,
"requiredFields": ["url", "domain"],
"noEmptyFields": ["url", "domain"]
}
},
{
"name": "Empty input handling",
"input": {
"urls": [],
"maxPagesPerDomain": 1
},
"assertions": {
"maxResults": 0,
"maxDuration": 30
}
}
],
"timeout": 120,
"memory": 512
}
Input tips
- Keep test inputs small -- 1-3 URLs, 3-5 max results. Tests should be fast and cheap.
- Name test cases descriptively -- the name appears in the report. "Basic search" is better than "test1".
- Start with
minResultsandrequiredFields-- these catch the most common failures. Add type checking and duration limits as needed. - Test edge cases -- empty inputs, single results, maximum limits. These are where bugs hide.
- Order cases from simple to complex -- makes the report easier to read and debug.
Output example
Each suite run produces one report in the dataset:
{
"actorName": "ryanclinton/website-contact-scraper",
"actorId": "abc123def456",
"totalTests": 3,
"passed": 2,
"failed": 1,
"totalDuration": 45.2,
"results": [
{
"name": "Basic website scan",
"passed": true,
"duration": 12.1,
"resultCount": 1,
"assertions": [
{ "assertion": "minResults >= 1", "passed": true, "expected": 1, "actual": 1 },
{ "assertion": "field 'url' exists", "passed": true, "expected": "present", "actual": "present" },
{ "assertion": "field 'emails' is array", "passed": true, "expected": "array", "actual": "array" },
{ "assertion": "duration <= 60s", "passed": true, "expected": "60s", "actual": "12.1s" }
]
},
{
"name": "Multiple URLs",
"passed": true,
"duration": 25.0,
"resultCount": 2,
"assertions": [
{ "assertion": "minResults >= 2", "passed": true, "expected": 2, "actual": 2 }
]
},
{
"name": "Empty input handling",
"passed": false,
"duration": 8.1,
"resultCount": 0,
"assertions": [
{ "assertion": "maxResults <= 0", "passed": true, "expected": 0, "actual": 0 },
{ "assertion": "duration <= 30s", "passed": true, "expected": "30s", "actual": "8.1s" }
],
"error": "Run status: FAILED"
}
],
"testedAt": "2026-03-18T14:30:00.000Z"
}
Output fields
| Field | Type | Description |
|---|---|---|
actorName | string | Display name of the tested actor |
actorId | string | Apify actor ID |
totalTests | number | Number of test cases in the suite |
passed | number | Number of test cases that passed |
failed | number | Number of test cases that failed |
totalDuration | number | Total execution time in seconds for the entire suite |
results | array | Per-case results (see below) |
results[].name | string | Test case name |
results[].passed | boolean | Whether the test case passed (run succeeded and all assertions passed) |
results[].duration | number | Execution time for this test case in seconds |
results[].resultCount | number | Number of items in the output dataset |
results[].assertions | array | Assertion results: { assertion, passed, expected?, actual? } |
results[].error | string | Error message if the run failed (optional) |
testedAt | string | ISO 8601 timestamp |
How much does it cost?
Actor Test Runner uses pay-per-event pricing at $0.35 per suite run -- one flat fee regardless of how many test cases. Target actor runs are billed separately.
| Scenario | Suites | Orchestration Cost | Notes |
|---|---|---|---|
| One-off suite (5 cases) | 1 | $0.35 | Target actor runs 5 times |
| Daily CI/CD (30/mo) | 30 | $10.50 | Plus target actor costs |
| Nightly fleet test (10 actors) | 10/night | $105.00/mo | 300 suites/month |
The Apify Free plan ($5/mo credits) covers approximately 14 suite runs (orchestration only).
Run test suites using the API
Python
from apify_client import ApifyClient
client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("ryanclinton/actor-test-runner").call(run_input={
"targetActorId": "ryanclinton/website-contact-scraper",
"testCases": [
{
"name": "Basic scan",
"input": {"urls": ["https://example.com"], "maxPagesPerDomain": 3},
"assertions": {"minResults": 1, "requiredFields": ["url", "domain"]},
},
],
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(f"Suite: {item['passed']}/{item['totalTests']} passed in {item['totalDuration']}s")
for tc in item["results"]:
status = "PASS" if tc["passed"] else "FAIL"
print(f" [{status}] {tc['name']} ({tc['duration']}s, {tc['resultCount']} results)")
for a in tc["assertions"]:
if not a["passed"]:
print(f" FAILED: {a['assertion']} (expected {a.get('expected')}, got {a.get('actual')})")
JavaScript
import { ApifyClient } from "apify-client";
const client = new ApifyClient({ token: "YOUR_API_TOKEN" });
const run = await client.actor("ryanclinton/actor-test-runner").call({
targetActorId: "ryanclinton/website-contact-scraper",
testCases: [
{
name: "Basic scan",
input: { urls: ["https://example.com"], maxPagesPerDomain: 3 },
assertions: { minResults: 1, requiredFields: ["url", "domain"] },
},
],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const report = items[0];
console.log(`Suite: ${report.passed}/${report.totalTests} passed`);
report.results.forEach(tc => {
console.log(` [${tc.passed ? "PASS" : "FAIL"}] ${tc.name}`);
});
cURL
curl -X POST "https://api.apify.com/v2/acts/ryanclinton~actor-test-runner/runs?token=YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"targetActorId": "ryanclinton/website-contact-scraper",
"testCases": [{
"name": "Basic scan",
"input": {"urls": ["https://example.com"], "maxPagesPerDomain": 3},
"assertions": {"minResults": 1, "requiredFields": ["url", "domain"]}
}]
}'
FAQ
How many test cases can I include in one suite? There is no hard limit, but each test case runs the target actor sequentially. A suite with 10 cases each taking 30 seconds will take about 5 minutes total. Keep suites focused and fast.
Why are test cases run sequentially, not in parallel? To avoid overwhelming the target actor or exhausting your compute budget unpredictably. Sequential execution gives consistent, reproducible results and predictable costs.
What happens if one test case crashes? The crashed case reports FAIL with the error message. Remaining test cases continue running normally. One bad case doesn't block the rest of the suite.
Can I reuse test cases across actors?
Yes, if actors have similar input schemas. Copy the test cases array and change the targetActorId. Useful for comparing replacement actors.
What's the difference between Test Runner and Cloud Staging Test? Cloud Staging Test runs a single input with optional assertions. Test Runner runs multiple inputs with independent assertions per case. Use Cloud Staging Test for quick smoke tests, Test Runner for comprehensive multi-scenario suites.
Can I use this for load testing? No. Test Runner runs one case at a time. For load testing, use the Apify API to trigger multiple parallel runs directly.
Related actors
| Actor | How to combine |
|---|---|
| Cloud Staging Test | Quick single-input validation. Use Test Runner for multi-case suites, Cloud Staging Test for fast one-off checks. |
| Schema Validator | Schema compliance checking. Run Schema Validator for type/field analysis, Test Runner for functional correctness. |
| Regression Suite | Historical comparison. Regression Suite extends Test Runner with diff logic to detect tests that were passing but now fail. |
| Actor Health Monitor | Runtime failure monitoring. Test Runner validates output quality; Health Monitor catches crashes and error rates. |
Support
Found a bug or have a feature request? Open an issue in the Issues tab on this actor's page.
How it works
Configure
Set your parameters in the Apify Console or pass them via API.
Run
Click Start, trigger via API, webhook, or set up a schedule.
Get results
Download as JSON, CSV, or Excel. Integrate with 1,000+ apps.
Use cases
Sales Teams
Build targeted lead lists with verified contact data.
Marketing
Research competitors and identify outreach opportunities.
Data Teams
Automate data collection pipelines with scheduled runs.
Developers
Integrate via REST API or use as an MCP tool in AI workflows.
Related actors
Bulk Email Verifier
Verify email deliverability at scale. MX record validation, SMTP mailbox checks, disposable and role-based detection, catch-all flagging, and confidence scoring. No external API costs.
GitHub Repository Search
Search GitHub repositories by keyword, language, topic, stars, forks. Sort by stars, forks, or recently updated. Returns metadata, topics, license, owner info, URLs. Free API, optional token for higher limits.
Website Content to Markdown
Convert any website to clean Markdown for RAG pipelines, LLM training, and AI apps. Crawls pages, strips boilerplate, preserves headings, tables, and code blocks. GFM support.
Website Tech Stack Detector
Detect 100+ web technologies on any website. Identifies CMS, frameworks, analytics, marketing tools, chat widgets, CDNs, payment systems, hosting, and more. Batch-analyze multiple sites with version detection and confidence scoring.