5
\$\begingroup\$

Background: I had created a macro few years ago and when I was reviewing this now, it was hard to understand. I recently went through all the articles from RubberduckVBA and started learning that VBA can also be Object Oriented language and, I tried implementing this concept in my macro.

Purpose of Macro: we have new files every month, we add few columns at the end of the file and provide our comments. Again next month we used to pull comments using concat and vlookup but then I created a quick macro so it directly pulls all the data. It checks all the worksheets, compares with previous months file and pulls extra columns from old file.

Example: We have 8 columns in Sheet1 + 4 columns for comments. 12 column in Sheet2 + 5 columns of comments. The macro checks last column in current file and base don that dynamically copies last 4 and 5 columns in respective sheet based on the concatenated value of entire row in current/fresh file.

Note: I am copying entire range as we also have to pull number formatting, formula from the previous file.

Request: The macro works fine in both format, I would like to know what I missed or what can be updated in current version of the macro to make it more Object Oriented.

the following is the previous procedural Macro.

Option Explicit
Public Sub CarryForwardOld()
 'Declare and set variables
 'Add/check Tools> Reference> Microsoft Scripting Runtime
 Dim ReadingRange As String
 Dim dict As Scripting.Dictionary
 Set dict = New Scripting.Dictionary
 'Set screenupdating to false to increase the speed of processing
 'Application.Calculation = xlCalculationAutomatic
 Application.Calculation = xlCalculationManual
 Application.ScreenUpdating = False
 Application.EnableCancelKey = xlInterrupt
 Dim wbCurrent As Workbook
 Set wbCurrent = ActiveWorkbook
 Dim getfile As String
 getfile = selectedfile(wbCurrent.Name)
 
 If getfile = vbNullString Then Exit Sub
 Dim wbOld As Workbook
 Set wbOld = Workbooks(getfile)
 
 If Not wbOld Is Nothing Then
 If wbOld.Name = wbCurrent.Name Then
 MsgBox "Both file names canot be same! System cannot distinguish between old and new file.", vbCritical, "Rename Either of the File"
 Exit Sub
 End If
 End If
 
 wbCurrent.Activate
 Dim rOld As Long
 Dim rNew As Long
 rOld = 0
 rNew = 0
 
 Dim index As Long
 index = 0
 Dim wsOld As Worksheet
 Dim wsCurr As Worksheet
 
 Dim LastColumnWrite As Long
 Dim WritingRow As Long
 Dim LastRowCurrent As Long
 Dim LastRowOld As Long
 Dim LastColumnCurrent As Long
 Dim LastColumnOld As Long
 Dim readingrow As Long
 For Each wsOld In wbOld.Sheets
 
 On Error Resume Next
 Set wsCurr = wbCurrent.Sheets(wsOld.Name)
 On Error GoTo 0
 
 If Not wsCurr Is Nothing Then
 
 LastColumnCurrent = GetLasts(wsCurr, "Column") - index
 LastRowCurrent = GetLasts(wsCurr, "Row")
 LastRowOld = GetLasts(wsOld, "Row")
 LastColumnOld = GetLasts(wsOld, "Column")
 LastColumnWrite = GetLasts(wsCurr, "Column")
 
 wsOld.Activate
 
 For readingrow = 1 To LastRowOld
 
 With wsOld
 On Error Resume Next
 Dim AddValue As String
 AddValue = Concat(.Range(.Cells(readingrow, 1), .Cells(readingrow, LastColumnCurrent)))
 
 If Not dict.Exists(AddValue) Then
 dict.Add key:=AddValue, _
 Item:=.Range(.Cells(readingrow, LastColumnCurrent + 1), .Cells(readingrow, LastColumnOld)).Address
 End If
 On Error GoTo 0
 End With
 
 Application.StatusBar = "Reading row " & readingrow & " out of " & LastRowOld
 Next readingrow
 
 Application.StatusBar = False
 wsCurr.Activate
 
 For WritingRow = 1 To LastRowCurrent
 Application.StatusBar = "Writing row in Sheet: " & wsCurr.Name & "=>" & WritingRow & " out of " & LastRowCurrent
 ReadingRange = Concat(wsCurr.Range(wsCurr.Cells(WritingRow, 1), wsCurr.Cells(WritingRow, LastColumnCurrent)))
 Dim writeRange As Range
 If dict.Exists(ReadingRange) = True Then
 Set writeRange = wsOld.Range(dict(ReadingRange))
 'wsCurr.Range(Cells(WritingRow, LastColumnWrite + 1), Cells(WritingRow, LastColumnOld)) = Split(Dict(ReadingRange), "|")
 writeRange.Copy wsCurr.Range(wsCurr.Cells(WritingRow, LastColumnWrite + 1), wsCurr.Cells(WritingRow, LastColumnWrite + 1))
 rOld = rOld + 1
 Else
 Dim outRange As Range
 Set outRange = wsCurr.Range(wsCurr.Cells(WritingRow, LastColumnWrite + 1), wsCurr.Cells(WritingRow, LastColumnOld))
 
 Dim cell As Range
 outRange.Interior.colorindex = 36
 For Each cell In outRange
 If cell.Row = 1 Then GoTo nextcell:
 If cell.Offset(-1, 0).HasFormula Then
 cell.Interior.colorindex = -4142
 cell.FillDown
 
 End If
