NAME
DBIx::Fast::Test::Mock - a DBIx::Fast with canned answers, for unit tests
SYNOPSIS
use DBIx::Fast::Test::Mock;
my $mock = DBIx::Fast::Test::Mock->new(dialect => 'MariaDB');
$mock->on(qr/FROM users WHERE id = \?/ => [ { id => 7, name => 'ana' } ])
->on('SELECT COUNT(*) FROM orders' => 3)
->on(qr/^INSERT INTO `orders`/ => { last_id => 501 })
->on(qr/^UPDATE/ => sub ($sql, @binds) { { rows => 2 } })
->on(qr/^INSERT INTO `coupons`/ => { error => "Duplicate entry 'X'", err => 1062 });
my $db = $mock->db; # a real DBIx::Fast - pass it to the code under test
my $user = $db->hash('SELECT * FROM users WHERE id = ?', 7); # { id => 7, name => 'ana' }
is($mock->called(qr/^UPDATE/), 1);
is_deeply($mock->last_query, { sql => '...', binds => [...] });
DESCRIPTION
Unit tests for code that uses DBIx::Fast, without a database server. The mock sits below DBIx::Fast (a private DBI driver), so every method runs its real code - SQL generation, identifier quoting, the dialect's upsert, JSON columns, timestamps, typed exceptions, txn - and only the server is replaced: each statement is recorded and answered from the rules given with "on". dialect selects the SQL DBIx::Fast generates (MariaDB, mysql, Pg or SQLite).
Keep the mock object alive while its db is in use.
METHODS
new
DBIx::Fast::Test::Mock->new(
dialect => 'MariaDB', # MariaDB (default), mysql, Pg, SQLite
strict => 0, # 1: an unmatched statement is a DB error
args => { timestamps => {...}, after_write => sub {...} },
);
args are passed to "new" in DBIx::Fast (everything except the connection).
db
The DBIx::Fast instance.
on
$mock->on($match => $response);
$match is a regex, or an SQL string compared with whitespace collapsed. The first rule that matches answers. $response is:
- a plain scalar - one row, one column (
val,count) [ {...}, ... ]- rows as hashes (columns in sorted key order)[ [...], ... ]- rows as arrays (columnscol1,col2...){ columns => [...], data => [...] }- rows with an explicit column order{ rows => N, last_id => N }- a write: affected rows (default 1) and the idinsertreturns{ error => $msg, err => $code, state => $sqlstate }- a database error, raised as the matching DBIx::Fast::X class (err => 1062on MariaDB is aDBIx::Fast::X::Duplicate)sub ($sql, @binds) { ... }- computed per call; returns any of the above
Unmatched statements: reads return no rows, writes affect one row (an INSERT gets an increasing last_id) - or, with strict, fail.
With the Pg dialect the first insert/insert_ignore into a table also runs a catalog lookup of its primary key (as against a real server; it is recorded like any statement). Unanswered, the insert takes the last_insert_id path; answer it to get INSERT ... RETURNING:
$mock->on(qr/pg_index/ => 'id') # the primary key column
->on(qr/^INSERT INTO "orders"/ => [ [501] ]); # RETURNING "id" -> 501
queries, sql, last_query
my @q = $mock->queries; # ({ sql => ..., binds => [...] }, ...)
my @s = $mock->sql; # just the statements
my $q = $mock->last_query;
Transactions appear as BEGIN, COMMIT and ROLLBACK.
called
my $n = $mock->called(qr/^DELETE/);
reset, clear_rules
Forget the recorded statements / the rules.
dialect, strict
Readers for the constructor arguments.
SEE ALSO
AUTHOR
SeHarrys
LICENSE
This is free software under the Artistic License 2.0.