Testing isn’t just for testers

I originally prepared this talk and published it as an article on LinkedIn:


A quick look at Eleventy and Static Site Generators

Eleventy is a Static Site Generator.

What does that mean?

Benefits of a SSG:
Speed — Faster load times — no parsing of server side scripts
Simplicity — Easy to deploy — no server configuration, just upload
Security — Can lock down — nothing exposed on website to exploit server side
Savings — Can be run on cheap shared hosting, even free services like Netlify

Let’s take a look at Eleventy:


The first claim — “Eleventy is a simpler static site generator”
Simpler compared to what?

Some popular alternative SSGs include:

Jekyll – Ruby, you’ll need to install a Ruby development environment — and keep it up to date
Hugo – Go language, but you don’t need to have Go installed because it is a static binary — a command line application

On the Javascript site, like everything else, rather than a community coalescing around a single solution there are many alternatives. Which may be good, since it results in experimentation with different methods.

You can see an exhaustive list of SSGs at:


Javascript SSGs tend to coalesce around client-side frameworks, React, Vue, etc.

Popular React based frameworks:

Gatsby – react based
Next.js. — with server side component

React frameworks allow you to use React components and compose them with JSX and then use those components to create a static site. But with a twist that they “rehydrate” a rich client side framework (React) based application.

Vue based SSGs include:

Nuxt.js — inspired by Next.js
Gridsome — inspired by Gatsby
Vuepress — designed for generating documetation based sites, developed by Evan You, the creator of Vue.js and used for the Vue.js documentation

These Javascript frameworks are more heavyweight clients. While they are “static” in the sense that they don’t have a server side component, they’re really more of a “JAMstack” application.

JAM means “Javacript and Microservices” — or rather “Javascript, APIs, and Markup”) meaning that a web application is more like a mobile app, the presentation logic is all handled on the browser, which builds an app using the browser’s Javascript engine, and then fetches data by calling and changes the presentation by adding and subtracting components from the DOM.

The DOM is the “Document Object Model” — how the browser keeps track of your HTML programmatically and renders it. When you click a button, the browser fires an event that maps to the DOM element. Changing the DOM — adding and removing HTML elements dynamically, and attaching events such as “onclick” to elements to trigger those changes or to make AJAX calls to fetch data from the server.

AJAX is “Asynchronous Javascript and XMLHTTPRequest”
XMLHTTPRequest is the way that browsers can fetch data from a web server — like loading a page, but without reloading the page.

That’s the basis of Javascript client-side frameworks like React, Angular, and Vue. A bunch of logic, written in Javascript to execute on the browser, renders in the browser to add and substract elements, fetch data, and update status with the server — all without doing full page loads.

The benefit of doing this — at least theoretically — is that you don’t have to send as much information back and forth from the server to the client on every request. Rather than sending the tags and so forth — as well as all the other elements that don’t change, including images, JavaScript, and CSS, you just send the data that changes, and then update the DOM accordingly.

In practice, well… often the load isn’t that heavy, and static assets like images & libraries are cached on the browser anyway. And updating the DOM via Javascript events can take more time and resources than just re-rendering HTML. But still, client side frameworks give people a way to organize the logic of complex sites, and compose them programmatically.

[Aside] I’m somewhat ambivalent on client side frameworks, but that may be because I haven’t explored them deeply enough, or that I just haven’t found one that’s implemented in a way I like.

Eleventy is also a Javascript based SSG and it would be familiar to someone using Jekyll or Hugo. It was inspired by Jekyll. It doesn’t try to be a full stack Javascript solution, and that’s where the “simpler” claim comes in.

The other claim that Eleventy makes is flexibility. It is agnostic about the template language you use, for instance. You can use Liquid — the template system used by Jekyll (and Github Pages) by default, but you can swap that out for several other Javascript based templates, such has Nunjucks (very similar to Liquid), Handlebars, Mustache, HAML, Pug, or EJS. You can even use plain Javascript objects to “render” content — so you can use programmatic logic and composition to build elements or pages or partial pages. Much the same as you would building Vue or React components. It may even be possible to process React components to generate a static site with Eleventy, although I don’t know if anyone has done it.

