Access JumpStart 2.0 | Blog

A Rapid Development Framework for Microsoft Access

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.