Tabular assertions with PHPUnit
With tabular assertions, you describe the expected outcome in a markdown-like table format. Here's why there useful, and how to use tabular assertions with PHPUnit.
thoughts and inspiration on designing, programming, and writing for the web
With tabular assertions, you describe the expected outcome in a markdown-like table format. Here's why there useful, and how to use tabular assertions with PHPUnit.
Today I tagged v1 of a new testing package: spatie/tabular-assertions
. It's a distillation of a testing method I've been using in client projects the past two years. The package supports both PHPUnit and Pest out of the box.
With tabular assertions, you describe the expected outcome in a markdown-like table format.
expect($order->logs)->toLookLike(" | type | reason | price | paid | | product | created | 80_00 | 80_00 | | tax | created | 5_00 | 5_00 | | tax | created | 10_00 | 10_00 | | shipping | created | 5_00 | 5_00 | | product | paid | 0_00 | 0_00 | | tax | paid | 0_00 | 0_00 | | tax | paid | 0_00 | 0_00 | | shipping | paid | 0_00 | 0_00 |");
Tabular assertions have two major benefits over other testing strategies: expectations are optimized for readability & failed assertions can display multiple errors at once.
For an in-depth introduction to tabular testing, I've written two separate guides for Pest & PHPUnit.
I haven't come across this exact method anywhere else, so I had to come up with a name. If there's prior art that matches this with a better name, I'd love to know!
The idea was inspired by Jest, which allows you to use a table as a data provider.
Snapshot testing is also closely related to this. But snapshots aren't always optimized for readability, are stored in a separate file (not alongside the test), and are hard to write by hand (no TDD).
Tabular assertions have been a huge help when comparing large, ordered data sets like financial data or a time series. I hope you find it useful too!
Frontend frameworks often use the mental modal that a component (or entire UI) is the result of running state through a pure function. Dan Abromov shares his thoughts on the current state of React with server components.
UI = f(data)(state)
Here's how I interpret it: in the mental model we're used to, a component is a pure function that returns UI based on its props. In this case, a Greeter
is rendered on the client.
function Greeter(props) { return <p>Hello, {props.name}!</p>;}
Say we have a server component that know who needs to be greeted before the code hits the client. In that case, the name will come from data available on the server.
function Greeter(data) { return function () { return <p>Hello, {data.name}!</p>; }}
The outer function is executed on the server, the inner on the client.
The true power lies in mixing and matching the paradigms. Say you want to read a translation string on the server, and greet a name on the client.
function Greeter(data) { return function (props) { return <p>{data.translations.hello}, {props.name}!</p>; }}
Before I launched Svelte by Example, I called for early access testers in this newsletter. I don't ask for feedback often, I had to push myself to do this.
Sometimes I'm scared of receiving feedback that I'll agree with, but would push back the release when I want to get it out. Sometimes I'm scared of receiving feedback that could invalidate the entire idea. Sometimes I know there are problems but hope they'll magically go away if I ignore them, feedback might resurface them. Sometimes I'm overconfident and don't think it's worth getting another opinion.
No good excuse to be found. These are fears, and it's worth getting over them. Because if any of them are rooted in truth, they'll com back and haunt me rather sooner than later.
A few people responded to my request (thank you!), and it quickly became clear the ask was worth it. The first version of Svelte by Example became way better because of it. The examples became more consistent, the design improved, a bunch of typos were edited out. I didn't process all feedback. Sometimes it doesn't match what you have in mind, and that's fine.
I've learned my lesson: time to get over myself and ask for feedback whenever I can.
The Svelte team set up a miniature documentation site highlighting the new features coming to Svelte 5. Here's a quick review of my three favorites: runes, events, and snippets.
Friendly reminder that you can disable Homebrew's default auto update behaviour with HOMEBREW_NO_AUTO_UPDATE=1
.
HOMEBREW_NO_AUTO_UPDATE=1 brew install git-secret
If you want to set and forget the setting, add it to your ~/.bashrc
or ~/.zshrc
.
export HOMEBREW_NO_AUTO_UPDATE=1
Last week I remembered the scroll-margin
property existed.
I was adding anchors to the headings of a page so visitors can directly link to a section. However, when that link was visited, the heading was glued against the top of the viewport. I prefer some margin between the browser chrome and the text to let it breath.
There's a CSS property for that: scroll-margin
. It does nothing in most cases, but when you visit a URL that points to an anchor on the page, it will offset the element from the viewport.
h2 { scroll-margin-top: 1rem;}
You can read all about scroll-margin
in the MDN docs.
An interesting thought exercise on working together, what's the optimal number of people to work as a team without spoiling the broth?
Let’s say you have 100 people. They can break into groups of any size. Each group gets 100 toothpicks. The goal: build the tallest structure with the toothpicks. What is the optimal group size?
I always enjoy reading about other people's processes.
99% of the time, this is how my note-taking process goes:
- I’m catching up on my RSS feed (on my phone in the Reeder app)
- I read something that strikes me as interesting, novel, or insightful.
- I copy/paste it as an blockquote into a new, plain-text note in iA writer.
- I copy/paste the link of the article into iA writer.
- I finish reading the article and copy/paste anything else in the article that strikes me.
- I add my own comments in the note as they pop into my head.
- I move on to the next article in my RSS feed.
- Repeat.
And:
I like to let my notes sit for a couple days (or even weeks). I find that if I come back to a note and still find it interesting/insightful that means it’s worth keeping, so I put in the work of cleaning it up and publishing it.
Time is underestimated as a filter for content.
I'm in the process of cleaning up some npm packages that haven't been touched in a while, and part of that is pruning all the cruft that has been accumulated in package.json
over the years.
I was looking for a canonical order to sort the keys by, and thought the order the npm docs specify the configuration options made sense.
Of course, after manually sorting one package.json
file—which probably took 2 minutes—I decided to spend 30 minutes to find our how I could automate sorting the other two.
Luckily there's an npm package for everything. With the sort-package-json
package you can go sort your package.json
s in a logical order.
npx sort-package-json **/*/package.json
That's all I had to do to keep my monorepo clean and tidy.
6 Dec 2023 via thunk.dev
Another blam on the Thunk blog:
Product Managers, Agile, and other product development processes solve communication problems. If everyone had telepathic powers, those problems would shrink or disappear. The next best thing to telepathy is a team who excels at communication. If your team communicates to bridge these knowledge gaps, your need for PMs and Agile will shrink.
6 Dec 2023 via ia.net
A nuanced take on AI by the makers of iA Writer.
A product management mindset from Jason Cohen.
If you do little things first, there’s no time for big things.
If do big things first, then you can fill in the smaller things.
And:
It’s not important that every sprint is perfectly balanced between all types of work. It is important that we’re balanced over a period several months, otherwise something important is getting starved. Indeed, it’s often wise to build imbalanced sprints intentionally, because that means greater focus, less context-switching, and therefore getting more quality work done.
29 Nov 2023 via gleam.run
Gleam is a statically typed language that runs on the Erlang virtual machine. From distance, it looks like a love child of Go and Elixir (count me in!)
One neat language feature is a todo
keyword. First-class syntax to mark a piece of code as work in progress and to stop the compiler from yelling at your half-implemented code.
fn favourite_number() -> Int { // The type annotations says this returns an Int, // but we don't need to implement it yet. todo} pub fn main() { favourite_number() * 2}
I love this warm color scheme from Steph Ango (kepano, CEO of Obsidian) based on printing inks and warm paper. It's my new default theme in Sublime Text—which is still my preferred editor when I'm not slogging through large PHP codebases.