The Arbitrary.make function allows for the creation of random values that align with a specific Schema<A, I, R>.
This function returns an Arbitrary<A> from the fast-check library,
which is particularly useful for generating random test data that adheres to the defined schema constraints.
When generating random values, Arbitrary tries to follow the schema’s constraints. It uses the most appropriate fast-check primitives and applies constraints if the primitive supports them.
For instance, if you define an age property as:
Schema.Int.pipe(Schema.between(1, 80))
the arbitrary generation will use:
fc.integer({ min: 1, max: 80 })
to produce values within that range.
Patterns
To generate efficient arbitraries for strings that must match a certain pattern, use the Schema.pattern filter instead of writing a custom filter:
Example (Using Schema.pattern for Pattern Constraints)
1
import {
import Schema
Schema } from"effect"
2
3
// ❌ Without using Schema.pattern (less efficient)
By using Schema.pattern, arbitrary generation will rely on fc.stringMatching(regexp), which is more efficient and directly aligned with the defined pattern.
Transformations and Arbitrary Generation
When generating arbitrary data, it is important to understand how transformations and filters are handled within a schema:
// May produce empty strings due to ignored NonEmpty filter
9
var console:Console
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(newError('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(newError('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
schema1: Takes into account Schema.maxLength(500) since it is applied after the Schema.Trim transformation, but ignores the Schema.NonEmptyString as it precedes the transformations.
schema2: Adheres fully to all filters because they are correctly sequenced after transformations, preventing the generation of undesired data.
Best Practices
To ensure consistent and valid arbitrary data generation, follow these guidelines:
Apply Filters First: Define filters for the initial type (I).
Apply Transformations: Add transformations to convert the data.
Apply Final Filters: Use filters for the transformed type (A).
This setup ensures that each stage of data processing is precise and well-defined.
Example (Avoid Mixed Filters and Transformations)
Avoid haphazard combinations of transformations and filters:
1
import {
import Schema
Schema } from"effect"
2
3
// Less optimal approach: Mixing transformations and filters
consttrimmed: <string>(annotations?:Schema.Annotations.Filter<string, string> |undefined) => <I, R>(self:Schema.Schema<string, I, R>) =>Schema.filter<Schema.Schema<string, I, R>>
Verifies that a string contains no leading or trailing whitespaces.
Note. This combinator does not make any transformations, it only validates.
If what you were looking for was a combinator to trim strings, then check out the trim combinator.
@since ― 3.10.0
trimmed(),
import Schema
Schema.
constlowercased: <string>(annotations?:Schema.Annotations.Filter<string, string> |undefined) => <I, R>(self:Schema.Schema<string, I, R>) =>Schema.filter<Schema.Schema<string, I, R>>
Verifies that a string is lowercased.
@since ― 3.10.0
lowercased()),
7
{
8
strict?:true
strict: true,
9
decode: (fromA:string, fromI:string) => string
decode: (
s: string
s) =>
s: string
s.
String.trim(): string
Removes the leading and trailing white space and line terminator characters from a string.
trim().
String.toLowerCase(): string
Converts all the alphabetic characters in a string to lowercase.
toLowerCase(),
10
encode: (toI:string, toA:string) => string
encode: (
s: string
s) =>
s: string
s
11
}
12
)
Customizing Arbitrary Data Generation
You can customize how arbitrary data is generated using the arbitrary annotation in schema definitions.
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(newError('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
The annotation allows access the complete export of the fast-check library (fc).
This setup enables you to return an Arbitrary that precisely generates the type of data desired.
Integration with Fake Data Generators
When using mocking libraries like @faker-js/faker,
you can combine them with fast-check to generate realistic data for testing purposes.
Example (Integrating with Faker)
1
import {
import Arbitrary
Arbitrary,
import FastCheck
FastCheck,
import Schema
Schema } from"effect"
2
import {
constfaker:Faker
The faker instance for the en locale.
Language: English
Endonym: English
This instance uses the following locales internally (in descending precedence):
Create another arbitrary by mapping all produced values using the provided mapper
Values produced by the new arbitrary are the result of applying mapper value by value
// transform an Arbitrary producing {r,g,b} integers into an Arbitrary of '#rrggbb'
@param ― mapper - Map function, to produce a new element based on an old one
@param ― unmapper - Optional unmap function, it will never be used except when shrinking user defined values. Must throw if value is not compatible (since 3.0.0)
@returns ― New arbitrary with mapped elements
map(() => {
7
// Each time the arbitrary is sampled, faker generates a new name
8
return
constfaker:Faker
The faker instance for the en locale.
Language: English
Endonym: English
This instance uses the following locales internally (in descending precedence):
en
base
faker.
Faker.person: PersonModule
person.
PersonModule.fullName(options?: {
firstName?: string;
lastName?: string;
sex?: SexType;
}): string
Generates a random full name.
@param ― options An options object.
@param ― options.firstName The optional first name to use. If not specified a random one will be chosen.
@param ― options.lastName The optional last name to use. If not specified a random one will be chosen.
@param ― options.sex The optional sex to use. Can be either 'female' or 'male'.
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(newError('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).