nextcell:
 Next cell
 
 'wsCurr.Range(wsCurr.Cells(WritingRow, LastColumnWrite + 1), wsCurr.Cells(WritingRow, LastColumnOld)).Interior.ColorIndex = 36
 'wsCurr.Cells(WritingRow, LastColumnWrite + 1) = ReadingRange
 rNew = rNew + 1
 End If
 Next WritingRow
 
 End If
 Next wsOld
 Application.ScreenUpdating = True
 Application.Calculation = xlCalculationAutomatic
 wbOld.Close False
 Set wbOld = Nothing
 Application.StatusBar = False
 MsgBox "There are " & rNew & " new records and " & rOld & " old records!", vbOKOnly, "Success!"
End Sub
Public Function GetLasts(ByVal TargetWorksheet As Worksheet, ByRef RowColum As String) As Long
 If Not TargetWorksheet Is Nothing Then
 With TargetWorksheet
 Select Case True
 Case Left$(RowColum, 1) = "R"
 On Error Resume Next
 GetLasts = .Cells.Find(What:="*", _
 after:=.Range("A1"), _
 Lookat:=xlPart, _
 LookIn:=xlFormulas, _
 SearchOrder:=xlByRows, _
 SearchDirection:=xlPrevious, _
 MatchCase:=False).Row
 On Error GoTo 0
 
 Case Left$(RowColum, 1) = "C"
 On Error Resume Next
 GetLasts = .Cells.Find(What:="*", _
 after:=.Range("A1"), _
 Lookat:=xlPart, _
 LookIn:=xlFormulas, _
 SearchOrder:=xlByColumns, _
 SearchDirection:=xlPrevious, _
 MatchCase:=False).Column
 On Error GoTo 0
 End Select
 End With
 End If
