Mocking database for unit tests queries in Modx

Hey everyone,

I’m working on trying to add unit tests to some code in an app i’ve been building on Modx and I’ve been struggling with figuring out how to re-structure my code to be more unit testable.

Take the below extremely basic snippet that looks at a custom database table passing it $name to see if there are any active customers existing with that are similar to that name.

I could unit test this by simply using runSnippet('doesCustomerExisit') and having the whole snippet run, but I’d like to not depend on the database and instead mock out that query.

I was thinking I could create a class in an external file that contains some methods to handle the query and just pass back the results - but i’m not sure how to run this snippet code from within phpUnit and pass it a mocked version of the class.

// Get name from scriptProperties
$name = $modx->getOption('name', $scriptProperties, null);

// remove extra whitespace around the name
$name = preg_replace('/\s+/', ' ', $name);
$name = trim($name);

// create query of Customers class
$query = $modx->newQuery('Customers');
$query->select('name');
$query->where(array(
    'name:LIKE' =>  $name,
    'active' => 1
));

$count = $modx->getCount('Customers',$query);

return $count == 0 ? false : true;

It’s is difficult because $modx/xPDO is the database. It might help to put the three lines that create the query in a separate method called getQuery(). That will also help you when you want to create an integration test to see if the created query looks like it should.

Once the query is moved, you can mock getQuery() (it can return null since you’re not going to use the return value). The, create a mock of the $modx object with one mock method: getCount(). You can have getCount() return whatever you like.

I use Codeception (which wraps PhpUnit and provides Acceptance testing). With Codeception, you can automate Acceptance tests that log in to the Manager, click on links and buttons, and verify the results on the screen. I mention Codeception because this mocking code will look different than PhpUnit’s, but here’s a mock $modx class I needed for a unit test:

$modx = $this->make(modX::class, array(
            'getTableName' => function () {
                return '`modx_site_content`';
            }
        ));
        assertInstanceOf('modX', $modx);

This was to test a processor of mine. The only MODX method it ever called in the methods I was testing was getTableName().

If you want to be more elaborate, you can (in theory), create an in-memory database that stores either an array of user objects just an array of rows (like the modx_users table). The, you can mock $user->save(), $modx->getObject(), etc.

Thanks for the suggestions Bob, I’ll look into Codeception as I do want to get some automated acceptance testing added in as well and try separating out the query and see if I can make mocking it work.

Also, after thinking about it a bit, I’m wondering if a better pattern to get started testing these one off snippets that need to query the db would be to create a new object in the setUp method so that when I run test cases against it I know what is in there and then just remove it in the tearDown method.

It doesn’t really remove the database from the test like I was wanting but it would give a better end to end test of all of the code.

I’ve done that, it works well, and later you can create integration tests where you instantiate MODX and use the actual db.

Another way to go (the poor man’s mock) is to create a class of your own that extends the modX class, override the MODX methods you want to mock, and use your object in the unit tests.

I’m not sure about PhpUnit, but in Codeception, you can put that all in _bootstrap.php so you don’t have to instantiate MODX before every test. assuming that you want to mock the same methods in all tests in the test file.