GoLang – Simple BDD Approaches

This article highlights a few ways to build tests with Golang to help pass the heuristics of good testing on to your teams. Let me first start by highlighting with Dan North says about his own journey with test driven development.

I had a problem. While using and teaching agile practices like test-driven development (TDD) on projects in different environments, I kept coming across the same confusion and misunderstandings. Programmers wanted to know where to start, what to test and what not to test, how much to test in one go, what to call their tests, and how to understand why a test fails.

The deeper I got into TDD, the more I felt that my own journey had been less of a wax-on, wax-off process of gradual mastery than a series of blind alleys. I remember thinking “If only someone had told me that!” far more often than I thought “Wow, a door has opened.” I decided it must be possible to present TDD in a way that gets straight to the good stuff and avoids all the pitfalls. – Dan North

One of the ways that you can ease this pain is by focusing on behaviours rather than thinking about tests. The prototypical way of doing this is by simply naming your tests with “it should”. If have have a class Subject and it should fail when input is empty, we might name our test like so:

func TestMySubject_ItShouldFailWhenInputIsEmpty(t *testing.T) {}

The above is what I always thought of as the canonical simple BDD style, but I’ve been using When/Expect semantics lately.

func TestSubject_WhenContext_ExpectBehaviour(t *testing.T) {}
func TestGetProfile_WhenTheRepositoryIsEmpty_ExpectItShouldReturn404(t *testing.T) {}

This is sufficient to guide us towards behaviour driven development. Often you’ll see “given, when, then” semantics in BDD – we have an approximation here but need to write a separate test case for every variation of the scenario. If we can put the given/when/then semantics into a single test, we have a way of describing different contexts and expectations. So let’s flip this just a little bit and see if we can make a nice expressive test description.

func TestMySubject(t *testing.T) {
  t.Run("Given an empty subject", func(t *testing.T) {
    sut := …
    t.Run("When invoking a method", func(t *testing.T) {
      i, err := set.method()
      t.Run("Then it should fail", […])
      t.Run("Then it should be unchanged", […])
    })
  })
}

That’ll give us enough to describe the full scenario (GIVEN), the behaviour we’re focused on (WHEN), and the expectations (THEN). And it’s all in a single test context so it help reduce some of the copy/paste if we can build a context and test everything related in it.

Subscribe to our newsletter and receive our very latest news.

Go back

Your message has been sent

Warning
Warning
Warning.

Leave a comment