The Learning Test
Tags: go, tdd, testing
Published: 2019-03-03 20:48:36

---
title: The Learning Test
published: true
description: 
tags: go, tdd, testing
---

I'm reading [TDD By Example](https://www.oreilly.com/library/view/test-driven-development/0321146530/) by Kent Beck right now and one of the Red Bar Patterns really resonated with me. It's called the Learning Test.

Here's the scenario. You have a new library or framework that you're implementing. Instead of just diving into TDD'ing the implementation, you can write some learning tests. These are tests where you validate how you expect the 3rd party tool to work for you. I see this as testing the contract between your application and the 3rd party tool.

Here's an example that I did using [github.com/aclindsa/ofxgo](https://github.com/aclindsa/ofxgo).

Now this library does **a lot** and I really only care about one small part. This library can interact with OFX servers but my current reason for using it is to parse OFX files that have already been downloaded to the local file system.

```go
package main

import (
	. "github.com/onsi/gomega"
	"testing"
)

func TestReadLocalOFX(t *testing.T) {
	g := NewGomegaWithT(t)	
}
```
So after looking through some of the functions it looks like I want to use this[ `ParseResponse()`](https://github.com/aclindsa/ofxgo/blob/3e8a9c5a5382dccc0fd89b4d869e2202a48e8a66/response.go#L275) function.

I'll want to assert something after parsing to verify that it worked. Looking at the example in the README it looks like I can parse how many banks are contained in the OFX file.

```go
func TestReadLocalOFX(t *testing.T) {
	g := NewGomegaWithT(t)

	g.Expect(len(banks)).To(Equal(1))
}
```

Now I need to learn how to get that `banks` variable.

From `ofxgo` I don't need to initialize anything, I can just directly call `ParseResponse()` and pass in an open file. That seems like a good first step; parse the OFX file!

```go
import (
	"github.com/aclindsa/ofxgo"
	. "github.com/onsi/gomega"
	"testing"
)

func TestReadLocalOFX(t *testing.T) {
	g := NewGomegaWithT(t)

	resp, err := ofxgo.ParseResponse(ofxFile)
	
	g.Expect(len(banks)).To(Equal(1))
}
```

I'm going to need an OFX file for testing as well. So I head over to my banks site and download a OFX/QFX file and put it in the same directory as my test. I can now open the file from my test:

```go
func TestReadLocalOFX(t *testing.T) {
	g := NewGomegaWithT(t)
	
	ofxFile, err := os.Open("ofxFile.qfx")
	g.Expect(err).ShouldNot(HaveOccurred())

	resp, err := ofxgo.ParseResponse(ofxFile)
	g.Expect(err).ShouldNot(HaveOccurred())

	g.Expect(len(banks)).To(Equal(1))
}
```

Now I can get the banks variable from the `*Reponse` from `ParseResponse()`.

```go
func TestReadLocalOFX(t *testing.T) {
	g := NewGomegaWithT(t)

	ofxFile, err := os.Open("ofxFile.qfx")
	g.Expect(err).ShouldNot(HaveOccurred())

	resp, err := ofxgo.ParseResponse(ofxFile)
	g.Expect(err).ShouldNot(HaveOccurred())

	banks := resp.Bank
	g.Expect(len(banks)).To(Equal(1))
}
```

And now if I run my test it should work!

```
=== RUN   TestReadLocalOFX
--- FAIL: TestReadLocalOFX (0.00s)
....    
        Expected error:
            <*errors.errorString | 0xc000091050>: {
                s: "OFX SECURITY header not NONE",
            }
            OFX SECURITY header not NONE
        not to have occurred
FAIL
FAIL	ofxreader	0.018s
```

It failed! Already this test is teaching me something about this library. It's expecting this `OFX SECURITY header` to be set to `NONE`. Looking at my OFX file I see this as the header:

```
OFXHEADER:100
DATA:OFXSGML
VERSION:102
SECURITY:TYPE1
ENCODING:USASCII
CHARSET:1252
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE
```

It says security is `TYPE1`. I decide to search the repo for this error and find this: [https://github.com/aclindsa/ofxgo/blob/3e8a9c5a5382dccc0fd89b4d869e2202a48e8a66/response.go#L90-L92](https://github.com/aclindsa/ofxgo/blob/3e8a9c5a5382dccc0fd89b4d869e2202a48e8a66/response.go#L90-L92)

This library only works if the security header is set to `NONE`. I'll want to add this learning to my test so I don't forget.

```go
import (
	"github.com/aclindsa/ofxgo"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"io/ioutil"
	"strings"
	"testing"
)

func TestReadLocalOFX(t *testing.T) {
	g := NewGomegaWithT(t)

	By("setting OFX security header to NONE")
	file, err := ioutil.ReadFile("ofxFile.qfx")
	g.Expect(err).ShouldNot(HaveOccurred())
	newOFXFile := strings.Replace(string(file), "SECURITY:TYPE1", "SECURITY:NONE", 1)

	ofxFile := strings.NewReader(newOFXFile)

	resp, err := ofxgo.ParseResponse(ofxFile)
	g.Expect(err).ShouldNot(HaveOccurred())

	banks := resp.Bank
	g.Expect(len(banks)).To(Equal(1))
}
```

And now if I run the test:

```go
=== RUN   TestReadLocalOFX
STEP: setting OFX security header to NONE
--- PASS: TestReadLocalOFX (0.01s)
PASS
ok  	ofxreader	0.022s
```

It passes! That's one learning test for this library.

I'm hoping to bring learning tests to my team at work because I think it'll help spread knowledge among all team members and become useful reference points.

Do you use learning tests at work? Have they been helpful to you and your team?