Up & Away

Since it’s been so long since I updated here, here’s a quick run-down of what’s been going on: My family was blessed with our third baby, who is doing amazing ūüôā I’ve started a new job doing contract work and have been able to start learning a lot of new technologies – so I want to come back here and blog about them as I learn! I’ve finally gotten to actually learn and use nodejs and Vue, gotten to take a deeper dive into Laravel, and get a lot more experience with Docker, and much more. Plus, the projects I’m working on are directly helping people, which is something important to me.

bank-2130561_640.jpg

I’ve also decided to finally pursue a dream I’ve had for over 12 years – writing a game. I decided to learn C# so I could use Unity to finally accomplish this dream. I came up with the concept I wanted to create, and I’ve started working on it! So I’m going to start providing updates here, mostly for my own benefit to be able to look back and see what I’ve done.

In other news, I’ll be out in Utah in a few weeks for OpenWest 2017¬†so if you’ll be there be sure to come say hi to me!

Advertisements

#Hacktoberfest Week 2 Recap

We’re over halfway through the month of October, which means #Hacktoberfest is halfway done! Did you get your 4 PRs yet?

This past week, I got 5 more PRs done, two around unit tests for one project, a documentation fix for a unit-testing package for Laravel’s Eloquent, adding new functionality for the Snorlax library that I mentioned last week, and some more unit tests for another library.

If you haven’t started yet, you still have tons of time! Last year I think I got my 4 done at the very end, so don’t feel like you can’t participate at this point! Look for open #hacktoberfest¬†issues or just find a project you like and see what you can do to help out!

How Do I Know If My Unit Tests Are High Quality?

I get asked a lot how we can tell if our unit tests are actually any good. When doing test code review, there’s a lot of factors I look for to judge whether or not they are effective, quality unit tests. For a project I am working on, I’m looking for some examples of code in-the-wild – to evaluate your unit tests. If you’re interested in having a seasoned TDD pro look over your PHP Unit tests and analyze their quality and effectiveness – for FREE – please drop me a message. I’ll review the tests and respond with a report on what areas of unit testing you need to work on, and which specific tests have flaws. Your code will never be shared or published anywhere.

#Hacktoberfest Week 1 Recap

The first week of #Hacktoberfest is done! How’d you do? You just need to get one this week if you want to space out your four PRs over the month! Remember, they don’t have to be merged this month – so don’t worry if you don’t quite get it right, just give it a shot!

I thought it would be nice to do a weekly recap of the PRs I’m making this month as part of #hacktoberfest. I got 5 done so far, so I’m a little behind my own schedule, but I do have leads on a few other projects to work on. As I mentioned in my earlier post,¬†If you have a PHP project that needs tests or just have a bug you need help with, feel free to message me and I‚Äôll take a look, or just tag it with the hacktoberfest label and encourage people to work on it this month!

So, the 5 PRs I did so far included:¬†a small bugfix to UUID, adding requested test coverage to two¬†new-to-me¬†libraries, and then improving some tests in UUID. My fifth PR was actually to fix an issue I introduced in one of those new libraries where I made the tests incompatible with older versions of PHP that they did want to support, so I started work on another PR to fix that, but I’m still debugging the issue.

Next week I plan on finishing that fix to that library so they can still run on older PHP versions, adding more tests to another library, and adding another requested feature to the Snorlax library I found this week. The maintainers of this library are very friendly, and I’ve enjoyed working on their project – so go check it out¬†and see if it’s something you would use, or want to contribute to. They still have multiple issues open for #hacktoberfest!

Happy #Hacktoberfest!

For the third year in a row, Hacktoberfest is back! Hacktoberfest is a celebration of open source software encouraging everyone to contribute. The exciting part is if you make 4 Pull Requests to ANY Open Source project on Github in the month of October, you get an awesome shirt!

(This is last year’s shirt, they’re different each year)

