Learning Tests
I’m reading TDD By Example 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.
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.
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()
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.
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!
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:
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()
.
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 I see this:
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
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.
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:
=== 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.