| title | Automated tests for Actors |
|---|---|
| description | Learn how to automate ongoing testing and make sure your Actors perform over time. See code examples for configuring the Actor Testing Actor. |
| slug | /actors/development/automated-tests |
| sidebar_position | 9 |
| sidebar_label | Automated tests |
Learn how to automate ongoing testing and make sure your Actors perform over time. See code examples for configuring the Actor Testing Actor.
import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
Automated testing is crucial for maintaining the reliability and performance of your Actors over time. This guide will help you set up automated tests using the Actor Testing Actor.
- Prepare test tasks - Create 1–5 separate testing tasks for your Actor.
- Configure Actor testing - Set up a task using the Actor Testing Actor.
- Validate tests - Run the test task multiple times until all tests pass.
- Schedule tests - Set up a recurring schedule for your tests.
- Monitor results - Review and address any issues on a weekly basis.
Example of Actor testing tasks
When creating test tasks:
- Include a test for your Actor's default configuration
- Set a low
maxItemvalue to conserve credits - For large data tests, reduce test frequency to conserve credits
Follow the setup guide in the Actor's README.
Here are some recommended test scenarios:
await expectAsync(runResult).toHaveStatus('SUCCEEDED');await expectAsync(runResult).withLog((log) => {
// Neither ReferenceError or TypeErrors should ever occur
// in production code – they mean the code is over-optimistic
// The errors must be dealt with gracefully and displayed with a helpful message to the user
expect(log)
.withContext(runResult.format('ReferenceError'))
.not.toContain('ReferenceError');
expect(log)
.withContext(runResult.format('TypeError'))
.not.toContain('TypeError');
});await expectAsync(runResult).withStatistics((stats) => {
// In most cases, you want it to be as close to zero as possible
expect(stats.requestsRetries)
.withContext(runResult.format('Request retries'))
.toBeLessThan(3);
// What is the expected run time for the number of items?
expect(stats.crawlerRuntimeMillis)
.withContext(runResult.format('Run time'))
.toBeWithinRange(1 * 60000, 10 * 60000);
});await expectAsync(runResult).withDataset(({ dataset, info }) => {
// If you're sure, always set this number to be your exact maxItems
expect(info.cleanItemCount)
.withContext(runResult.format('Dataset cleanItemCount'))
.toBe(3); // or toBeGreaterThan(1) or toBeWithinRange(1,3)
// Make sure the dataset isn't empty
expect(dataset.items)
.withContext(runResult.format('Dataset items array'))
.toBeNonEmptyArray();
const results = dataset.items;
// Check dataset items to have the expected data format
for (const result of results) {
expect(result.directUrl)
.withContext(runResult.format('Direct url'))
.toStartWith('https://www.yelp.com/biz/');
expect(result.bizId)
.withContext(runResult.format('Biz ID'))
.toBeNonEmptyString();
}
});await expectAsync(runResult).withKeyValueStore(({ contentType }) => {
// Check for the proper content type of the saved key-value item
expect(contentType)
.withContext(runResult.format('KVS contentType'))
.toBe('image/gif');
},
// This also checks for existence of the key-value key
{ keyName: 'apify.com-scroll_lossless-comp' },
);