Most people who choose Eleventy do so for simplicity and flexibility, so getting tied into a complex framework isn’t their goal. If you prefer one of those, you’re probably better off using one of the other SSGs geared specifically for your framework.

Eleventy is designed to work with static HTML files, and then add simple logic —
conditionals: “if this is true, then display it”
iteration: “render an element for each item in a list”

Another feature of Eleventy (and other SSGs like Jekyll) is that it allows you use plain text files — actually Markdown — and generate HTML pages from them. That way you can write a blog, or documentation, or whatever, by composing a simple text file, with a bit of simplified markup for headings, lists, and so forth, and then add a header, footer, sidebar, etc. and then display them as web pages without having to add all the tags. Or — and here’s the key — without having to use a content management system (like WordPress) and edit all your content in a fancy textarea — and save it to a database.

In the next post, we’ll go ahead and jump in and see how that works.

How I got started testing

I decided to go back to college after several years away from school. I had been working construction and I was now my own boss as a drywall contractor. But winter had come, and work was slow, and while I’d enjoyed working alone and singing along (badly) to my own rock music with no one to hear me, truth is, I was lonely.

I’m the type of guy who has trouble admitting that sort of thing, even now, more than 20 years later.

So I went back to school.

I didn’t know what to study, since I’d only toyed around with college before, taking classes like snowboarding and pottery, and ditching calculus and anything hard or early in the morning (calculus was both.) It took me a year of working at fast food jobs and saving up after high school to get into college, but I finally made it. And now I was sloughing off. I only stayed in calculus as long as I did because I was sitting next to a pretty girl — but I never even spoke to her.

College was fun, but I wasn’t heading anywhere. I’d randomly chosen “Nuclear Engineering” as a major, but knew that wasn’t working out. I took art and writing classes, and was trying to teach myself to play piano. I hitched rides up to the mountains to go snowboarding whenever I could and worked nights cleaning the grill, washing dishes, and waxing the floors at the college cafeteria. I was pissing off my roommates and blowing off my classes and partying, but I wasn’t really having fun. I was directionless, and I knew it.

Luckily, an old friend from high school came along. He was a year older and took me under his wing. I ended up quitting college and working drywall for him and a couple other friends that summer. We travelled all over for work. We both liked painting and music and we tried starting a band. We were going to call ourselves “Various Artists” and our first album was going to be called “Greatest Hits”. You’ve probably seen our stuff in the bargain bin at discount stores or on a rack between the beef jerky and souvenirs at a gas station truck stop.

Money was decent, and we worked hard and played hard. One time, when we’d been gone for several weeks doing several jobs out of state, and we came back to our apartment and it was a mess. The sink was overflowing with dirty dishes growing mold. Tools were strewn all over the living room with grease stains on the carpet. Our bedroom was our painting studio. We threw out all the dishes and bought new ones at the local thrift shop. I think we got evicted eventually.

When he took off, I took over doing drywall touch up jobs and thought about going back to school. But it was easy to live the college lifestyle in a college town and not go to college. I had a scam where I’d ask a girl out by offering to make her and her roommates a fancy dinner, take her to the grocery store to buy steak or salmon or whatever I wanted, and then I’d have them do all the chopping vegetables and stuff, and then I’d leave the mess for her to clean up.

I’ve often thought about opening a restaurant, but having worked in several, I had no illusions about the amount of work involved and so I never did. And when I met a girl many years later who said she loved washing dishes, I married her (but that part of the story comes much later.)

The point of the story (so far) is that I wasn’t very responsible. I had a lot of initiative, but I wasn’t good at the follow through. I could work hard — when I wanted to.

