Steve Grunwell

Open-source contributor, speaker, and electronics tinkerer

Tag: Testing

Black and white photo of two female-presenting scientists looking into adjoining microscopes

The True Meaning of Code Coverage

Whether you’re auditing third-party libraries or trying to toughen up your own test suites, you’ve likely come across a project boasting “100% code coverage”. It just sounds so official, right? 100% of this code is covered by automated tests, so it must be good!

I have good news and bad news. The good news is that a high percentage of code coverage does generally reflect an above-average effort to test the code—I’d much rather use a library with 60% code coverage than 15% (or zero!), wouldn’t you?

The bad news is that a high percentage of code coverage doesn’t mean that the code is necessarily working the way it’s supposed to!

In this post, we’re going to look at what code coverage really means as a metric (specifically within the context of PHP, but these lessons should be broadly applicable), as well as several ways that code coverage can give you a false sense of confidence.

Continue reading→

A typewriter with a sheet of paper reading "Equality"

Strict Equality for Better Code

A major focus of my day job right now is cleaning up the PHP in a decades-old monolith, which includes tests written for two different test runners by hundreds of engineers over the years.

I could write a book on the horrors I’ve seen (and currently have at least half a dozen blog posts in draft state), but I’m not interested in raking anyone over the coals for past engineering decisions—honestly, it’s to be expected with any project this size and age. Instead, I wanted to take a moment to talk about one of the most prevalent oversights made by engineers of all levels: strict equality.

Continue reading→

Taco, a black cat, peeking out of a white drawer

The Beauty of PHP Value Objects

At last year’s php[tek], one of my biggest “holy cow, why haven’t I been doing this?!” moments came from my friend Andrew Cassell when he explained PHP Value Objects in the context of Domain-Driven Design.

Put simply, a Value Object is an immutable object that encapsulates some data and will always be in a valid state.

Continue reading→

A series of pipes and gauges along a wall

Travis CI for WordPress Plugins

If you’ve spent much time on GitHub, you’ve probably come across repositories with green badges that look like this: Build: Passing

A lot of repositories will have these badges/shields, as they indicate that the last run of the Continuous Integration (CI) pipeline for this repository “passed” (e.g. everything is working as expected).

Now, Continuous Integration can mean a lot of things: maybe the project has a wealth of well-written tests that are all passing, or the simply that coding standards are all up-to-par. No matter the coverage level, this green badge indicates to potential users of your code that it satisfies the quality checks you’ve put in place.

Better yet, once we have a Continuous Integration pipeline in place, we can make it a prerequisite for pull requests to be merged. If you’re tired of PRs that don’t respect your project’s coding standards, ignore PHP compatibility rules, or otherwise produce lots of overhead, automating the high-level testing can save you lots of time.

Continue reading→

Join us for WooSesh: October 9 and 10

WooCommerce Custom Orders Table @ WooSesh 2019

As you may or may not be aware, one of the many projects I maintain at work is the WooCommerce Custom Orders Table plugin. The plugin takes all of the individual post meta entries that come along with most every order — billing and shipping addresses, order totals, and way more — and stores them in a flat database table, optimized for performance.

The WooCommerce Custom Orders Table plugin has been crucial on some of our bigger stores at Liquid Web, but one does not simply change how WooCommerce stores order data without being very careful and deliberate. That’s why I’m pleased that Brian and Patrick at WooSesh reached out and asked if I could talk all about it!

Continue reading→

The Gateway Arch in St. Louis, MO

Confidently Testing WordPress @ WordCamp US 2019

The term “WordCamp” can encompass a number of types of conferences. Some are friendly homecomings, groups of a couple hundred people, a few tracks, and a small group of volunteers working tirelessly to spread the love of WordPress to their local communities. These are the camps where I spend most of my time: Dayton, Detroit, Kent, Grand Rapids, and Ann Arbor are all a fairly short drive away, and hotels are typically affordable.

There are other WordCamps, though: the destination camps. The WordCamps that people will travel from all over the country for. WordCamps Miami, Chicago, Phoenix, Orange County — places where people will flock from all over the country to attend and rub elbows with some of the biggest names in WordPress.

The biggest of these whale-sized WordCamps, however, is WordCamp US. Like it’s European counterpart, WordCamp US is meant to be the country’s premiere WordCamp. That’s why I’m pleased to announce that this year, I’ll be speaking at WordCamp US 2019!

Continue reading→

A stylized, neon "Portland, Oregon" Old Town sign

Testing WordPress & Code Review @ Cascadia PHP 2019

Last year, I was fortunate enough to spend a week and a half on the West Coast, splitting time between Portland, OR and San Diego, CA for the first installments of two new community PHP conferences: Cascadia PHP and WavePHP.

Sadly, WavePHP isn’t happening this year, but I’m thrilled to announce I’ll be returning to Portland for Cascadia PHP 2019!

Continue reading→

Looking out from the Smithsonian Natural Science museum on a foggy day

My First Workshop @ php[world] 2019!

It’s no secret that I’m a big fan of php[world]: php[world] 2014 was my first big speaking engagement, and I’ve spoken at the 2016 and 2018 editions of the conference (and attended in 2017). That’s why I’m thrilled to announce that I’ll be returning to Washington, D.C. this October for php[world] 2019!

Continue reading→

The Ledyard Building in Grand Rapids, Michigan

Confidently Testing WordPress @ WordCamp Grand Rapids 2019

Grand Rapids is one of those towns I just can’t get enough of: hot on the heels of Beer City Code 2019, I’m excited to announce I’ll be returning to Grand Rapids for WordCamp Grand Rapids 2019!

I’ll be giving my Confidently Testing WordPress talk, which has been making the rounds this year (it’s almost as if people need a bit of help getting into testing 🤔)