End Function
Private Function selectedfile(Optional ByVal CurrentFile As String = vbNullString) As String
 On Error GoTo ErrorHandler
 Dim dialog As FileDialog
 Set dialog = Application.FileDialog(msoFileDialogFilePicker)
 With dialog
 .AllowMultiSelect = False
 .InitialFileName = GetSetting("Office1", "CarryForward", "LastPath")
 .Title = "Select Old/Previous file for reference: " & CurrentFile
 .Show
 If .SelectedItems.Count <> 0 Then
 selectedfile = .SelectedItems.Item(1)
 SaveSetting "Office1", "CarryForward", "LastPath", selectedfile
 Workbooks.Open FileName:=selectedfile
 selectedfile = Mid$(selectedfile, InStrRev(selectedfile, "\") + 1)
 
 End If
 End With
 If selectedfile = vbNullString Then
 MsgBox "You cancelled the prcess!", vbOKOnly, "Alert!"
 Exit Function
 End If
 Exit Function
ErrorHandler:
 If Err.Number > 0 Then 'TODO: handle specific error
 Err.Clear
 Resume Next
 End If
End Function
Private Function Concat(ByVal ConcatRange As Range) As String
 Dim cell As Variant
 Dim delim As String
 delim = "|"
 Dim Result As String
 Result = vbNullString
 Dim CellArray As Variant
 If ConcatRange.Cells.Count > 1 Then
 CellArray = Application.WorksheetFunction.Transpose(ConcatRange.Value)
 
 Else
 Concat = ConcatRange.Value
 Exit Function
 End If
 For Each cell In CellArray
 
 If IsError(cell) Then
 Dim errstring As String
 Dim errval As Variant
 errval = cell
 
 Select Case errval
 Case CVErr(xlErrDiv0)
 errstring = "#DIV"
 Case CVErr(xlErrNA)
 errstring = "#N/A"
 Case CVErr(xlErrName)
 errstring = "#NAME"
 Case CVErr(xlErrNull)
 errstring = "#NULL"
 Case CVErr(xlErrNum)
 errstring = "#NUM"
 Case CVErr(xlErrRef)
 errstring = "#REF"
 Case CVErr(xlErrValue)
 errstring = "#VALUE"
 Case Else
 errstring = vbNullString
 End Select
 
 Result = Result & delim & errstring
 Else
 Result = Result & delim & cell
 End If
 Next cell
 Concat = Right$(Result, Len(Result) - 1)
End Function

The following is the class module implementing Object Orientation.

Class: CarryMe.cls

 Option Explicit
Private Type TCell
 Book As Workbook
 Sheet As Worksheet
 LastRow As Long
 LastColumn As Long
 Records As Long
End Type
Private Previous As TCell
Private Current As TCell
'Add/check Tools> Reference> Microsoft Scripting Runtime
Private dict As Scripting.Dictionary
Public Sub Execute()
 'Set screenupdating to false to increase the speed of processing
 With Application
 '.Calculation = xlCalculationAutomatic
 .Calculation = xlCalculationManual
 .ScreenUpdating = False
 .EnableCancelKey = xlInterrupt
 End With
 
 SelectPreviousFile
 
 If Previous.Book Is Nothing Then Exit Sub
 Dim wsheet As Worksheet
 For Each wsheet In Current.Book.Sheets
 
 SetParameters wsheet.Name
 
 ReadDataToDictionary
 
 WriteDictToSheet
 
 Next wsheet
 
 Previous.Book.Close False
 Set Previous.Book = Nothing
 With Application
 .ScreenUpdating = True
 .Calculation = xlCalculationAutomatic
 .StatusBar = False
 End With
 
 MsgBox "There are " & Current.Records & " new records and " & Previous.Records & " old records!", vbOKOnly, "Success!"
End Sub
Private Sub SetParameters(ByVal SheetName As String)
 Set Current.Sheet = Current.Book.Sheets(SheetName)
 Set Previous.Sheet = Previous.Book.Sheets(SheetName)
 
 With Current.Sheet
 Current.LastRow = .Cells.Find(What:="*", after:=.Range("A1"), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False).Row
 Current.LastColumn = .Cells.Find(What:="*", after:=.Range("A1"), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False).Column
 End With
 
 If Previous.Sheet Is Nothing Then Exit Sub
 
 With Previous.Sheet
 Previous.LastRow = .Cells.Find(What:="*", after:=.Range("A1"), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False).Row
 Previous.LastColumn = .Cells.Find(What:="*", after:=.Range("A1"), Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False).Column
 End With
End Sub
Private Sub ReadDataToDictionary()
 Set dict = New Scripting.Dictionary
 
 With Previous.Sheet
 
 Dim index As Long
 
 For index = 1 To Previous.LastRow
 On Error Resume Next
 Dim AddValue As String
 AddValue = Concat(.Range(.Cells(index, 1), .Cells(index, Current.LastColumn)))
 
 If Not dict.Exists(AddValue) Then
 dict.Add key:=AddValue, _
 Item:=.Range(.Cells(index, Current.LastColumn + 1), .Cells(index, Previous.LastColumn)).Address
 End If
 On Error GoTo 0
 Next index
 End With
End Sub
Private Sub WriteDictToSheet()
 With Current.Sheet
 
 Dim index As Long
 
 For index = 1 To Current.LastRow
 Application.StatusBar = "Writing row in Sheet: " & Current.Sheet.Name & "=>" & index & " out of " & Current.LastRow
 
 Dim ReadingRange As String
 ReadingRange = Concat(.Range(.Cells(index, 1), .Cells(index, Current.LastColumn)))
 
 If dict.Exists(ReadingRange) Then
 Dim writeRange As Range
 Set writeRange = Previous.Sheet.Range(dict(ReadingRange))
 
 writeRange.Copy .Range(.Cells(index, Current.LastColumn + 1), .Cells(index, Previous.LastColumn + 1))
 Previous.Records = Previous.Records + 1
 Else
 Dim outRange As Range
 Set outRange = .Range(.Cells(index, Current.LastColumn + 1), .Cells(index, Previous.LastColumn))
 
 Dim cell As Range
 outRange.Interior.colorindex = 36
 For Each cell In outRange
 If cell.Row = 1 Then GoTo nextcell:
 If cell.Offset(-1, 0).HasFormula Then
 cell.Interior.colorindex = -4142
 cell.FillDown
 End If
nextcell:
 Next cell
 
 Current.Records = Current.Records + 1
 End If
 Next index
 End With
End Sub
Private Sub SelectPreviousFile()
 On Error GoTo ErrorHandler
 Dim dialog As FileDialog
 Set dialog = Application.FileDialog(msoFileDialogFilePicker)
 With dialog
 .AllowMultiSelect = False
 .InitialFileName = GetSetting("Office1", "CarryForward", "LastPath")
 .Title = "Select Old/Previous file for reference: " & Current.Book.Name
 .Show
 If .SelectedItems.Count <> 0 Then
 Dim selectedfile As String
 selectedfile = .SelectedItems.Item(1)
 SaveSetting "Office1", "CarryForward", "LastPath", selectedfile
 Workbooks.Open FileName:=selectedfile
 selectedfile = Mid$(selectedfile, InStrRev(selectedfile, "\") + 1)
 
 End If
 End With
 Select Case True
 Case selectedfile = vbNullString
 MsgBox "You cancelled the prcess!", vbOKOnly, "Alert!"
 
 Case selectedfile = Current.Book.Name
 MsgBox "Both file names canot be same! System cannot distinguish between old and new file.", vbCritical, "Rename Either of the File"
 
 Case Else
 Set Previous.Book = Workbooks(selectedfile)
 End Select
 
 Exit Sub
ErrorHandler:
 If Err.Number > 0 Then 'TODO: handle specific error
 Err.Clear
 Resume Next
 End If
End Sub
Private Function Concat(ByVal ConcatRange As Range) As String
 Dim cell As Variant
 Dim delim As String
 delim = "|"
 Dim Result As String
 Result = vbNullString
 Dim CellArray As Variant
 If ConcatRange.Cells.Count > 1 Then
 CellArray = Application.WorksheetFunction.Transpose(ConcatRange.Value)
 
 Else
 Concat = ConcatRange.Value
 Exit Function
 End If
 For Each cell In CellArray
 
 If IsError(cell) Then
 Dim errstring As String
 Dim errval As Variant
 errval = cell
 
 Select Case errval
 Case CVErr(xlErrDiv0)
 errstring = "#DIV"
 Case CVErr(xlErrNA)
 errstring = "#N/A"
 Case CVErr(xlErrName)
 errstring = "#NAME"
 Case CVErr(xlErrNull)
 errstring = "#NULL"
 Case CVErr(xlErrNum)
 errstring = "#NUM"
 Case CVErr(xlErrRef)
 errstring = "#REF"
 Case CVErr(xlErrValue)
 errstring = "#VALUE"
 Case Else
 errstring = vbNullString
 End Select
 
 Result = Result & delim & errstring
 Else
 Result = Result & delim & cell
 End If
 Next cell
 Concat = Right$(Result, Len(Result) - 1)
End Function
Private Sub Class_Initialize()
 Set Current.Book = ActiveWorkbook
 Set Previous.Sheet = Nothing
 Set Current.Sheet = Nothing
End Sub

Usage: Following the code that I use to initiate the class and use macro.

Module: TempModule.bas

Public Sub TestingCarryClass()
Dim CarryForward As CarryMe
Set CarryForward = New CarryMe
CarryForward.Execute
End Sub
Greedo
2,6252 gold badges15 silver badges36 bronze badges
asked Apr 21, 2021 at 20:01
\$\endgroup\$
4
  • 3
    \$\begingroup\$ Good stuff! Thanks for reading all my ramblings! \$\endgroup\$ Commented Apr 21, 2021 at 20:44
  • \$\begingroup\$ Thank you @MathieuGuindon for all your contributions. I had created hundreds of Macro and UDFs and I am now refactoring and updating all the codes using rubberduck VBA add-in, started learning OOP, SOLID principle and I cannot tell you how much happy I am discovering all these concepts that I never knew in my entire life. It's been more than 7 years and I am planning to refactor more than 30k lines of code. Thank you once again! \$\endgroup\$ Commented Apr 21, 2021 at 20:52
  • \$\begingroup\$ You are on the very site that taught me all these things, I hope you stick around! \$\endgroup\$ Commented Apr 21, 2021 at 21:06
  • \$\begingroup\$ Yes, I never knew about code reviews, while searching for a OOP, I found out about this code review meta and then after exploring questions, the battleship game was the turning point for me, I came to know about your blog and then add-in and other helpful resources and now I am with the flow of exploring the entire new universe of OOP. Thank you and the RubberduckVBA add-in team for all your contributions to community. \$\endgroup\$ Commented Apr 23, 2021 at 9:08

1 Answer 1

4
\$\begingroup\$

The OOP version of the code generated some thoughts around separation of concerns: user interactions and data processing.

Single Responsibility Principle(SRP):

Every module, class or function in a computer program should have responsibility over a single part of that program's functionality, and it should encapsulate that part.

SRP would encourage identifying user interactions and data processing as two responsibilities.

So, considering to the OOP version of CarryForwardOld, there are two modules: TempModule and CarryMe.

TempModule clearly does one thing:

It is the Entry Point to the operation to be performed. Once the Entry Point is called it delegates all work to Class Module.CarryMe for processing.

CarryMe however, does more that one 'thing':

  1. Requests user interactions to select the 'Previous' file and to acknowledge success of the process.
  2. Modifies a 'Current' workbook based on data in a 'Previous' workbook to carry forward the data.

So, a reasonable improvement to the CarryMe class would be to allow it to be tested free of human interaction. Currently, a user must select the source workbook. Also, at the end of calling Execute, the user receives a pop-up message that requires acknowledgement. Either of these user interactions eliminate the option for using a code-only test client.

CarryMe requires two Workbook objects (Previous and Current) to operate on. However, there is no reason that theCarryMe class needs to take responsibility for getting the workbooks. Further, to accomplish its task, the CarryMe class does not need to be responsible for indicating success with a pop-up message. By extracting the user interaction code from CarryMe, CarryMe.Execute can be unit tested.

So, rather than exposing a parameterless subroutine (CarryMe.Execute) to accomplish the task, one could expose a function that takes two Workbook parameters and returns True if successful (and False if it fails).

Using a Boolean returning function is a typical pattern that provides a 'safe' way to attempt an operation that could possibly fail. The pattern/function guarantees that it will return a pass/fail result rather than causing an exception or returning an error code. The functions are typically prefaced with Try and can either attempt an operation or attempt to retrieve a value/object. Either way, the 'TryXXXX' pattern relieves the calling code from having to catch an exception or evaluate error return codes.

Below, the Execute subroutine has been modified to a Boolean returning function TryExecute:

 Public Function TryExecute(ByVal currentWrkbk As Workbook, ByVal previousWrkbk As Workbook) As Boolean
 
 'TryExecute wraps the operation with error handling to guarantee Excel 
 'Application settings are reset.
 'Note: The original Execute() version has a bug in that these settings are not reset if a 
 'Previous' workbook is not selected by the user.
 If previousWrkbk Is Nothing Then Exit Function
 
 TryExecute = False 'will be set to True if the code succeeds
 On Error GoTo ErrorExit:
 With Application
 .Calculation = xlCalculationManual
 .ScreenUpdating = False
 .EnableCancelKey = xlInterrupt
 End With
 Set Current.Book = currentWorkbook
 Set Previous.Book = previousWrkbk
 
 Dim wsheet As Worksheet
 For Each wsheet In Current.Book.Sheets
 
 SetParameters wsheet.Name
 ReadDataToDictionary
 WriteDictToSheet
 
 Next wsheet
 
 TryExecute = True
 ErrorExit:
 With Application
 .ScreenUpdating = True
 .Calculation = xlCalculationAutomatic
 .StatusBar = False
 End With
 
 End Function

For automated testing (no user interaction), a test module could test TryExecute with code like:

 Public Function CarryForwardTryExecuteTest() As Boolean
 Dim previousWorkbook as Workbook
 Dim previousWorkbookPath As String
 previousWorkbookPath = <Filepath to a test 'previous' workbook>
 Set previousWorkbook = Workbooks.Open(previousWorkbookPath)
 Dim currentWorkbook as Workbook
 Dim currentWorkbookPath As String
 currentWorkbookPath = <Filepath to a test 'current' workbook>
 Set currentWorkbook = Workbooks.Open(currentWorkbookPath )
 
 Dim CarryForward As CarryMe
 Set CarryForward = New CarryMe
 CarryForwardTryExecuteTest= CarryForward.TryExecute(currentWorkbook, previousWorkbook) Then
 End Function

So, where should the extracted user interactions be handled? The module TempModule, which is invoked by a user, is a candidate to handle the user interactions. If it can be assumed that TempModule.TestingCarryClass can only be invoked by a user, then it is reasonable to support user interactions from TempModule. Otherwise, add another module and EntryPoint to be responsible for the user-initiated processes.

So, if TempModule handles the user interactions, it would look like:

 Option Explicit
 Public Sub TestingCarryClass()
 Dim previousWorkbook As Workbook
 Set previousWorkbook = SelectPreviousFile()
 
 If previousWorkbook Is Nothing Then
 Exit Sub
 End If
 
 Dim CarryForward As CarryMe
 Set CarryForward = New CarryMe
 'Note the addition of Properties CurrentRecordCount and PreviousRecordCount
 'to CarryMe
 If CarryForward.TryExecute(Application.ActiveWorkbook, previousWorkbook) Then
 MsgBox "There are " & CarryForward.CurrentRecordCount & " new records and " & CarryForward.PreviousRecordCount & " old records!", vbOKOnly, "Success!"
 Exit Sub
 End If
 
 MsgBox "Unexpected Error"
 End Sub
 'Note: unchanged
 Private Function SelectPreviousFile() As Workbook
 
 Set SelectPreviousFile = Nothing
 
 On Error GoTo ErrorHandler
 Dim dialog As FileDialog
 Set dialog = Application.FileDialog(msoFileDialogFilePicker)
 With dialog
 .AllowMultiSelect = False
 .InitialFileName = GetSetting("Office1", "CarryForward", "LastPath")
 .Title = "Select Old/Previous file for reference: " & Current.Book.Name
 .Show
 If .SelectedItems.Count <> 0 Then
 Dim selectedfile As String
 selectedfile = .SelectedItems.Item(1)
 SaveSetting "Office1", "CarryForward", "LastPath", selectedfile
 Workbooks.Open Filename:=selectedfile
 selectedfile = Mid$(selectedfile, InStrRev(selectedfile, "\") + 1)
 
 End If
 End With
 Select Case True
 Case selectedfile = vbNullString
 MsgBox "You cancelled the prcess!", vbOKOnly, "Alert!"
 
 Case selectedfile = ActiveWorkbook.Name 'Current.Book.Name
 MsgBox "Both file names canot be same! System cannot distinguish between old and new file.", vbCritical, "Rename Either of the File"
 
 Case Else
 'Set Previous.Book = Workbooks(selectedfile)
 Set SelectPreviousFile = Workbooks(selectedfile)
 End Select
 
 Exit Function
 ErrorHandler:
 If Err.Number > 0 Then 'TODO: handle specific error
 Err.Clear
 Resume Next
 End If
 End Function

Extracting user interface responsibilities from the CarryMe class allows it to focus on workbook processing (a single responsibility) with UI responsibilities handled by TempModule. And...CarryMe.TryExecute can now be unit tested.

answered Apr 23, 2021 at 3:11
\$\endgroup\$
2
  • \$\begingroup\$ Thank you so much for the inputs, I started implementing this using the command pattern from rubberduckvba (rubberduckvba.wordpress.com/2020/11/19/…) but didn't realize that for testing purpose I have to keep user interaction separated from the other functionality. Quick Question: the class should have only function exposed to client code, right? which accepts 2 workbooks and returns Boolean value. I think I missed the testing part and I have to work on making my codes more test friendly. Thanks again for the thoughts. \$\endgroup\$ Commented Apr 23, 2021 at 9:05
  • \$\begingroup\$ As there's no more suggestions, I accepted your answer and will see if someone comes up with more suggestions. :) \$\endgroup\$ Commented May 24, 2021 at 22:09

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.