While I was away, my dad and my brother and a couple of other guys had started a small local ISP (that’s “internet service provider”) back when you dialed up on your home phone line and listened for the static and beeps of your external modem connect to another modem in a shed in some guy’s backyard and that’s how you connected to the internet.

Back then, they were in it for text based adventure games like “Legend of the Red Dragon” and bulletin board chat and message boards. That’s what the internet was. I knew nothing about it.

But my dad was an entrepreneur. He’d started his own independent logging company when he was younger, and built a lumber mill with partners. He know the forestry industry. And he taught us kids to love the mountains and wilderness.

But when the lumber mill failed, he bought a VCR and satellite TV shop before most people knew about that sort of thing. As a consequence, I was well versed in the classics like Bruce Lee and Indiana Jones. I’d seen Star Wars a hundred times before Return of the Jedi came out.

That didn’t last, and he went back to work at a lumber mill. And that meant another move.

We moved around a lot when I was a kid. When people hear that I went to eleven different schools, they often guess (correctly) that my dad was in the military. But the day I was born was his last day of service. We moved for work. And I think maybe my dad had a bit of a restless spirit. At least I like to blame him for mine.

There was a bit of a pattern to our moves. We’d move to Montana, but making a living in Montana is hard, so he’d end up taking a job somewhere else, we’d move there, and then a few years later, we’d move back to Montana. He loved the mountains and forests. After all my travels, guess where I’ve ended up?

Anyway, so I was going back to school after being away for several years. And because my dad’s latest business venture was a fledgling internet service after closing down a pizza restaurant he’s started. (I was the delivery driver for a while in high school), I’d created a few web pages and learned how to do a few technical support things like tell people to restart their computer and how to install and configure TCP/IP on old Microsoft Windows PCs.

So I enrolled as a “Computer Science” major. But I still ended up taking courses like “Figure Painting” and “Radio Production”. But I didn’t try going back into calculus. I signed up for a more basic math class. There was a pretty girl with a floppy hat and a floral print dress, who had a pretty smile and strawberry blonde hair in that class. She was also in my art class.

(Hi Bambi!)

How we got together is a long story, and since I don’t want to make my wife jealous (spoiler alert, I married someone else), I won’t dwell on it here, I’ll skip to the relevant part.

I wasn’t doing great in computer science. It was fun, but I didn’t do the homework. But I wasn’t doing it in radio production either. Between work and play, school was suffering again. I was snowboarding again, and doing drywall again. And dating the aforementioned girl. And I’d found a writing group online and was doing my best imitation of Hemingway.

But one day, one little event changed things, as they sometimes do. Okay, lots of little events change things all the time, but this one fits into this part of the story.

I tagged along with her to her public speaking class to lend moral support. She gave a cute little speech about cold pricklies and warm fuzzies, but right after her speech was a friend from my computer science classes. We stuck around to listen to his speech too.

He was a big lumbering guy, who was way smart. The type of guy who groans when he gets asked if he was on the football team. Maybe he was, but that’s not what he was into. You know, like the tall guy who is always asked if he plays basketball because he’s tall — but he’s really a musician and nobody wants to hear about that. Anway, this guy was way into computers, and so his speech was about about computers.

He talked about Linux. Linux was this new, free operating system that was way better than Windows and was going to take over the world. You could even set it up to run “X Windows”. Everything cool had an “X” in front of it in the 1990s. And X Windows looked pretty cool. I was blown away by his talk.

I think I was the only one in the room listening. Everyone else clapped politely, but I had a ton of questions. I immediately delved into learning about Linux. I found out that the operating system that my dad’s ISP used to coordinate all the modems in the shed other people’s computers and connect them to the internet was Linux. I had an operating systems class that taught about Unix and I used the server (logged in as root) to do my homework. I started answering questions in class. I started writing shell scripts and perl CGI scripts for web pages.