Last year’s Hacktoberfest was the first time I actually did PRs to OS projects (other than my own small projects) so I have a special fondness for it. Those PRs were eventually merged, even though one took about 10 months to get merged in! But it’s okay if yours don’t get merged, you just have to try!¬†Any PRs count, including documentation, tests, whatever – So it’s a great way to get started. And this year, projects are tagging issues with a hacktoberfest label so you can easily find open issues in your language of choice to work on!

This year, I managed to get my four PRs done within the first four days – which inspired me to try to go for 31 (since October has 31 days). I’ll be focusing a lot on unit tests and testing related PRs, but also some generic bug fixing as well – and I’ll be posting about them here, so hopefully other people who aren’t already contributing to OSS can see how easy and fun it can be to make a small difference.

Follow along with me here on the blog, or check out my profile for October to see all of them at once! There’s also a neat tool that checks your progress for you, so here’s mine, and you can enter your username to track your own progress!

If you have a PHP project that needs tests or just have a bug you need help with, feel free to message me and I’ll take a look, or just tag it with the hacktoberfest label and encourage people to work on it this month!

Code Quality Enforcement via Git Pre-Commit Hook

There are a lot of¬†methods for measuring the quality of a codebase, but if you’re not running these tools locally, it’s much harder to make headway on improvements. I always suggest running quality checks before pushing changes, so I wrote a¬†git hook to enforce the quality checks before commit. A git hook is a¬†script that runs when a specific git action is taken, and you can read more about the various types of hooks.

I chose to set this up as a pre-commit hook rather than a pre-push hook to ensure the problems are fixed as quickly as possible, but if you are diligent about pushing often, you could use pre-push instead.

First, you’ll need to install the tools you want to use. I’m using PHP Mess Detector, and PHP CS Fixer (not a quality tool, but it’s also good to run style fixes locally to prevent conflicts).

> composer require --dev phpmd/phpmd
> composer require --dev fabpot/php-cs-fixer

We’ll also be using two Symfony components¬†in our script, so go ahead and install those too.

> composer require --dev symfony/console
> composer require --dev symfony/process

Now we’ll create a PHP script that¬†runs these tools against just the files being commited, and cancels the commit if the tools report problems. It also ensures the composer.lock file stays updated. This script needs to go in your project’s .git/hooks directory.

#!/usr/bin/php
<?php

require __DIR__ . '/../../vendor/autoload.php';

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\ProcessBuilder;

class CodeQualityTool extends Application
{
    private $output;
    private $input;
    private $projectRoot;

    //The locations of the files you want to measure. Add/remove as needed.
    const PHP_FILES = '/^(.*)(\.php)$/';

    public function __construct()
    {
        /** OS agnostic */
        $this->projectRoot = realpath(__DIR__ . '/../../');
        parent::__construct('Code Quality Tool', '1.0.0');
    }

    /**
     * @param $file
     *
     * @return bool
     */
    private function shouldProcessFile($file)
    {
        return preg_match(self::PHP_FILES, $file);
    }

    public function doRun(InputInterface $input, OutputInterface $output)
    {
        $this->input  = $input;
        $this->output = $output;

        $output->writeln('<fg=white;options=bold;bg=cyan> -- Code Quality Pre-Commit Check -- </fg=white;options=bold;bg=cyan>');
        $output->writeln('<info>Fetching files</info>');
        $files = $this->extractCommitedFiles();

        $output->writeln('<info>Fixing code style</info>');
        $this->codeStyle($files);

        $output->writeln('<info>Checking composer</info>');
        if (!$this->checkComposer($files)) {
            throw new Exception('composer.lock must be commited if composer.json is modified!');
        }

        $output->writeln('<info>Checking for messy code with PHPMD</info>');
        if (!$this->checkPhpMd($files)) {
            throw new Exception(sprintf('There are PHPMD violations!'));
        }

        $output->writeln('<fg=white;options=bold;bg=green> -- Code Quality: Passed! -- </fg=white;options=bold;bg=green>');
    }

