Test::Mojo::Role::ElementCounter - Test::Mojo role that provides element count tests


Say, we need to test our app produces exactly this markup structure:

Select a product!

The test we write:

use Test::More; use Test::Mojo::WithRoles 'ElementCounter'; my $t = Test::Mojo::WithRoles->new('MyApp'); $t->get_ok('/products') ->dive_in('#products ') ->element_count_is('> li', 3) ->dive_in('li:first-child ') ->element_count_is('a', 1) ->dived_text_is('a[href="/product/1"]' => 'Product 1') ->element_count_is('+ li > a', 1) ->dived_text_is('+ li > a[href="/products/Cat1"]' => 'Cat 1') ->dive_in('+ li > ul ') ->element_count_is('> li', 2) ->element_count_is('a', 2) ->dived_text_is('a[href="/product/2"]' => 'Product 2') ->dived_text_is('a[href="/product/3"]' => 'Product 3') ->dive_out('> ul') ->element_count_is('+ li a', 1); ->dive_reset ->element_count_is('#products + p', 1) ->text_is('#products + p' => 'Select a product!') done_testing;


Note that as of Mojolicious version 6.06, Test::Mojo implements the exact match version of element_count_is natively (same method name). This role is helpful only if you need dive methods or ranges.


A Test::Mojo role that allows you to do strict element count tests on large structures.


You have all the methods provided by Test::Mojo, plus these:


$t = $t->element_count_is('.product', 6, 'we have 6 elements');
$t = $t->element_count_is('.product', '<6', 'fewer than 6 elements');
$t = $t->element_count_is('.product', '>6', 'more than 6 elements');

Check the count of elements specified by the selector. Second argument is the number of elements you expect to find. The number can be prefixed by either < or > to specify that you expect to find fewer than or more than the specified number of elements.

You can shorten the selector by using dive_in to store a prefix.


$t = $t->dive_in('#products > li ');

$t->dive_in('#products > li ')
    ->dive_in('ul > li ')
    ->element_count_is('a', 6);
    # tests: #products > li > ul > li a

To simplify selectors when testing complex structures, you can tell the module to remember the prefix portion of the selector with dive_in. Note that multiple calls are cumulative. Use dive_out, dive_up, or dive_reset to go up in dive level.

Note: be mindful of the last space in the selector when diving. ->dive_in('ul')->dive_in('li') would result in ulli selector, not ul li.

Note: the selector prefix only applies to element_count_is and dived_text_is methods. It does not affect operation of other methods provided by Test::Mojo


$t = $t->dive_out('li');
$t = $t->dive_out(qr/\S+\s+(li|a)\s+$/);

$t->dive_in('#products li ')
    ->dive_out('li'); # we're now testing: #products

Removes a portion of currently stored selector prefix (see dive_in). Takes a string or a regex as the argument that specifies what should be removed. If a string is given, it will be taken as a literal match to remove from the end of the stored selector prefix.


# these two are equivalent
$t = $t->dive_up;
$t = $t->dive_out(qr/\S+\s*$/);

Takes no arguments. A shortcut for ->dive_out(qr/\S+\s*$/).


$t = $t->dive_reset;

Resets stored selector prefix to an empty string (see dive_in).


$t = $t->dive('#products li:first-child ')
    ->dived_text_is('a' => 'Product 1');

Same as Test::Mojo's text_is method, except the selector will be prefixed by the stored selector prefix (see dive_in).

NOTE: as of version 1.001006, Test::Mojo's text_like will be used with a regex constructed to be the exact match, with any amount of whitespace before and after the string. This is done to workaround Mojolicious Donut breaking its whitespace handling in Mojo::DOM and by extention Test::Mojo, and leaving useless whitespace all over the place.


Fork this module on GitHub: [](


To report bugs or request features, please use []( If you can't access GitHub, you can email your request to `bug-test-mojo-role-elementcounter at`




You can use and distribute this module under the same terms as Perl itself. See the LICENSE file included in this distribution for complete details.