I dropped out of school again after my girlfriend left to tour the country in some singing group, but I kept studying on my own. I had a stack of Linux and other technical books. I got a place with my brother and we’d fight over computer time — he wanted to play games or chat and I wanted to boot into Linux and mess around with code. I’d hang out on sites like Hacker News (not the Reddit site, but one for l33t real h4x0rs! — Cult of the Dead Cow & stuff.)

And then one day, I got a job at Microsoft. The evil empire. By then, there was a small but growing coterie of Linux fans, and I was one of them. We’d bash Bill Gates and Microsoft and Windows. And now I was joining them.

Here’s how it happened.

I bought an old run down drug house and spent months hauling out trash, gutting the place, fixing the roof and foundation, redoing the plumbing, etc. I was miserable and not making any money. I was actually running up a lot of debt.

Remember that I mentioned I’d joined a writing group online? I think I’d stopped writing much — both fiction & poetry or code. But I had a friend who was a published author and she needed some help with building her website. I didn’t do much to help her, but we became friends.

Her husband was a programmer, and he’d just gotten a job at Microsoft. He told me I should apply. I think he figured, in his modest way, that if they’d hired him, they’d hire anyone. Turns out he was right. I didn’t have a degree, I didn’t have much practical experience. But I applied. I did a phone interview and thought nothing of it.

Then one day a couple weeks later I got a call. I was in the middle of screwing sheetrock into the ceiling, so I almost missed it. But it was a recruiter at Micrsoft. She wanted to know if I could be there for an interview tomorrow. Actually, 4 interviews on 4 different teams. One of them was on my buddy’s team. Sure, I said.

Seattle Washington was an 8 hour drive away. I left immediately. I left a screw hanging halfway out of a 4′ x 8′ piece of sheetrock only halfway attached to the ceiling. I left all my tools scattered on the floor of the house that had already been burglarized (and I lost a bunch of tools) only a few months before.

I took a shower, bought a nice shirt, called up my friends and asked if I could stay at their place, and drove all the rest of that day to Seattle. Next morning, I went through 2 rounds of interviews with 2 teams and 2 more the next day. I failed with one group when I couldn’t tell what a SCSI cable was and I couldn’t play foosball. I failed with another group when I couldn’t do their whiteboarding coding exercise to their satsifaction. I got an offer from my buddy’s team (probably because of his influence) and with another team because I answered a brain teaser in a unique (and very inefficient way).

Say you have a room with 3 lights and you’re standing outside the room with 3 light switches. How can you flip only two switches before entering the room and know which switch goes to which light bulb? Or something like that.

I asked a few questions, hypothesized about using a multimeter, and then thought of this:

“How long can I take to figure it out?”

“As long as you like,” the interviewer responded.

So I said I’d flip on one switch, wait a year, and then flip on another switch and walk in. One light would be on, and one would be burned out.

“That’ll work,” he said.

Inefficiency, combined with cleverness, pays off. Or at least it did at Microsoft at the height of their business 20 years ago.

I got the job, and turned down my buddy’s team, because this sounded more interested. Was I ever wrong.

But I’ll save that story for another day. Suffice it to say, that’s how I ended up working at Microsoft, and that’s how I ended up doing test automation for a living.

And now, on a rainy Monday morning in May 20 years later, after working for lots of companies doing test automation, I live in the woods in Montana, I work in a yurt (a big round tent) with a fire burning in the wood stove, and I’m typing this on an old laptop running Linux because I don’t want to go outside and dig post holes for a fence & barn for my animals.

And I’m trying to figure out how to rebuild my business doing test automation and didn’t feel like coding yet.

Scheduling tests to monitor websites

If you have access to your crontab you can set a Selenium script to run periodically. If you don’t have cron, you can use a VM (with Vagrant) or Container (with Docker) to get it.

Cron is available on Linux & Unix systems. On Windows, you can use Task Scheduler. On Mac, there is launchd, but it also includes cron (which wraps launchd).

You could also set up a job to run on a schedule using a continuous integration server such as Jenkins. Or write a simple, long running script that runs in the background and sleeps between executions.

