Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test VBA code? - Two different pointers

I am working on this excellent tutorial, but in the end my first test is not passing, due to the fact that I can clearly see I am creating two different arrays (and pointers), and trying to compare them with one another.

Now the tutorial from what I can see leaves out a few lines of code that I have added, which is also where I see the problem, but without those lines the code does not even run of coarse.

All my other test methods are the same as the example, except for this method that I created the following lines - else nothing happens when you run the test.

Public Sub Run(ByVal dataService As IDataService, ByVal wsService As IWorksheetService)
Dim data As Variant                   'Added this line
data = dataService.GetSomeTable       'Added this line
Call wsService.WriteAllData(data)     'Added this line
End Sub

And here is where I can see the code going south...

'@TestMethod
Public Sub WorksheetServiceWorksOffDataFromDataService()
    'Arrange
    Dim dataServiceStub As MyDataServiceStub
    Set dataServiceStub = New MyDataServiceStub
    Dim expected As LongLong
    expected = VarPtr(dataServiceStub.GetSomeTable)  'expected creates an Array

Dim wsServiceStub As MyWorksheetServiceStub
Set wsServiceStub = New MyWorksheetServiceStub

'Act
With New MyTestableMacro
    .Run dataServiceStub, wsServiceStub 'here we create a second array
End With

Dim actual As LongLong
actual = wsServiceStub.WrittenArrayPointer 'here we point to the address of the second array

'Assert
Assert.AreEqual expected, actual  ' this test fails cause it points to two different addresses
End Sub

I had to change the type from Long as in the tutorial for the array pointers to LongLong due to the fact that the number on 64 bit is too long for Long. LongPtr also worked

like image 651
Alfa Bravo Avatar asked Apr 25 '18 13:04

Alfa Bravo


People also ask

How to unit test VBA functions in Python?

The xlwings guys just demoed how to unit tests VBA functions in Python. Essentially, you do not need to add anything to your workbook but you rater write "stand alone" Python code that call your VBA functions so that you can compare expected vs actual results of your VBA functions.

How do I run multiple VBA codes at the same time?

The command requires users to skillfully step over each line of the VBA code in the second sub. If the code calls for another sub, simply step over the second sub and continue running the code. To run the code, press Shift+F8 simultaneously to execute the second sub. The stepping out command helps the user step out of the current running sub.

What is the Assert class in Rubberduck VBA?

The Assert class in Rubberduck VBA provides some of the most commonly used assertions. Believe it or not, that’s it! We’re done with creating our first test case. Now starts the real fun: let’s run it. In order to run the test: Inside Test Explorer, click Run > All Tests.

How to create a unit test in Excel using Rubberduck?

The options we’ll use in this tutorial are located unter the Rubberduck > Unit Tests menu: 3. Create a function to test In the previous step, we started Excel and opened Visual Basic Editor. In this step, you are going to insert a new code module, and add a new Function to it.


1 Answers

VarPtr is what's artificially complicating that test, introducing frail and flawed pointer logic that doesn't need to be there.


Change your stub data service to return some non-empty data - literally anything will do:

Option Explicit
Implements IDataService
'@Folder "Services.Stubs"

Private Function IDataService_GetSomeTable() As Variant
    IDataService_GetSomeTable = GetSomeTable
End Function

Public Function GetSomeTable() As Variant
    Dim result(1 To 50, 1 To 10) As Variant
    result(1, 1) = "test"
    GetSomeTable = result
End Function

Now change the stub worksheet service to keep a copy of the actual data (rather than just a LongPtr):

Option Explicit
Implements IWorksheetService
'@Folder "Services.Stubs"

Private Type TStub
    WasWritten As Boolean
    WrittenData As Variant
End Type
Private this As TStub

Private Sub IWorksheetService_WriteAllData(ByRef data As Variant)
    this.WasWritten = True
    this.WrittenData = data
End Sub

Public Property Get DataWasWritten() As Boolean
    DataWasWritten = this.WasWritten
End Property

Public Property Get WrittenData() As Variant
    WrittenData = this.WrittenData
End Property

Now change the test to assert that IDataService.GetSomeTable is returning the same data that IWorksheetService.WriteAllData works with - you can do that using Assert.SequenceEquals, which compares all elements of two arrays and fails if anything is different:

'@TestMethod
Public Sub WorksheetServiceWorksOffDataFromDataService()
    'Arrange
    Dim dataServiceStub As StubDataService
    Set dataServiceStub = New StubDataService
    Dim expected As Variant
    expected = dataServiceStub.GetSomeTable

    Dim wsServiceStub As StubWorksheetService
    Set wsServiceStub = New StubWorksheetService

    'Act
    With New Macro
        .Run dataServiceStub, wsServiceStub
    End With

    Dim actual As Variant
    actual = wsServiceStub.WrittenData

    'Assert
    Assert.SequenceEquals expected, actual
End Sub

This makes the test much simpler, and it passes:

Passing tests in Rubberduck test explorer


I will be updating the article with this simpler test later today.

like image 139
Mathieu Guindon Avatar answered Nov 11 '22 10:11

Mathieu Guindon