Verifying the order of calls across different mock objects in PHPUnit

Recently, I’ve faced this problem and I ended up with a (relatively) neat solution that, I think, might be worth sharing with the world. Imagine that you are testing a complex class with a large number of dependencies and that you need to verify that some methods on those dependencies will be called in an exactly defined order.

This is an excerpt of the initial implementation that just verifies these calls happen at all:

// So, these calls need to happen in $subject in the exact order they are defined here.
//
//
$table_names_mock->expects( $this->once() )
	->method( 'get_full_table_name' )
	->with( TableNames::ASSOCIATIONS )
	->willReturn( self::ASSOCIATIONS_TABLE_NAME );
$orderby_mock->expects( $this->once() )
	->method( 'register_joins' );
$element_selector_mock->expects( $this->once() )
	->method( 'initialize' );
$root_condition_mock->expects( $this->once() )
	->method( 'get_join_clause' )
	->willReturn( self::JOIN_FROM_ROOT_CONDITION );
$root_condition_mock->expects( $this->once() )
	->method( 'get_where_clause' )
	->willReturn( self::WHERE_FROM_ROOT_CONDITION );
// ... and so on

// Now, we test that the subject produces the expected result and all expectations are met.
//
//
$subject = new SqlExpressionBuilder( $table_names_mock );
$subject->setup( $join_manager_mock );
$query = $subject->build(
	$root_condition_mock,
	self::QUERY_OFFSET,
	self::QUERY_LIMIT,
	$orderby_mock,
	$element_selector_mock,
	$need_found_rows,
	$result_transformation_mock
);

$this->assertStringsEqualExceptWhitespace( $expected_query, $query );

Of course, this will be good enough in most situations, but if the order of calls is really important and you want to have truly thorough tests, read on…

$this->at()

At first, I thought it going to be easy. The test case has a method at( $index ) that provides an expectation of a call to happen on a mock at a specific order. But, as it turns out, the index is relative to each mock. Imagine we’re testing these two lines of code by providing mocks for both objects:

$first_object->do_something();
$second_object->do_something_else();

And the test:

$first_mock->expects( $this->at( 0 ) )->method( 'do_something' );
$second_mock->expects( $this->at( 1 ) )->method( 'do_something_else' );

The test will fail on the second line because the call on $second_mock also happens on index 0 from its perspective. Obviously, there’s no way we can verify that one call happens before the other using $this->at().

Closure within closure

What I ended up with is a solution that makes use of closures and the use() keyword with passing by reference (or not). First, let’s define two variables within our test method:

$current_call_index = 0;
$expected_at = 0;

The first one, $current_call_index, will be increased each time one of our “monitored” methods on a mock is called during testing. $expected_at will be used when setting up the expectations and it will hold the latest expected index of a method call.

Next, we define a closure that returns another closure, using those two variables. The “inner” closure will then then be used as a callback on mock’s method expectation, and it will assert it’s being called at the right time:

$verify_call_index = function( $return = null ) use ( 
	&$current_call_index, 
	&$expected_at 
) {
	$verification_callback = function() use( 
		$expected_at, 
		$return, 
		&$current_call_index 
	) {
		// ...
	};
	$expected_at++;

	return $verification_callback;
};

There are several important things happening here:

  • $verify_call_index uses both above-defined variables by reference. It builds the inner closure and increases $expected_at, so each time it’s called, this value will be higher.
  • The inner closure $verification_callback doesn’t use $expected_at by reference, so that value becomes fixed within the closure.
  • But $current_call_index is still used by reference.
  • We also pass around the $return argument in case the mocked method needs to return a value.

Now, let’s look into the $verification_callback body as well:

$verify_call_index = function( $return = null ) use ( 
	&$current_call_index, 
	&$expected_at 
) {
	$verification_callback = function() use( 
		$expected_at, 
		$return, 
		&$current_call_index 
	) {
		$this->assertSame( $expected_at, $current_call_index );
		$current_call_index++;

		return $return;
	};
	$expected_at++;

	return $verification_callback;
};

First, we assert that the (fixed) $expectected_at value matches the (passed by reference) $current_call_index, which means the method on the mock object has been called in the order in which the $verify_call_index has been used.

Then, we increase it and return a value the mocked method should return (if there is any).

Finally, let’s put this contraption to use when setting up the expectations:

$table_names_mock->expects( $this->once() )
	->method( 'get_full_table_name' )
	->with( TableNames::ASSOCIATIONS )
	->willReturnCallback( $verify_call_index( self::ASSOCIATIONS_TABLE_NAME ) );
$orderby_mock->expects( $this->once() )
	->method( 'register_joins' )
	->willReturnCallback( $verify_call_index() );
