rubberduck-vba / Rubberduck

Every programmer needs a rubberduck. COM add-in for the VBA & VB6 IDE (VBE).
https://rubberduckvba.com
GNU General Public License v3.0
1.92k stars 302 forks source link

Intellisense is not working for Assert in Unit Test TestModule #6223

Closed PioPio2 closed 2 months ago

PioPio2 commented 2 months ago

Rubberduck version information The info below can be copy-paste-completed from the first lines of Rubberduck's log or the About box:

Rubberduck version 2.5.9.6316
Operating System: Win7 32bit
Host Product: Microsoft Office x86
Host Version: 16.0.17726.20160
Host Executable: ACCESS.EXE

Description In the TestModule when I type Assert. I don't have the suggestion of the methods available

To Reproduce

  1. Create a new TestModule
  2. Create a TestMethod
  3. add the statement of the the procedure you want to test
  4. type Assert. but the suggestion don't show up
  5. See error error

Expected behavior Default

PioPio2 commented 2 months ago

I forgot to add the intellisense is not working only for "Assert." but it is working regularly for all the other cases

PioPio2 commented 2 months ago

I also made the following test in the TestModule

'@ModuleInitialize
Private Sub ModuleInitialize()
    'this method runs once per module.
    Set Assert = CreateObject("Rubberduck.AssertClass")
    Assert.inco
    Set Fakes = CreateObject("Rubberduck.FakesProvider")
End Sub

then I compiled and I had no errors on Assert.inco. It was only when I ran the test I got the error.

Greedquest commented 2 months ago

This is possibly because you have not declared the class "Early Bound"

Option Explicit

'Early Binding
Private Assert As Rubberduck.PermissiveAssertClass

'Late Binding, no intellisense
Private Assert As Object

There is a Rubberduck Setting for the default, although you can change it by hand in the editor

image

Let me know if that is the fix

Greedquest commented 2 months ago

image

N.b. Early binding requires this reference, RD will add automatically but it's another dependency for your proj. Late binding is a bit more flexible in that way

PioPio2 commented 2 months ago

Early binding was already set in the default and "Rubberduck AddIn" was already in the reference list. However, I added

'Early Binding
Private Assert As Rubberduck.PermissiveAssertClass

to my code and the intellisense is working now

retailcoder commented 2 months ago

the intellisense is working now

Happy to hear!

Please note that this will be true of any object you interact with in VBA: the compiler (and IDE) can only "see" the members of an object if it knows what interface to look at, at compile time.

If all it's seeing is Variant or Object, it defers to run-time, and if the member isn't there then a run-time error (438) is raised - so that's what late binding refers to: the compiler is completely blind, and so is the IDE.

By declaring an explicit class/interface type to work with, the IDE can fetch the members to give you a completion list, because it can bind the type at compile time, and then Rubberduck (and others, surely) can keep track of what's being called, too.

Whenever you're typing VBA code and you type a dot and nothing happens when you expect a dropdown list, it's because you're interacting with an object whose compile-time type is unknown. Making a late-bound call here, is making an implicit assumption about what type of object we're working with, essentially telling the compiler "it's ok I got this". We can get back to type safety by declaring an actual variable of that type, and then working with that variable. That's basically what happens here with the declared type of the Assert variable: if we make it Object, then all member calls against it will be late-bound.

Late binding is useful for Rubberduck unit tests because the project will still be fully compilable without the Rubberduck type library present. If a VBA project is distributed with its early-bound unit tests, the project itself will run, but will not be fully compilable unless Rubberduck is also installed on the end user's machine; the code can still run regardless, because a module is only loaded in memory if it's needed... and no code should ever be calling into a test module.