Skip to main content

Rage Against the Machinery

 

I often review and collaborate on unit tests at work. One of the patterns I see a lot is this:

  • there are a handful of tests, each about a page long
  • the tests share a lot of functionality, copy-pasted
  • the test data is a complex object, created inside the test
  • the test data varies little from test to test.

In Kotlin-ish pseudocode, each unit test might look something like this:

@Test
fun `test input against response for endpoint`() {

	setupMocks()
	setupTestContext()
	...
	
	val input = Object(a, OtherObject(b, c), AnotherObject(d))
	...
	
	val response = someHttpCall(endPoint, 
		method,
		headers,
		createBodyFromInput(input)
		)
...

val expected = Object(w, OtherObject(x, y), AnotherObject (z)) val output = Object(process(response.getField()), otherProcess(response.getOtherField()), response.getLastField()) assertEquals(expected, output) }

...

While these tests are generally functional, and I rarely have reason to doubt that they're useful, getting an idea of the intent versus the implementation of each test, and the coverage of the tests as a whole, is difficult or time-consuming, or both, and readers pay some kind of cost every time they come to the tests. 

The machinery is in the way of the work. Rage!

At its heart, the example above is checking, for some function, that an input (a, b, c, d) produces an output (w, x, y, z). So, what I try to do, if the investment seems worthwhile given other priorities, is refactor to separate the test machinery from the test data.

For our example, it might look something like this:

@ParamterisedTest("testDataSet")
fun `test input against response for endpoint`(testData) {

	setupEverything()
	
	val input = createInputObject(testData.input)
	val expected = createExpectedObject(testData.expected)
	
	val response = callEndpoint(input)	
	val output = extractOutputObject(response)
	
	assertEquals(expected, output)
}

fun testDataSet() {

    // each row is a pair of input, expected output
    val testData = [
		([a, b, c, d], [w, x, y, z]),
		...
		]
}

This has a single unit test that loops against rows of data (in testDataSet) and boilerplate is moved to standalone functions with intentful names.

Key to this for me is that the data appears as cleanly as possible in the test data set. I want to have only the stuff that varies in the test data and all the constants factored away. This means that once I understand the function being checked by the test, I can review the data alone and look for patterns or missing cases, and adding new cases is quick.

If I feel I need it, I can add names or comments as part of the data and dump it on failure. I might also use formatting to make the visualisation more helpful:

testData = [
		("customer C special case", [a, b, C], X),
		("vanilla case",            [a, b, _], Y),
		...
		]

If you're thinking that this looks like data-driven tests, you're right. One of the things I enjoy about frameworks like Karate is that they have a human-readable syntax for this kind of setup that removes even more boilerplate noise:

		| case                    | input     | expected|
		| customer C special case | [a, b, C] |     X   |
		| vanilla case            | [a, b, _] |     Y   |
		...

I think this is a healthy way to set up the unit tests when there are sets of similar cases that vary only in data.

But there's another benefit too: this is effectively a little test rig inside the tests and I can use it to explore the feature that I am testing. See The Love of a Loop and a List for more on that. This means that part of my calculation about whether it's worth performing the refactoring is the extent to which I think it might help me to test right now. 

If I decide that I can't pay the refactoring tax, but think it might still be useful to explore from the unit tests, I can parameterise one of the cases and write a for loop that calls it with my data. Yes, that sounds horrid but it's throwaway code, created to help me answer the questions that I want to ask the system under test.

The machinery should serve my needs, not trigger my rage.
Image: https://flic.kr/p/2mtwLRd
Syntax highlighting: Pinetools

Comments

Popular posts from this blog

Testing (AI) is Testing

Last November I gave a talk, Random Exploration of a Chatbot API , at the BCS Testing, Diversity, AI Conference .  It was a nice surprise afterwards to be offered a book from their catalogue and I chose Artificial Intelligence and Software Testing by Rex Black, James Davenport, Joanna Olszewska, Jeremias Rößler, Adam Leon Smith, and Jonathon Wright.  This week, on a couple of train journeys around East Anglia, I read it and made sketchnotes. As someone not deeply into this field, but who has been experimenting with AI as a testing tool at work, I found the landscape view provided by the book interesting, particularly the lists: of challenges in testing AI, of approaches to testing AI, and of quality aspects to consider when evaluating AI.  Despite the hype around the area right now there's much that any competent tester will be familiar with, and skills that translate directly. Where there's likely to be novelty is in the technology, and the technical domain, and the effect of

Can Code, Can't Code, Is Useful

The Association for Software Testing is crowd-sourcing a book,  Navigating the World as a Context-Driven Tester , which aims to provide  responses to common questions and statements about testing from a  context-driven perspective . It's being edited by  Lee Hawkins  who is  posing questions on  Twitter ,   LinkedIn , Mastodon , Slack , and the AST  mailing list  and then collating the replies, focusing on practice over theory. I've decided to  contribute  by answering briefly, and without a lot of editing or crafting, by imagining that I'm speaking to someone in software development who's acting in good faith, cares about their work and mine, but doesn't have much visibility of what testing can be. Perhaps you'd like to join me?   --00-- "If testers can’t code, they’re of no use to us" My first reaction is to wonder what you expect from your testers. I am immediately interested in your working context and the way

Testers are Gate-Crashers

  The Association for Software Testing is crowd-sourcing a book,  Navigating the World as a Context-Driven Tester , which aims to provide  responses to common questions and statements about testing from a  context-driven perspective . It's being edited by  Lee Hawkins  who is  posing questions on  Twitter ,   LinkedIn , Mastodon , Slack , and the AST  mailing list  and then collating the replies, focusing on practice over theory. I've decided to  contribute  by answering briefly, and without a lot of editing or crafting, by imagining that I'm speaking to someone in software development who's acting in good faith, cares about their work and mine, but doesn't have much visibility of what testing can be. Perhaps you'd like to join me?   --00-- "Testers are the gatekeepers of quality" Instinctively I don't like the sound of that, but I wonder what you mean by it. Perhaps one or more of these? Testers set the quality sta

