Access JumpStart 2.0 | Blog

A Rapid Development Framework for Microsoft Access

I have a class called PoLineController which has a public IsLineValid method. I made a change to this method in my previous TDD article, but I didn’t test it first. I did end up refactoring it a bit.

So looking back, I am going to think about how I could have done this by writing a test first.

This might get messy!

Ultimately, the issue here was the user getting an error message and being unable to change a value on the PO line because of a new feature that needs to be implemented. There are 2 different types of lines we are concerned with when dealing with this new feature. A “Concrete” line and anything else.

The “Concrete” line can have 2 different Quantities. The Ordered Quantity and the Used Quantity. Sometimes the concrete is all used up, sometimes not, and sometimes they borrow more concrete from another order and use it on the existing order. Suffice it to say they want to still know how much concrete was ordered and how much was used for each line.

The feature to add is that the Ordered Quantity could be null or 0 and the Used Quantity can be filled in. This is a new criteria for the validity of a line.

I’m thinking then that the test just needs to be able to run the IsLineValid function, setting up the test for an invalid quantity set and making sure that worked, and for a valid quantity set and make sure that worked. As I mentioned in my last article, the problem here is that in order to make an instance of this class in a test, I need to have a functioning form which requires tons of setup. That is something I do not want to do here due to the complexity and time it would take to do that. So is there a way for me to easily do this?

My first thought is to remove the thisForm references from my code. That is the variable that contains the related form. I am using that object to get the actual values from the form itself. See the code below:

Public Function IsLineValid() As Boolean
    IsLineValid = IsPhaseValid _
            And IsItemDescriptionValid _
            And IsQtyValid _
            And IsUnitOfMeasureValid And IsCostTypeValid And IsTaxableValid
    If IsLineValid = True And Nz(thisForm![Cost_Reference_Number], "") = "" Then Cancel_Cost_Reference_Check = False
End Function

Private Function IsPhaseValid() As Boolean
    IsPhaseValid = Not ISNULL(thisForm![Phase_Number]) And thisForm![Phase_Number] <> "" And thisForm![Phase_Number] <> 0
End Function

Private Function IsItemDescriptionValid() As Boolean
    IsItemDescriptionValid = Not ISNULL(thisForm![Item_Description]) And thisForm![Item_Description] <> ""
End Function

Private Function IsQtyValid() As Boolean
    Dim retVal As Boolean
    If LineControlManager.LineIsConcrete Then
        retVal = IsQtyOrderedValid Or IsQtyUsedValid
    Else
        retVal = IsQtyOrderedValid
    End If
    IsQtyValid = retVal
End Function

Private Function IsQtyOrderedValid() As Boolean
    IsQtyOrderedValid = Not ISNULL(thisForm![Qty_Ordered]) And thisForm![Qty_Ordered] <> "" And thisForm![Qty_Ordered] <> 0
End Function

Private Function IsQtyUsedValid() As Boolean
    IsQtyUsedValid = Not ISNULL(thisForm![Qty_Used]) And thisForm![Qty_Used] <> "" And thisForm![Qty_Used] <> 0
End Function

Private Function IsUnitOfMeasureValid() As Boolean
    IsUnitOfMeasureValid = Not ISNULL(thisForm![Unit_of_Measure]) And thisForm![Unit_of_Measure] <> ""
End Function

Private Function IsCostTypeValid() As Boolean
    IsCostTypeValid = ISNULL(thisForm!Cost_Type_ID) = False
End Function

Private Function IsTaxableValid() As Boolean
    IsTaxableValid = ISNULL(thisForm![Taxable]) = False
End Function

I’ve noticed as well that I have another responsibility of my IsLineValid function that should probably be removed to it’s own separate function. That’s the line:

If IsLineValid = True And Nz(thisForm![Cost_Reference_Number], "") = "" Then Cancel_Cost_Reference_Check = False

There are only 4 places where I am calling the function IsLineValid. So I could remove the line to it’s own public method. Actually, now if I look at where this Cancel_Cost_Reference_Check reference is, perhaps I could Just reference the new method instead.

Turns out in this case Cancel_Cost_Reference_Check is set to private and is not even being used in this class, so I’m just going to delete it.

All right, so now we just have a boolean returning function that is just looking at field values to determine whether the line is valid or not. Moving on, I will start a test method in RubberDuck and see where that takes me. Did that, here’s my test code (which currently is failing due to me not knowing yet how to pass the needed fields to some kind of mock form object):

'@TestMethod("POLineController")
Private Sub GivenConcreteLine_WhenNothingOrderedAndSomethingUsed_ThenLineIsValid()
    On Error GoTo TestFail
    Dim LineController As ECI_POLineController
    Set LineController = New ECI_POLineController
    
    'Need to figure out how to set the line values before the assert.
    Assert.AreEqual True, LineController.IsLineValid

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
    Resume TestExit
End Sub

The test sets up the object and runs the method testing to see if it’s true.

I still need to figure out how to get the function which is currently referencing the thisForm object to get the values, to see if there is another way to get the values where I could substitute my own values when testing, or use the form’s values when the system is live. I think what I will do in my next article is to create a new interface with getters for the values I need. Then I can create two different objects that will implement the interface. One of them will retrieve form values, the other will be able to set and retrieve test values. Looking forward to the next article now!