    /**
     * @return array
     */
    private function extractCommitedFiles()
    {
        $files  = [];
        $output = [];

        exec("git diff --cached --name-status --diff-filter=ACM", $output);

        foreach ($output as $line) {
            $action  = trim($line[0]);
            $files[] = trim(substr($line, 1));
        }

        return $files;
    }

    /**
     * @param $files
     *
     * This function ensures that when the composer.json file is edited
     * the composer.lock is also updated and commited
     *
     * @throws \Exception
     */
    private function checkComposer($files)
    {
        $composerJsonDetected = false;
        $composerLockDetected = false;

        foreach ($files as $file) {
            if ($file === 'composer.json') {
                $composerJsonDetected = true;
            }

            if ($file === 'composer.lock') {
                $composerLockDetected = true;
            }
        }

        if ($composerJsonDetected && !$composerLockDetected) {
            return false;
        }
        return true;
    }

    /**
     * @param array $files
     *
     * @return bool
     */
    private function codeStyle(array $files)
    {
        $commandLineArgs = [
            'bin' . DIRECTORY_SEPARATOR . 'php-cs-fixer',
            'fix',
            null,
            '--level=psr2'
        ];

        foreach ($files as $file) {
            if (!$this->shouldProcessFile($file)) {
                continue;
            }

            $commandLineArgs[2] = $file;
            $processBuilder     = new ProcessBuilder($commandLineArgs);
            $processBuilder->setWorkingDirectory($this->projectRoot);
            $phpCsFixer = $processBuilder->getProcess();
            $phpCsFixer->run();

            exec('git add ' . $file);
        }
    }

    /**
     * @param $files
     *
     * @return bool
     */
    private function checkPhpMd($files)
    {
        $succeed = true;

        foreach ($files as $file) {
            if (!$this->shouldProcessFile($file)) {
                continue;
            }
            $processBuilder = new ProcessBuilder([
                'bin/phpmd',
                $file,
                'text',
                'phpmd-rules.xml'
            ]);
            $processBuilder->setWorkingDirectory($this->projectRoot);
            $process = $processBuilder->getProcess();
            $process->run();

            if (!$process->isSuccessful()) {
                $this->output->writeln($file);
                $this->output->writeln(sprintf('<error>%s</error>', trim($process->getErrorOutput())));
                $this->output->writeln(sprintf('<info>%s</info>', trim($process->getOutput())));
                $succeed = false;
            }
        }

        return $succeed;
    }
}

$console = new CodeQualityTool();
$console->run();</pre>

You’ll probably want to also commit the hook into the repository so you can share it between everyone who works on the project. An easy way to do this is to create a hooks directory in your project, copy the file in there, and then add some code in your composer.json that copies the files into the user’s .git/hooks folder after install. This will ensure that anyone who starts working on your project gets the hooks when they run composer. All you have to do is add the copy command into the scripts property of composer.json:

"scripts": {
    "post-install-cmd": [
        "cp hooks .git/hooks"
    ]
}

Please feel free to comment if you have any questions, or share how you’ve enforced use of code quality tools in your project!

How to use Behat and Phantom JS on Circle CI or Travis CI

A few months ago, I started working on some behavior tests for a javascript heavy website. I chose to write the tests in Behat, and wanted to use the Phantom JS driver because it would be faster than running Selenium 2 with a real browser. It also had to run on Circle CI. Once I got this working, several people asked for help getting theirs working, and some were using Travis CI. I threw together this demo repository which has a walkthrough of all the steps you need to follow to get these tools working together on either CI service.

Behat & PhantomJS: An example of using the Behat Selenium2 Mink Driver to power PhantomJS, and integrate with both CircleCI and TravisCI

Please let me know if this helps you, or if you have any questions!

Do you need testing help?

Last month, I posted this tweet offering up testing help for any PHP open source project. If you’d like to take advantage of this,¬†just ask! Send me a tweet, ping me on github, or¬†let me know below: