A big part of Test Driven Development is to write code that passes a test without overthinking things.
In my last test I wrote, it’s pretty clear it will not stand up to the next couple of tests one might think of.
The test is making sure that the count of updates to the field is 1 when I made 1 update. The test is as follows:
'@TestMethod("FormListener")
Private Sub FormListenerReturnsCountOfOneForTestTextField()
On Error GoTo TestFail
FormListenerTest.Setup NewForm
NewForm.TestText = "TestingUpdate"
DoCmd.RunCommand acCmdSaveRecord
Assert.AreEqual FormListenerTest.TimesFieldChanged("TestText"), CLng(1)
TestExit:
'@Ignore UnhandledOnErrorResumeNext
On Error Resume Next
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
Resume TestExit
End Sub
The code I wrote to pass this test is an example of not thinking, and just writing something that will pass:
Public Function TimesFieldChanged(theFieldName As String) As Long
Dim retVal As Long
retVal = 1
TimesFieldChanged = 1
End Function
You can see that I simply wrote the function to pass back the value that would pass the single test I’ve written so far.
I’m just pausing here to note that this code is clearly going to change, is not generalized, and is basically placeholder code to pass the test. This is by design. Rather than try to get ahead of the tests, in TDD we want to literally just write something simple enough to get the test to pass. Simplicity is the goal, the complexity comes and evolves the code as we add more tests.
I think in this case, I’m just going to write another failing test to get a 0 or a 1 and I’m going to combine these two asserts into the same test. Is that a good idea? I don’t really know. It simplifies my test code a little and prevents me from copying lots of code though. Maybe I’ll refactor it in a different way later, but for now, Let’s do this:
'@TestMethod("FormListener")
Private Sub FormListenerReturnsCountOfOneForTestTextField()
On Error GoTo TestFail
FormListenerTest.Setup NewForm
Assert.AreEqual FormListenerTest.TimesFieldChanged("TestText"), CLng(0)
NewForm.TestText = "TestingUpdate"
DoCmd.RunCommand acCmdSaveRecord
Assert.AreEqual FormListenerTest.TimesFieldChanged("TestText"), CLng(1)
TestExit:
'@Ignore UnhandledOnErrorResumeNext
On Error Resume Next
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
Resume TestExit
End Sub
This of course causes the test to fail since I coded the test to return 1. I probably should have started with the 0 actually. I made a mistake there.
After running the above test, I was getting some wonky responses from the RubberDuck test explorer, so I am making a copy of the function and doing one test for 0 and one test for 1. Now the 0 test passes and the 1 test fails.
Now it is time to add code to see if the TestText field can change and return 1.
Stop Jon! As I look to see what kind of code I need to add to make this function return 1 I am seeing that I have to do a lot of coding. I would need to start tracking all the field names and whether it got updated and place that result in some kind of collection or array and iterate over it. Seems like way too much coding for one test. I think I have gone down the wrong track. Let me re-think this and we will try to find my error in a future email.