I have a service that runs Selenium tests and monitoring for my clients, and use both cron and Jenkins for executing test runs regularly. I also have event-triggered tasks that can be triggered by a checkin or user request.

Each line represents a task with schedule in the following format:

#minute   #hour     #day      #month    #weekday  #command

# perform a task every weekday morning at 7am
*         7         *         *         1-5       wakeup.sh

# perform a task every hour
@hourly python selenium-monitor.py

You can edit crontab to create a task by typing crontab -e

You can view your crontab by typing crontab -l

If you just want to repeat your task within your script while it’s running, you can add a sleep statement and loop (either over an interval or until you kill the script).

#!/usr/bin/env python

from time import sleep
from selenium import webdriver

sites = ['https://google.com', 'https://bing.com', 'https://duck.com']

interval = 60 #seconds
iterations = 10 #times

def poll_site(url):
	driver = webdriver.Chrome()
	title = driver.title
	return title

while (iterations > 0):
	for url in sites:
	iterations -= 1

See the example code on github:

#!/usr/bin/env python
from time import sleep
from selenium import webdriver
sites = ['https://google.com', 'https://bing.com', 'https://duck.com']
interval = 60 #seconds
iterations = 10 #times
def poll_site(url):
driver = webdriver.Chrome()
title = driver.title
return title
while (iterations > 0):
for url in sites:
iterations -= 1
view raw selenium-monitor.py hosted with ❤ by GitHub

Originally posted on Quora:


Sauce Connect tunnel for Sauce Labs real device cloud setup

I have helped a lot of Sauce Labs users, and one of the common challenges is setting up a Sauce Connect tunnel in order to test against your internal environment.

The first thing you need to do is download and install the tunnel. It is a standalone command line executable available for Windows, Mac, and Linux. I recommend using Linux.

You can download Sauce Connect at:


Once downloaded, you need to extract the package to get the ‘sc’ binary from the /bin directory.

wget https://saucelabs.com/downloads/sc-4.5.4-linux.tar.gz
tar -xvzf sc-4.5.4-linux.tar.gz 
cd sc-4.5.4-linux/bin

To start the tunnel, simply pass your Sauce Labs username and access key from the command line or set the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables:


There are quite a few other options that can be passed, and I won’t talk about them here, but you can see them by typing sc --help at the command line or by reading the documentation here:


In order to start a tunnel for the Sauce Labs mobile real device cloud, you need to pass 1 additional parameter to point the the mobile datacenter.  You also need to specify a different API KEY (see screenshot below). 

So your command should look something like this:

sc -x https://us1.api.testobject.com/sc/rest/v1 -u $SAUCELABS_USERNAME -k $SAUCECONNECT_API_KEY -i $TUNNEL_IDENTIFIER