$element_selector_mock->expects( $this->once() )
	->method( 'initialize' )
	->willReturnCallback( $verify_call_index() );
$root_condition_mock->expects( $this->once() )
	->method( 'get_join_clause' )
	->willReturnCallback( $verify_call_index( self::JOIN_FROM_ROOT_CONDITION ) );
// ... and so on

When setting up the expectations, we call $verify_call_index, so that the willReturnCallback() method actually receives an inner closure $verify_callback, but each time with a value of $expected_at increased by one.

When the $subject is doing its thing, each call to one of these mocked methods will cause the $current_call_index value to be compared with the expected call index and then increased by one. And if the expected call index doesn’t match the current call index… the test fails.

Translation of “The Prusík Lineage 1515 – 1970” finished

After a very long time, I can finally present you with the translation of the brochure “Dějiny rodu Prusíků 1515 – 1970” by Josef Prusík.

First, it had to be scanned from an original print and then digitized (manually rewritten). During this process, my friend also added relevant footnotes with historical context, links to relevant resources and more than a dozen of vizual materials which are now part of the document.

Then, the text went through a professional translation from Czech to English and finally, it was typesetted with LaTeX.

The document is freely available on the page about our lineage with the hope that many of our relatives who no longer know Czech learn about their roots and ancestry.

How to run Varying Vagrant Vagrants aka VVV on Windows 10

If you ever need to install Varying Vagrant Vagrants on Windows 10, this is what worked for me (three times on three different computers).

Tested with Windows 10 64bit, VirtualBox 5.0.20, Vagrant 1.8.4, Cygwin 2.5.1 and VVV 1.3.0.

Requirements

  • Administrator privileges
  • Good enough hardware
  • Basic knowledge of VVV
  • Basic knowledge of linux shell
  • Patience
  • Luck (or a bloody sacrifice to some dark god)
  • User name that doesn’t contain spaces in C:\Users\%YOUR_NAME%

Note: If you do have spaces in your username, Cygwin is going to create your home directory in C:\Cygwin64\%YOUR USERNAME% which will later cause problems with running Vagrant. So, right after installing Cygwin, you need to change your home directory to something without spaces and other controversial characters.

Steps to follow

  1. Install Virtualbox.
  2. Install Vagrant.
  3. Install Cygwin.
    1. While installing, select (at least) these additional packages: git, curl and nano.
    2. Choose to create the desktop shortcut.
  4. Install the Microsoft Visual C++ 2010 SP1 Redistributable Package (x86) and don’t ask me why.
  5. Locate the file C:\Windows\System32\drivers\etc\hosts and change its privileges so that you (the current user) can read and write to it.
  6. Open Cygwin and continue working in the terminal.
  7. Get VVV: git clone https://github.com/Varying-Vagrant-Vagrants/VVV.git
  8. Adjust the Customfile to get over a nasty Vagrant/Windows issue:
    cd VVV
    nano Customfile
    and then add these two lines to the Customfile:
    config.vm.box = "trusty32"
    config.vm.box_url = "https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-i386-vagrant-disk1.box"

    and save with Ctrl+O, Ctrl+X.
  9. Install the hosts updater plugin: vagrant plugin install vagrant-hostsupdater.
  10. Install the triggers plugin: vagrant plugin install vagrant-triggers.
  11. Start the whole thing by vagrant up.
  12. Go grab a coffee or six, it will take a while even if your hardware is good.
  13. Let me know how it worked for you.

Background noise / ambient / calm music / sound loops

If you have something similar or weird in the same way, please share. :)

Disable captive portal detection on elementary OS Freya

Freya comes with a captive portal assistant that tries to help you to login (accept ToS) on public networks (like Wifi in a café, etc.). When you connect to such network, a new window opens with what Freaya thinks is the portal website. Actually, it opens https://elementary.io/ and expects it to be redirected to the portal.

However useful it can be for most people, in some situations you may wish to disable this feature. It is very simple, but difficult to find out. Just run this command:

sudo chmod -x /etc/NetworkManager/dispatcher.d/90captive_portal_test

And that’s it!

Reference: https://bugs.dogfood.paddev.net/elementaryos/+bug/1039042

Guide to dark GUI in elementaryOS and Lubuntu

Some time ago I decided I want to use dark GUI wherever possible, main reason being the fact that “white on black” causes much less strain of the eyes than the other way round. This post contains references to all the different guides, dark themes, software or other related tips I encountered and found useful in some way.

It may look like a lot of work, but if you spend a lot of time on the computer and care about your health, it is definitely worth it.

I provide guides for the two linux distributions I use, but they might be useful also for another Ubuntu-based distros. Continue reading Guide to dark GUI in elementaryOS and Lubuntu