The test-driven development cycle is:
- Create failing test for the behavior you are wanting.
- Create just enough code for the test to pass.
- Refactor – meaning reorganize the code without changing any functionality.
And you just keep repeating that over and over again until you have built whatever you are working toward. In our case, I am trying to create a system for form auditing and I’ve started with a class to listen to a form.
I have just written the third bit of passing code, and now it is time to refactor if I’d like. This could be refactoring the tests or the code itself.
In this case, two of my tests take about 0.5 seconds each and I’d like to cut that down a bit. I am opening a form as part of both tests and I think maybe I can just open it once for performing these tests rather than closing and reopening each time.
In order to do this, I’m going to utilize the RubberDuck Test Module functions that initialize the environment for the testing and cleanup afterwards. There are 4 subroutines RubberDuck already added to my test module:
- ModuleInitialize() – per the comments this method runs once for the whole module (in other words for every test that runs in this module, this will have run once before it when the module is setup.
- ModuleCleanup() – same as ModuleInitialize except it runs at the very end.
- TestInitialize() – per the comments this method runs every time a test procedure is run, just before the test procedure.
- TestCleanup() – and this one runs after the test is done.
So, I think I will perform these code changes to refactor:
- Move my:
Dim NewForm as New Access.Form
to the Global Declarations at the beginning of the module so I can use NewForm throughout the tests. I will also change the line to:
Private NewForm as New Access.Form - Delete the old line from the 2 Test Subs that used it.
- Move:
DoCmd.OpenForm “TestForm”
Set NewForm = Forms(“TestForm”)
from the Test Subs and put them in the ModuleInitialize() procedure just once. - Finally, move the code to cleanup the Test Form by closing it and setting the variable to Nothing to ModuleCleanup() and remove it from both Test modules.
After doing this and re-running all the tests they all pass and they are all super fast now!
Yesterday the total run time was about 1.01 seconds, now it’s 0.81 seconds, not too much faster, but each of the tests runs very quickly at just 2, 6, and 1 milliseconds respectively.
Here is the code of the testing module after my changes:
'@TestModule
'@Folder("Tests")
Option Compare Database
Option Explicit
Option Private Module
#Const LateBind = LateBindTests
#If LateBind Then
Private Assert As Object
Private Fakes As Object
#Else
Private Assert As Rubberduck.AssertClass
Private Fakes As Rubberduck.FakesProvider
#End If
Private NewForm As New Access.Form
'@ModuleInitialize
Private Sub ModuleInitialize()
'this method runs once per module.
#If LateBind Then
Set Assert = CreateObject("Rubberduck.AssertClass")
Set Fakes = CreateObject("Rubberduck.FakesProvider")
#Else
Set Assert = New Rubberduck.AssertClass
Set Fakes = New Rubberduck.FakesProvider
#End If
DoCmd.OpenForm "TestForm"
Set NewForm = Forms("TestForm")
End Sub
'@ModuleCleanup
Private Sub ModuleCleanup()
'this method runs once per module.
Set Assert = Nothing
Set Fakes = Nothing
DoCmd.Close acForm, "TestForm"
Set NewForm = Nothing
End Sub
'@TestInitialize
Private Sub TestInitialize()
'This method runs before every test in the module..
End Sub
'@TestCleanup
Private Sub TestCleanup()
'this method runs after every test in the module.
End Sub
'@TestMethod("FormListener")
Private Sub CreateFormListener()
On Error GoTo TestFail
'Arrange:
Dim FormListenerTest As FormListener
'Act:
Set FormListenerTest = New FormListener
'Assert:
Assert.Succeed
TestExit:
'@Ignore UnhandledOnErrorResumeNext
On Error Resume Next
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
Resume TestExit
End Sub
'@TestMethod("FormListener")
Private Sub SetupFormListener()
On Error GoTo TestFail
'Arrange:
Dim FormListenerTest As New FormListener
'Act:
FormListenerTest.Setup NewForm
'Assert:
Assert.Succeed
TestExit:
'@Ignore UnhandledOnErrorResumeNext
Set FormListenerTest = Nothing
On Error Resume Next
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
Resume TestExit
End Sub
'@TestMethod("FormListener")
Private Sub FormListenerHearsFormBeforeUpdateEvent()
On Error GoTo TestFail
'Arrange:
Dim FormListenerTest As New FormListener
FormListenerTest.Setup NewForm
'Act:
NewForm.TestText = "TestingUpdate"
DoCmd.RunCommand acCmdSaveRecord
'Assert:
Assert.IsTrue FormListenerTest.BeforeUpdateTriggered
TestExit:
'@Ignore UnhandledOnErrorResumeNext
Set FormListenerTest = Nothing
On Error Resume Next
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
Resume TestExit
End Sub