Am I Wrong?

I happened across Exploratory Testing: Why Is It Not Ideal for Agile Projects? by Vitaly Prus this week and I was triggered. But why? I took a few minutes to think that through. Partly, I guess, I feel directly challenged. I work on an agile project (by the definition in the article) and I would say that I use exclusively exploratory testing. Naturally, I like to think I'm doing a good job. Am I wrong? After calming down, and re-reading the article a couple of times, I don't think so. 😸 From the start, even the title makes me tense. The ideal solution is a perfect solution, the best solution. My context-driven instincts are reluctant to accept the premise, and I wonder what the author thinks is an ideal solution for an agile project, or any project. I notice also that I slid so easily from "an approach is not ideal" into "I am not doing a good job" and, in retrospect, that makes me smile. It doesn't do any harm to be reminded that your cognitive bias

Test Now

The Association for Software Testing is crowd-sourcing a book,  Navigating the World as a Context-Driven Tester , which aims to provide  responses to common questions and statements about testing from a  context-driven perspective . It's being edited by  Lee Hawkins  who is  posing questions on  Twitter ,   LinkedIn , Mastodon , Slack , and the AST  mailing list  and then collating the replies, focusing on practice over theory. I've decided to  contribute  by answering briefly, and without a lot of editing or crafting, by imagining that I'm speaking to someone in software development who's acting in good faith, cares about their work and mine, but doesn't have much visibility of what testing can be. Perhaps you'd like to join me?   --00-- "When is the best time to test?" Twenty posts in , I hope you're not expecting an answer without nuance? You are? Well, I'll do my best. For me, the best time to test is when there

README

    This week at work my team attended a Myers Briggs Type Indicator workshop. Beforehand we each completed a questionnaire which assigned us a personality type based on our position on five behavioural preference axes. For what it's worth, this time I was labelled INFJ-A and roughly at the mid-point on every axis.  I am sceptical about the value of such labels . In my less charitable moments, I imagine that the MBTI exercise gives us each a box and, later when work shows up, we try to force the work into the box regardless of any compatiblity in size and shape. On the other hand, I am not sceptical about the value of having conversations with those I work with about how we each like to work or, if you prefer it, what shape our boxes are, how much they flex, and how eager we are to chop problems up so that they fit into our boxes. Wondering how to stretch the workshop's conversational value into something ongoing I decided to write a README for me and

ChatGPT Whoppers

Over Christmas I thought I'd have a look at ChatGPT . Not to "break" it, or find more examples of its factual incorrectness , but to explore it sympathetically, for fun. And it was fun. In particular, the natural language generation and understanding capabilities of the system are really impressive. However, even without trying it's not hard to expose weaknesses in the tool. So much so that I doubt I would have bothered to blog about what I found, except that I enjoyed the accidental semantic connection between a handful of my observations. I asked for ASCII art to celebrate my 600th blog post on software testing and got this whopper! . .: :: :; ;: .;; .;;: ::;: :;;: ;;;:

A Qualified Answer

The Association for Software Testing is crowd-sourcing a book,  Navigating the World as a Context-Driven Tester , which aims to provide  responses to common questions and statements about testing from a  context-driven perspective . It's being edited by  Lee Hawkins  who is  posing questions on  Twitter ,   LinkedIn ,   Slack , and the AST  mailing list  and then collating the replies, focusing on practice over theory. I've decided to  contribute  by answering briefly, and without a lot of editing or crafting, by imagining that I'm speaking to someone in software development who's acting in good faith, cares about their work and mine, but doesn't have much visibility of what testing can be. Perhaps you'd like to join me?   --00-- "Whenever possible, you should hire testers with testing certifications"  Interesting. Which would you value more? (a) a candidate who was sent on loads of courses approved by some organisation you don't know and ru

Farewell AST

After four years, three of them as Vice President, I'm standing down from the board of the Association for Software Testing . Let me say up front that I am an unapologetic romantic about my craft. (And, yeah , I called it a craft. Sue me.) I believe in what AST stands for, its mission , and in context-driven testing , so it's been an absolute privilege to be involved in running the organisation. It's also been fun, and full of difficult situations and choices, and hard work on top of family life and a day job. There also was the small matter of the global Covid pandemic to deal with. The immediate impact was on CAST, our annual conference , and in some ways the beating heart of the AST. We had to variously cancel, reschedule, and move CAST online and we are still experiencing the after-effects as we organise the 2023 in-person event . So why am I leaving? Well, first, I'm not leaving the organisation, only the board. I am a life member and

Having a Test.blast()

Last week I attended a meetup on API testing with Mark Winteringham . In it, he talked through some HTTP and REST basics, introduced us to Postman by making requests against his Restful Booker bed and breakfast application, and encouraged us to enter the Test.bash() 2022 API Challenge which was about to close. The challenge is to make a 20-minute video for use at the Ministry of Testing's October Test.bash() showing the use of automation to check that it's possible to create a room using the Restful Booker API. I talk and write about exploring with automation a lot (next time is 14th October 2022, for an Association for Software Testing webinar ) and I thought it might be interesting to show that I am not a great developer and spend plenty of time Googling, copy-pasting, and introducing and removing typos. So I did and my video is now available in the Ministry of Testing Dojo . The script I hacked during the video is up in GitHub . My