Robert Basic's blog

Mocking hard dependencies with Mockery

by Robert Basic on December 23rd, 2014

One problem with unit testing legacy applications is that the code has new statements all over the place, instantiating new objects in a way that doesn't really makes it easier to test the code.

Of course, the easy answer to this is "Just refactor your application!", but that's almost always easier said than done.

If refactoring is an option, do it. If not, one option is to use Mockery to mock the hard dependencies.

One prerequisite to make this work is that the code we are trying to test uses autoloading.

Let's take the following code for an example:

<?php
namespace App;
class Service
{
    function callExternalService($param)
    {
        $externalService = new Service\External();
        $externalService->sendSomething($param);
        return $externalService->getSomething();
    }
}

The way we can test this without doing any changes to the code itself is by creating instance mocks by using the overload prefix.

<?php
namespace AppTest;
use Mockery as m;
class ServiceTest extends \PHPUnit_Framework_TestCase {
    public function testCallingExternalService()
    {
        $param = 'Testing';

        $externalMock = m::mock('overload:App\Service\External');
        $externalMock->shouldReceive('sendSomething')
            ->once()
            ->with($param);
        $externalMock->shouldReceive('getSomething')
            ->once()
            ->andReturn('Tested!');

        $service = new \App\Service();

        $result = $service->callExternalService($param);

        $this->assertSame('Tested!', $result);
    }
}

If we run this test now, it should pass. Mockery does it's job and our App\Service will use the mocked external service instead of the real one.

The problem whit this is when we want to, for example, test the App\Service\External itself, or if we use that class somewhere else in our tests.

When Mockery overloads a class, because of how PHP works with files, that overloaded class file must not be included otherwise Mockery will throw a "class already exists" exception. This is where autoloading kicks in and makes our job a lot easier.

To make this possible, we'll tell PHPUnit to run the tests that have overloaded classes in separate processes and to not preserve global state. That way we'll avoid having the overloaded class included more than once. Of course this has it's downsides as these tests will run slower.

Our test example from above now becomes:

<?php
namespace AppTest;
use Mockery as m;
/**
 * @runTestsInSeparateProcesses
 * @preserveGlobalState disabled
 */
class ServiceTest extends \PHPUnit_Framework_TestCase {
    public function testCallingExternalService()
    {
        $param = 'Testing';

        $externalMock = m::mock('overload:App\Service\External');
        $externalMock->shouldReceive('sendSomething')
            ->once()
            ->with($param);
        $externalMock->shouldReceive('getSomething')
            ->once()
            ->andReturn('Tested!');

        $service = new \App\Service();

        $result = $service->callExternalService($param);

        $this->assertSame('Tested!', $result);
    }
}

And that should be pretty much it. If nothing else, it should make parts of old code easier to test.

For anyone interested, I put the example code up on Github.

Tags: mockery, php, testing, unit tests.
Categories: Development, Programming.

Configuring the trackpad and touchpad behaviour for Thinkpad T540p on Fedora 21

by Robert Basic on December 22nd, 2014

This is the last post about the Thinkpad and Fedora. At least for a while. Promise.

With the new generation of Thinkpads, Lenovo decided to change the touchpad. They removed the 5 physical buttons from the touchpad area and left us with one bigger touchpad. To click anything it's now either touch click (two-finger click for right clicks) or one can push the entire touchpad down, the clickpad. The touchpad has different regions for getting left/middle/right clicks. Sort of.

And for the first time I completely agree with reviews on the internet. This thing is horrible.

If you want both the touch and push clicks, forget it. It just won't be usable how inaccurate this thing is. If the touch click is turned on, it will get in the way of your typing. You forget yourself and do a push click with the touch click enabled? Here's some double/triple click for you. Just don't even bother with the touch click, turn it off.

You're left now with the trackpad and touchpad for movement, and the clickpad for clicks. Again, pain. You push down the touchpad to click aaand... Good luck and I hope you clicked the thing you wanted. Your finger will move, just a little bit and so will the cursor. And here's the kicker. Touchpad movements can't be turned off. At least not in a easy and intuitive way.

After quite some time searching the internet for a fix, I found a forum post explaining how to get this thing usable (sadly, I can't find that post again to link to it):

Section "InputClass"
    Identifier "TrackPad with buttons only"
    MatchDriver "synaptics"
    Option "SoftButtonAreas" "65% 0 0 0 50% 65% 0 0" # emulate right and middle buttons
    Option "AreaBottomEdge" "1" # disable moving but not buttons
EndSection

Save this as 99-thinkpad-clickpad.conf in /etc/X11/xorg.conf.d/. The touch parts of the touchpad are disabled so we have the trackpad for movements and the clickpad for buttons. I just need to tweak the left/middle/right button clicks a bit more, because there are times when I accidentally do a middle click.

Oh, and now there's no mouse scroll because that's touchpad only.

Tags: t540p, thinkpad, laptop, fedora, touchpad, clickpad, trackpad.
Categories: Blablabla, Software.

Battery charge thresholds for Thinkpad T540p on Fedora 21

by Robert Basic on December 19th, 2014

This week I got myself a new laptop, a Thinkpad T540p. One of the features it has is that the battery's life can be prolonged by setting custom charging thresholds.

The start charge threshold tells the battery to start charging only when the charge drops bellow that limit, and the stop charge threshold tells the battery to stop the charging when the upper limit is reached. I set the start threshold to 40% and the stop threshold to 70%. I think this should be good enough for me as I mainly use my laptop at home where it's always plugged in.

After a bit of a digging around, I managed to get it working under Fedora 21.

The main thing to know is that there is tp_smapi for older Thinkpad's, and tpacpi-bat for the newer ones. I have a T540p so it's the latter for me.

There is also tlp, but at the moment I couldn't get it to work completely because it has no packages for Fedora 21 yet. Fedora 20/19 should be OK though, and for those I would probably go with tlp.

After git cloning the repository, run the install perl script, and if all went OK, set the start threshold like:

./tpacpi-bat -s ST 1 40

and the stop threshold:

./tpacpi-bat -s SP 1 70

Where 1 is the battery number, starting from 1, and 40 and 70 are the start and the stop thresholds in percentage.

These settings should remain after a reboot, but what I noticed is that they are gone after taking out and putting the battery back in. When I tried to set the threshold parameters again, I couldn't do it as the system was complaining about the missing acpi_call kernel module. I re-run the perl install script from tpacpi-bat and got it working again.

I'll be on the lookout for the tlp packages for Fedora 21, that looks like it would work nicer than this.

Now the only last bit I'm missing is the battery cycle count, but seems current kernels don't support it yet.

Tags: fedora, laptop, thinkpad, t540p, tpacpi-bat, battery.
Categories: Software, Development.

Xdebug and private /tmp on Fedora

by Robert Basic on December 16th, 2014

This one was a bit weird and needed some figuring out. Xdebug profiler output files were not being generated in the /tmp directory.

I wanted to do some profiling with xdebug. I set all the necessary configuration settings in my php.ini, restarted apache, confirmed xdebug is present and configured correctly with php -i | grep xdebug, appended ?XDEBUG_PROFILE=1 aaaand! Nothing. Nothing in /tmp, the default profiler output directory. Double checked paths, permissions, nope, nothing. No profiler files were generated.

find /tmp -name "cachegrind*"

listed the files in

/tmp/systemd-httpd.service-X9iE20R/tmp/

What the?

Apparently systemd services in Fedora can have this setting called PrivateTmp and services with this setting set to true are started with a private /tmp directory. Something something security.

Well then. I created a /var/log/xdebug directory, changed the owner to apache and set the xdebug.profiler_output_dir to that new directory and all is well again.

Hey, I learned something new today.

Tags: xdebug, php, fedora, systemd.
Categories: Development, Software, Programming.

Ack in vim

by Robert Basic on December 9th, 2014

I started using vim 3, 4 years ago. The way I use it is that I started out with no plugins and with a handful of lines in .vimrc. It is far too easy to cram all kind of stuff into it and then get lost in the myriads of key combinations. To prevent that, I decided to slowly add in bits and pieces I find lacking in my day to day usage of vim. Also allows me to first learn the editor and later the plugins.

Today was an exceptional day as I added not one, but two plugins to vim! And that is a big change for me as the total number of plugins I now use is 4.

The first one I added is ack.vim. It's a nice little plugin to run ack from within vim and show the results in a split window. It's rather easy to use, one just basically types :Ack search_string and that's it. The one thing I immediately disliked is that it sort of gets lost in the subdirectories of a project.

A quick google search found a solution for that little problem as well: vim-rooter. It doesn't do much, just changes the current working directory of vim to the project root when you open a file.

And that's basically it. Nice and fast searching with ack from vim.

Tags: ack, vim, plugins, rooter.
Categories: Development, Software, Programming.