Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: adds support for expecting on global functions and namespace functions #241

Merged
merged 6 commits into from
Oct 25, 2023

Conversation

georgejecook
Copy link
Collaborator

@georgejecook georgejecook commented Oct 22, 2023

Adds support for expecting on global functions and namespaced functions.
DOCS

Mocking global and namespaced functions

Mocking and stubbing of global functions works the same as for class methods. Please note, the functions must be in scope - you cannot mock a global function call inside of a node. So if your call crosses an observer, or callfunc bridge, it will not work. This is by design, and there are no plans to provide intra-node global or namespace mocking, at this point.

To enable this feature, set the isGlobalMethodMockingEnabled rooibos config parameter to true. DO NOT enable this on your ide's bsconfig.json, as it will negatively impact performance and may cause other issues. This setting is not compatible with watch mode, at this moment.

Configuration

In addition to the isGlobalMethodMockingEnabled, you use the following config flags:

  • isGlobalMethodMockingEfficientMode - default to true, when set causes rooibos to modify only those functions that were mocked or stubbed
  • globalMethodMockingExcludedFiles - files that rooibos will not modify, when adding global mocking or stubbing support.

Known limitations:

You must include default values in your expect calls, even if your invoking code does not use them

  • if you mock or stub a global or namespaced method, you will have to expect default parameters in your expectCalled invocation, as rooibos will receive the default values.

e.g.

namespace utils
  function getModel(name as string, asJson = false as boolean)
  end function
end namespace

'in the code
function getBeatlesModel()
  utils.getModel("Ringo")
end function

'in the unit test
@describe("getBeatlesModel")
@it("gets ringo")
function _()
  'This will fail, because rooibos will receive the asJson param with the default value
  m.expectCalled(utils.getModel("ringo"))

  'therefore we need to do this
  m.expectCalled(utils.getModel("ringo", false))
end function

Note - this will be addressed shortly; pr's are welcome in the interim.

There might be some bugs, or unknown issues

Mocking, of global and namespaced functions is supported; but is a relatively new feature. It is possible that there are some scenarios that are not supported. Please raise issues in the rooibos slack channel, if you spot any anomalies.

EXAMPLE

Given Global.bs, with contents

function globalSayHello(firstName = "" as string, lastName = "" as string)
  return firstName + ", " + lastName
end function

namespace utils
  function sayHello(firstName = "" as string, lastName = "" as string)
    return firstName + ", " + lastName
  end function
end namespace

Once can now write tests like this:

    '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    @describe("global functions")
    '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    @it("uses default implementation on global functions")
    function _()
      m.assertEqual(utils.sayHello("John", "Lennon"), "John, Lennon")
    end function

    @it("uses default implementation on namespaced functions")
    function _()
      m.assertEqual(globalSayHello("John", "Lennon"), "John, Lennon")
    end function

    @it("supports mocks on global functions")
    function _()
      m.expectCalled(globalSayHello("John", "Lennon"), "john")

      m.assertEqual(globalSayHello("John", "Lennon"), "john")
    end function

    @it("supports mocks on namespaced functions")
    function _()
      m.expectCalled(utils.sayHello("Paul", "McCartney"), "paul")

      m.assertEqual(utils.sayHello("Paul", "McCartney"), "paul")
    end function

    @it("reports uncalled mocks on global functions")
    function _()
      m.expectCalled(globalSayHello("John", "Lennon"), "john")

      m.assertRunningTestIsFailed()
    end function

    @it("reports uncalled mocks on namespaced functions")
    function _()
      m.expectCalled(utils.sayHello("Paul", "McCartney"), "paul")

      m.assertRunningTestIsFailed()
    end function

    @it("resets to default implementation on global functions")
    function _()
      m.assertEqual(utils.sayHello("John", "Lennon"), "John, Lennon")
    end function

    @it("resets to default implementation on namespaced functions")
    function _()
      m.assertEqual(globalSayHello("John", "Lennon"), "John, Lennon")
    end function

    @it("supports overloaded mocks on global functions")
    function _()
      m.expectCalled(globalSayHello("John", "Lennon"), "john")
      m.expectCalled(globalSayHello("John", "Lennon"), "john2")

      m.assertEqual(globalSayHello("John", "Lennon"), "john")
      m.assertEqual(globalSayHello("John", "Lennon"), "john2")
    end function

    @it("supports overloaded mocks on namespaced functions")
    function _()
      m.expectCalled(utils.sayHello("Paul", "McCartney"), "paul1")
      m.expectCalled(utils.sayHello("Paul", "McCartney"), "paul2")

      m.assertEqual(utils.sayHello("Paul", "McCartney"), "paul1")
      m.assertEqual(utils.sayHello("Paul", "McCartney"), "paul2")
    end function

Comment on lines +316 to +352
instance = {}
instance.new = sub()
end sub
instance.sayHello = sub(a1, a2)
print "hello"
end sub
return instance
end function
function beings_Person()
instance = __beings_Person_builder()
instance.new()
return instance
end function

function beings_sayHello()
if RBS_SM_1_getMocksByFunctionName()["beings_sayhello"] <> invalid
result = RBS_SM_1_getMocksByFunctionName()["beings_sayhello"].callback()
return result
end if
print "hello2"
end function

function sayHello()
if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid
result = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback()
return result
end if
print "hello3"
end function

function RBS_SM_1_getMocksByFunctionName()
if m._rMocksByFunctionName = invalid
m._rMocksByFunctionName = {}
end if
return m._rMocksByFunctionName
end function
`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you hate proper indentation? :P

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol. I honestly find if I do anything else, it won't pass the test. Always been too busy to sort it out.

@georgejecook georgejecook merged commit 10f6305 into master Oct 25, 2023
8 checks passed
@georgejecook georgejecook deleted the feat/mock-global-funcs branch October 25, 2023 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants