Ok, so I got a test started up here, let’s try to continue and see where it goes:
'@TestMethod("FormListener")
Private Sub FormListenerRaisesBoundDataChangedEvent()
FormListenerTest.Setup NewForm
NewForm.TestText = "NewThing"
End Sub
There’s what I have so far.
I’m thinking next line will need to save the record. One simple way to do that is to use this command:
NewForm.Dirty = False
So now at this point the BeforeUpdate routine on the form should fire and I want to make sure that the FormListenerTest is activated and then fires off it’s own event. But perhaps before that event is fired off, I want a FormIterator to search the form for updated fields. Then perhaps I want to pass that dictionary into the new event that is raised.
So, can I run a test to listen to FormListenerTest and see what the FormIterator event returns?
I think I will have to create a test class module (not a test normal module like I have now). The test class module will listen to a formlistener class instance.
Hmm… but am I actually looking again at architecture / design here? Maybe. Perhaps ultimately I am talking about an interface here that will allow me to expose just the necessary elements on what will eventually be a “Live” class, but also allow a “Test” class to implement the interface and extend it to provide more testing capabilities to be able to return things. Ooh, I like where this is going. In fact perhaps I could extract interfaces from FormListener and/or FormIterator and perform more tests using a test version of the object which would expose the internals enough to see if they were doing what they were supposed to do.
Yes, Eureka! I will not run butt-naked through the streets though.
Ok, so let’s try having RubberDuck extract an interface from my current version of the FormListener.
Ooh, check that out. I had to refresh RubberDuck, but then it let me Right Click inside the class and choose RubberDuck->Refactor->Extract Interface.
I’ll use the default name, Implementation Options, and Instancing, and just choose the Public Setup member which is all I really need initially. The other property and methods were there for testing originally. We’ll leave them in the class but extract the interface and we get:
'@Interface
Option Explicit
Public Sub Setup(theForm As Access.Form)
End Sub
Nice, then I’ll go back to my current FormListener class and implement it:
Oh my, I didn’t even have to implement it. It was already done for me! Check this out:
Option Compare Database
Option Explicit
Implements IFormListener
Private WithEvents frm As Access.Form
Private m_bBeforeUpdateTriggered As Boolean
Public Sub Setup(theForm As Access.Form)
Set frm = theForm
frm.BeforeUpdate = "[Event Procedure]"
End Sub
Public Property Get BeforeUpdateTriggered() As Boolean: BeforeUpdateTriggered = m_bBeforeUpdateTriggered: End Property
Public Property Let BeforeUpdateTriggered(ByVal bNewValue As Boolean): m_bBeforeUpdateTriggered = bNewValue: End Property
Private Sub frm_BeforeUpdate(Cancel As Integer)
Me.BeforeUpdateTriggered = True
End Sub
Public Function TimesFieldChanged(theFieldName As String) As Long
Dim retVal As Long
retVal = 0
TimesFieldChanged = 0
End Function
Private Sub IFormListener_Setup(theForm As Access.Form)
Setup theForm
End Sub
That was fun. And all my tests still pass. I guess I’m really in the “refactoring” phase at this point and will need to continue it a little further. I think I can leave that setup the way it is for now. I kind of find it interesting how I will be able to run the “IFormListener_Setup” sub and it will just pass it through to the Public Setup sub. This means I will see the Setup autocomplete whether I Dim the variable that will hold the class as an IFormListener or as FormListener. Interesting.
Looking forward to the next session.