Continue reading→

A gray squirrel eating a nut

Two Talks @ WordCamp Kent 2019

WordCamp Kent has grown to be one of my favorite, must-attend WordCamps, and they keep accepting me to speak. This year, I’m fortunate enough to again be giving two talks at WordCamp Kent 2019!

Continue reading→

Page 1 of 2

Be excellent to each other.

Matomo encountered an error: Uncaught ValueError: Path cannot be empty in /var/www/matomo/vendor/matomo/doctrine-cache-fork/lib/Doctrine/Common/Cache/FileCache.php:253 Stack trace: #0 /var/www/matomo/vendor/matomo/doctrine-cache-fork/lib/Doctrine/Common/Cache/FileCache.php(253): file_put_contents() #1 /var/www/matomo/vendor/matomo/doctrine-cache-fork/lib/Doctrine/Common/Cache/PhpFileCache.php(95): Doctrine\Common\Cache\FileCache->writeFile() #2 /var/www/matomo/vendor/matomo/cache/src/Backend/File.php(73): Doctrine\Common\Cache\PhpFileCache->doSave() #3 /var/www/matomo/vendor/matomo/cache/src/Backend/Chained.php(72): Matomo\Cache\Backend\File->doSave() #4 /var/www/matomo/vendor/matomo/cache/src/Lazy.php(69): Matomo\Cache\Backend\Chained->doSave() #5 /var/www/matomo/core/Translation/Loader/LoaderCache.php(50): Matomo\Cache\Lazy->save() #6 /var/www/matomo/core/Translation/Translator.php(365): Piwik\Translation\Loader\LoaderCache->load() #7 /var/www/matomo/core/Translation/Translator.php(331): Piwik\Translation\Translator->loadTranslations() #8 /var/www/matomo/core/Translation/Translator.php(97): Piwik\Translation\Translator->getTranslation() #9 /var/www/matomo/core/Piwik.php(915): Piwik\Translation\Translator->translate() #10 /var/www/matomo/core/Plugin/MetadataLoader.php(57): Piwik\Piwik::translate() #11 /var/www/matomo/core/Plugin.php(147): Piwik\Plugin\MetadataLoader->load() #12 /var/www/matomo/core/Plugin.php(138): Piwik\Plugin->reloadPluginInformation() #13 /var/www/matomo/core/Plugin/Manager.php(1266): Piwik\Plugin->__construct() #14 /var/www/matomo/core/Plugin/Manager.php(1227): Piwik\Plugin\Manager->makePluginClass() #15 /var/www/matomo/core/Plugin/Manager.php(1107): Piwik\Plugin\Manager->loadPlugin() #16 /var/www/matomo/core/Plugin/Manager.php(1092): Piwik\Plugin\Manager->reloadActivatedPlugin() #17 /var/www/matomo/core/Plugin/Manager.php(944): Piwik\Plugin\Manager->reloadActivatedPlugins() #18 /var/www/matomo/core/Plugin/Manager.php(121): Piwik\Plugin\Manager->loadPlugins() #19 /var/www/matomo/core/FrontController.php(325): Piwik\Plugin\Manager->loadActivatedPlugins() #20 /var/www/stevegrunwell.com/wp-content/plugins/wp-piwik/classes/WP_Piwik/Request/Php.php(40): Piwik\FrontController->init() #21 /var/www/stevegrunwell.com/wp-content/plugins/wp-piwik/classes/WP_Piwik/Request/Php.php(18): WP_Piwik\Request\Php->call() #22 /var/www/stevegrunwell.com/wp-content/plugins/wp-piwik/classes/WP_Piwik/Request.php(63): WP_Piwik\Request\Php->request() #23 /var/www/stevegrunwell.com/wp-content/plugins/wp-piwik/classes/WP_Piwik.php(1038): WP_Piwik\Request->perform() #24 /var/www/stevegrunwell.com/wp-content/plugins/wp-piwik/classes/WP_Piwik.php(1205): WP_Piwik->request() #25 /var/www/stevegrunwell.com/wp-content/plugins/wp-piwik/classes/WP_Piwik/TrackingCode.php(16): WP_Piwik->updateTrackingCode() #26 /var/www/stevegrunwell.com/wp-content/plugins/wp-piwik/classes/WP_Piwik.php(296): WP_Piwik\TrackingCode->__construct() #27 /var/www/stevegrunwell.com/wp-includes/class-wp-hook.php(324): WP_Piwik->addJavascriptCode() #28 /var/www/stevegrunwell.com/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters() #29 /var/www/stevegrunwell.com/wp-includes/plugin.php(517): WP_Hook->do_action() #30 /var/www/stevegrunwell.com/wp-includes/general-template.php(3208): do_action() #31 /var/www/stevegrunwell.com/wp-content/themes/grunwell-2018/footer.php(43): wp_footer() #32 /var/www/stevegrunwell.com/wp-includes/template.php(810): require_once('...') #33 /var/www/stevegrunwell.com/wp-includes/template.php(745): load_template() #34 /var/www/stevegrunwell.com/wp-includes/general-template.php(92): locate_template() #35 /var/www/stevegrunwell.com/wp-content/themes/grunwell-2018/archive.php(84): get_footer() #36 /var/www/stevegrunwell.com/wp-includes/template-loader.php(106): include('...') #37 /var/www/stevegrunwell.com/wp-blog-header.php(19): require_once('...') #38 /var/www/stevegrunwell.com/index.php(17): require('...') #39 {main} thrown (which lead to: Session must be started before any output has been sent to the browser; output started in /var/www/stevegrunwell.com/wp-includes/script-loader.php/3015)