See also the sample script sauce-connect.sh that includes additional parameters for setting different port number, log file, etc. (which will conflict if you run on the same host as another tunnel.

-x https://us1.api.testobject.com/sc/rest/v1 \
-B all \
-i my_rdc_tunnel \
–se-port $PORT \
–logfile /tmp/sc.$PORT.log \
–pidfile /tmp/sc.$PORT.pid \
view raw sauce-connect.sh hosted with ❤ by GitHub

Here is the full documentation for real device tunnels:


Set custom name for JUnit Parameterized tests

For JUnit parameterized tests, you can add a descriptive name based on the parameter like this:

@Parameters(name="{index}: {0} {1}")
public static Collection<Object[]> data() {
  return Arrays.asList(new Object[][] {
    { "x", "y" },
    { "foo", "bar" },
    { "hello", "world" }

This will output test results like:

[0: x y]
[1: foo bar]
[2: hello world]

See also:


Checking XPATH and CSS Selectors in the browser console

There are a couple of magic functions you can use to inspect and parse an HTML document while you’re reading it in the browser.

$x() allows you to check an XPATH. It’s basically a shorthand for document.evaluate(xpath, document);

$$() allows you to check a CSS Selector. It’s basically a shorthand for document.querySelectorAll(css);

On Chrome $x() returns an XPathResult — just like document.evaluate() — which can only be inspected with the function iterateNext(). But on Safari and Firefox $x() will return an Array — just like $$() and document.querySelectorAll().

These shortcut functions can save some typing and mental effort.

Thanks to Andrew Krug from lazycoder.io for pointing out $x().

Keep Testing Weird

I’m at SauceCon 2019 in Austin, Texas which is a test automation conference put on by my employer, Sauce Labs.

The theme for the conference is “Keep Testin’ Weird” — a play on the city’s slogan “Keep Austin Weird”.

So I thought to myself, what’s weird about testing? It didn’t take long to come up with a long list. Testing is weird, and I’d love to hear all the weird stories everyone else has about testing.

Besides all the weird things that happen while testing — testing itself is pretty weird.

If you’re a software tester, you realize this the moment you’re asked to describe what you do for a living that it’s not like other professions. Personally, I’ve taken to just telling people “I work with computers” — and see how far down the rabbit hole they actually want to go. Which is a weird thing to do, but I guess I’m a little weird myself.

You kinda have to be weird to go into testing — or at least to stay at it very long. And not just because of all the weird stuff you encounter.

First of all, I don’t know anyone who ever deliberately went into testing. At least not until recently. It wasn’t really a known career path, and even for those who knew about it, testing wasn’t really highly regarded.

The act of testing itself is kinda weird. You’re not actually creating anything, but you have to be creative to be an effective tester. In fact, one of the qualities that make someone a good tester is that they like to break things. Testing is destructive — you have to destroy the product to save it. The greatest delight of a true tester is to find a truly catastrophic bug that is triggered in a really weird way.

You have to be a bit off to take pleasure in telling people that for all their hard work, it’s still not right. Testing is critical. Your job is to be, not just the bearer of bad news, but to actively go out looking for it, and since you have to justify your job, hoping to find it.

You have to be a bit immune to social criticism to be able to do so day after day. That means you probably don’t mind being weird.

Finding bugs is hard, especially when you’re handed this black box and you’re not only supposed to figure out how it works, but how it doesn’t. It takes a certain kind of perverse creativity to even come up with ways to test things effectively.

When you report a bug that can only happen under strange esoteric circumstances, it’s often dismissed as irrelevant and how that would never happen under real world conditions. But the real world is weird, and it’s just those types of weird issues that developers and designers don’t anticipate, that happen in production, and cut across layers to expose fundamental flaws or weaknesses in systems.

That you need to justify testing is really weird. Testing improves quality, and quality is the primary source of value, but testing isn’t considered valuable. Testing is often left out or cut short. And always being under-budgeted and under-resourced with inadequate time.

Testers have to have a varied skillset. You have to test things that you don’t understand. And you’re expected to find bugs and verify requirements. Without knowing the code, without understanding the requirements, and in many cases, without the practical experience of being an end user of the product you’re testing.

You’re not a developer, but you have to understand code. You’re not a product designer, but you have to understand the design and requirements in more depth than perhaps anyone else. You’re probably going to need to know not only how to test software, but how to build and deploy it.

How do you know when your job as a tester is done? Have you ever tried to define done? There’s development done, feature complete, deployed… But then there’s monitoring and maintenance, bug fixing and new features. Maybe you’re only really “done” when software has been deprecated and abandoned.

Is testing ever done? At some point you just have to draw a line and call it good enough. You can’t prove a negative, but your job is to declare a negative — this works with no issues — with a certain degree of certainty.

Test automation is weird. Writing automated tests while software is under development is like building the train as it’s running down the track, while the track is being laid — and testing that it works while it’s doing so.

Automation is meant to do the same thing over and over again, but why is it that test automation is so much harder to maintain than production code?

Automation code is throwaway code, but one of the greatest values comes when you can change the production code out from underneath it and the automation still passes — which means that the software isn’t broken. So you write test code to find bugs, but it actively prevents bugs from happening. That’s weird.

There is a lot more weirdness around testing and test automation but like any good tester knows, when you’re writing or speaking, you have to know when to stop, so I’ll end it here.

But I want to hear from you all. I’d like to ask you to share your thoughts and experiences about why testing is weird, what weirdness have you seen while testing, and what can we all do to keep testing weird?

Tensorflow Python Virtualenv setup

Learning a new technology can be challenging, and sometimes setup can slow you down.

I wanted to experiment with machine learning and just follow along with a a few tutorials.  But first, I had to get a compatible environment up and running.

So I thought I’d document it here, and after several bookmark/footnote tweets,

### install pyenv and virtualenv 
brew install pyenv
brew install pyenv-virtualenv

### initialize pyenv
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

### install python 3.6 with pyenv
pyenv install 3.6.5

### create a virtualenv for using tensorflow
pyenv virtualenv 3.6.5 tf
pyenv activate tf

### install dependencies in virtualenv using
pip pip install --upgrade pip
pip install tensorflow

### optionally use jupyter web interface
pip install jupyter
jupyter notebook

### use tensorflow in your app
import tensorflow as tf

I created a gist.


Here’s the great intro to Deep Learning video I followed along with:

I’ll follow up with a quick screencast video showing how the steps look together for those who want to see it.

How do you practice TDD for Android development projects?

TDD is more of a strategy then a certain process or tool. The idea is that testing drives your development — writing tests first, making sure tests pass to indicate development is complete, and continuously testing on each change to make sure no regressions or new bugs slip in.

You can use Espresso, Robotium, or UIAutomator directly for automating the mobile app but testing the UI is inherently slow, can be brittle, and may not be possible (or easy) to write UI (or end-to-end) tests while an app is under development. The UI may not be testable, or back end services may not be complete at early stages.

With test driven development, you want to use your tests to inform what you develop. This informs what you should develop first, and it helps you to write your application in a way that is testable.

If you have some feature that needs tested — for example: delivering different size media (images & video) based on available bandwidth and screen size, testing this with the UI seems to make sense, since it is a UI feature.

But try writing your test the way you want it to look, not the way it actually behaves in the app. Start with your assertion:


Now we try to satisfy that

First we need to get our values. Where does deviceScreenWidth come from? How do we determine imageWidth?

imageWidth is probably sent to the response processor so that when it sends the image URL it resizes the image — or selected the appropriately sized image.

That’s a design decision that’s already being influence by our tests. Maybe we want standard sizes — small, medium, large instead of trying to support every possible pixel width. Maybe isEqualTo should test within a range instead of just equal.

For deviceScreenWidth we need some representation of our device that includes it’s screen size. Do we get it from the userAgent or does the device send DisplayMetrics via an API? Is it passed from a service or a lookup table? Maybe we need a test of the function that passes a device identifier from the userAgent and calculates based on known values.

Now we know what code to write — and another test to write.

This can be a bit of a rabbit hole, but we don’t have to tackle everything at once.

In our unit test we just need to have an imageWidth and a deviceScreenWidth. We can make a note of what functions and parameters are needed to get this information, but for now we can just implement the functions immediately needed — and even make our first test pass by having those functions return hard coded values.

A nice simple test might look like this:

public void testImageCalculator() 
    device = new DeviceMetaData(SAMSUNG_GALAXY_S6);
    deviceScreenWidth = device.getDisplayMetrics(device).screen.width;
    imageWidth = getImageSizeForDevice(deviceScreenWidth);
    assertThat(imageWidth).isBetween(deviceScreenWidth, mediumDeviceMaxWidth);

Now we know what we need to develop next — the functions that make this test pass. A DeviceMetaData container class, something that gets display metrics for the device, and what we really care about (at this time) — the getImageSizeForDevice() function.

NOTE: This was originally an answer to a question on Quora