diff --git a/docs/index.md b/docs/index.md index 142fb082..26994728 100644 --- a/docs/index.md +++ b/docs/index.md @@ -559,7 +559,7 @@ function LoadDetails() as void end function ``` -We can use a mock, to facilitate testing against the network layer +We can use a stub, to facilitate testing against the network layer ``` detailsVM = CreateDetailsVM() @@ -572,7 +572,7 @@ m.AssertFalse(detailsVM.isLoading) m.AssertTure(detailsVM.isShowingError) ``` -In this case, our detailsVM object, wil +In this case, our detailsVM object, will not actually call ExecuteNetRequests's source code; but will instead call a _fake_ (i.e fake method body), which can return predtermined values, or be later checked for invocation arg conformance. #### Mocks Mocks are _expected_ fakes. Your code will invoke the method, as if it _is_ the real method; but the difference is that Rooibos will track the invoction of mocks, and if the method was not invoked in the manner you expected (i.e. with the expected parameters and the expected number of invocations) then a unit test failure will result. @@ -580,7 +580,7 @@ Mocks are _expected_ fakes. Your code will invoke the method, as if it _is_ the We create mocks by using the methods: - Expect - Creates a generic mock - - ExpectOnce - Creates a mock, which we expect to be called once + - ExpectOnce - Creates a mock, which we expect to be called once _or can created individual overloaded calls to the same method_ - ExpectNone - Creates a mock, which we _never_ expect to be invoked ### Asserting mocks @@ -627,6 +627,20 @@ You can save yourself a lot of time, and really think about and kick the tyres o Up to 9 arguments are supported. +#### Expecting several calls to the same method, with verified invocation params + +You may wish to call the same method various times, and return different values, or check different arguments were invoked + +In this case, we use overloaded `expectOnce` calls, as per the following example: + +``` + m.expectOnce(obj, "mockMethod", [arg1], result1, true) + m.expectOnce(obj, "mockMethod", [arg2], result2, true) + m.expectOnce(obj, "mockMethod", [arg3], result3, true) +``` + +This will now set the framework to expect 3 calls to mockMethod, the first with value of _arg1_, returning _result1_; the second with value of _arg2_, returning _result2_; and the last with value of _arg3_, returning _result3_ + #### Specifying an expected value of invalid, with m.invalidValue diff --git a/frameworkTests/source/tests/AssertionTests.brs b/frameworkTests/source/tests/AssertionTests.brs index 2497ab4c..fc54ddb9 100644 --- a/frameworkTests/source/tests/AssertionTests.brs +++ b/frameworkTests/source/tests/AssertionTests.brs @@ -6,14 +6,14 @@ '@Test Fail function Simp_basic_Fail() as void - - assertResult = m.Fail("reason") - - isFail = m.currentResult.isFail - m.currentResult.Reset() - - m.AssertFalse(assertResult) - m.AssertTrue(isFail) + + assertResult = m.Fail("reason") + + isFail = m.currentResult.isFail + m.currentResult.Reset() + + m.AssertFalse(assertResult) + m.AssertTrue(isFail) end function '@Test AssertTrue @@ -24,13 +24,13 @@ end function '@Params[1, false] '@Params["test", false] function Simp_basic_AssertTrue(value, expectedAssertResult) as void - - assertResult = m.AssertTrue(value) - isFail = m.currentResult.isFail - m.currentResult.Reset() - - m.AssertEqual(assertResult, expectedAssertResult) - m.AssertEqual(isFail, not expectedAssertResult) + + assertResult = m.AssertTrue(value) + isFail = m.currentResult.isFail + m.currentResult.Reset() + + m.AssertEqual(assertResult, expectedAssertResult) + m.AssertEqual(isFail, not expectedAssertResult) end function '@Test AssertFalse @@ -41,14 +41,14 @@ end function '@Params[1, false] '@Params["test", false] function Simp_basic_AssertFalse(value, expectedAssertResult) as void - - assertResult = m.AssertFalse(value) - - isFail = m.currentResult.isFail - m.currentResult.Reset() - - m.AssertEqual(assertResult, expectedAssertResult) - m.AssertEqual(isFail, not expectedAssertResult) + + assertResult = m.AssertFalse(value) + + isFail = m.currentResult.isFail + m.currentResult.Reset() + + m.AssertEqual(assertResult, expectedAssertResult) + m.AssertEqual(isFail, not expectedAssertResult) end function '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -73,14 +73,14 @@ end function '@Params[[{"one":1}, {"two":2}], [{"one":"1"}, {"two":"a"}] ] '@Params[[{"one":1}, {"two":2}], [{"a":1}, {"a":1}, {"a":1}] ] function Simp_AssertArrayContainsAAs_Fail(expectedAAs, items) as void - - assertResult = m.AssertArrayContainsAAs(items, expectedAAs) - - isFail = m.currentResult.isFail - m.currentResult.Reset() - - m.AssertFalse(assertResult) - m.AssertTrue(isFail) + + assertResult = m.AssertArrayContainsAAs(items, expectedAAs) + + isFail = m.currentResult.isFail + m.currentResult.Reset() + + m.AssertFalse(assertResult) + m.AssertTrue(isFail) end function @@ -93,14 +93,14 @@ end function '@Params[[{"one":1, "two":2}, {"one":1}], [{"one":1}, { "two":2, "one":1}]] '@Params[[{"one":1, "two":2}, {"one":1}, {"three":3}], [{"one":1}, {"three":3}, { "two":2, "one":1}]] function Simp_AssertArrayContainsAAs_Pass(expectedAAs, items) as void - - assertResult = m.AssertArrayContainsAAs(items, expectedAAs) - - isFail = m.currentResult.isFail - - m.currentResult.Reset() - m.AssertTrue(assertResult) - m.AssertFalse(isFail) + + assertResult = m.AssertArrayContainsAAs(items, expectedAAs) + + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertTrue(assertResult) + m.AssertFalse(isFail) end function '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -141,16 +141,16 @@ end function '@Params[[{"test":1}, {"test":1}], "AssociativeArray"] function Simp_AssertArrayContainsOnlyValuesOfType_Pass(values, typeName) as void - assertResult = m.AssertArrayContainsOnlyValuesOfType(values, typeName) - isFail = m.currentResult.isFail + assertResult = m.AssertArrayContainsOnlyValuesOfType(values, typeName) + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertTrue(assertResult) + m.AssertFalse(isFail) - m.currentResult.Reset() - m.AssertTrue(assertResult) - m.AssertFalse(isFail) - end function -'@Test fail +'@Test fail '@Params[["one", 2, "three"], "String"] '@Params[[1, "two", 3], "Integer"] '@Params[[true, "true", false], "Boolean"] @@ -164,23 +164,23 @@ end function '@Params[[{"test":1}, {"test":1}], "Array"] function Simp_AssertArrayContainsOnlyValuesOfType_Fail(values, typeName) as void - assertResult = m.AssertArrayContainsOnlyValuesOfType(values, typeName) - isFail = m.currentResult.isFail + assertResult = m.AssertArrayContainsOnlyValuesOfType(values, typeName) + isFail = m.currentResult.isFail + + isFail = m.currentResult.isFail + m.currentResult.Reset() + + m.AssertFalse(assertResult) + m.AssertTrue(isFail) - isFail = m.currentResult.isFail - m.currentResult.Reset() - - m.AssertFalse(assertResult) - m.AssertTrue(isFail) - end function '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ '@It tests white spaces work with annotations '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -'some comments to +'some comments to 'demonstrate '@Test comments between tests 'that we can have comments @@ -189,7 +189,7 @@ function Simp_whiteSpacing() as void m.AssertTrue(true) end function -'some comments to +'some comments to 'demonstrate '@Test comments between tests with params '@Params[1] @@ -224,9 +224,9 @@ end function '@Params[[3, 2, 1], [1, 2, 3], false] function Simp_EqArray_Pass(values, values2, expected) as void - result = m.EqArray(values, values2) - m.AssertEqual(result, expected) - + result = m.EqArray(values, values2) + m.AssertEqual(result, expected) + end function '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -243,13 +243,13 @@ end function '@Params[[invalid]] function Simp_AssertNotEmpty_Pass(values) as void - assertResult = m.AssertNotEmpty(values) - isFail = m.currentResult.isFail + assertResult = m.AssertNotEmpty(values) + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertTrue(assertResult) + m.AssertFalse(isFail) - m.currentResult.Reset() - m.AssertTrue(assertResult) - m.AssertFalse(isFail) - end function '@Test fail @@ -260,13 +260,13 @@ end function '@Params[""] function Simp_AssertNotEmpty_Fail(values) as void - assertResult = m.AssertNotEmpty(values) - isFail = m.currentResult.isFail + assertResult = m.AssertNotEmpty(values) + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(assertResult) + m.AssertTrue(isFail) - m.currentResult.Reset() - m.AssertFalse(assertResult) - m.AssertTrue(isFail) - end function '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -279,12 +279,12 @@ end function '@Params[""] function Simp_AssertEmpty_Pass(values) as void - assertResult = m.AssertEmpty(values) - isFail = m.currentResult.isFail + assertResult = m.AssertEmpty(values) + isFail = m.currentResult.isFail - m.currentResult.Reset() - m.AssertTrue(assertResult) - m.AssertFalse(isFail) + m.currentResult.Reset() + m.AssertTrue(assertResult) + m.AssertFalse(isFail) end function @@ -300,24 +300,24 @@ end function '@Params[[invalid]] function Simp_AssertEmpty_Fail(values) as void - assertResult = m.AssertEmpty(values) - isFail = m.currentResult.isFail + assertResult = m.AssertEmpty(values) + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(assertResult) + m.AssertTrue(isFail) - m.currentResult.Reset() - m.AssertFalse(assertResult) - m.AssertTrue(isFail) - end function '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -'@It tests expectOnce +'@It tests expect '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ '@Test multi return values function Simp_expect_multiValues() obj = {} m.expect(obj, "mockMethod", 5, invalid, {"multiResult": ["one", 2, invalid, "last"]}, true) - + result = obj.mockMethod() m.AssertEqual(result, "one") @@ -329,47 +329,184 @@ function Simp_expect_multiValues() result = obj.mockMethod() m.AssertEqual(result, "last") - + result = obj.mockMethod() m.AssertEqual(result, "last") + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(isFail) + +end function + + +'@Test can set up multi expects on same method +function Simp_expect_multiExpect_success() + obj = {} + arg1 = "arg1" + arg2 = "arg2" + arg3 = "arg3" + result1 = 1 + result2 = 2 + result3 = 3 + + m.expectOnce(obj, "mockMethod", [arg1], result1, true) + m.expectOnce(obj, "mockMethod", [arg2], result2, true) + m.expectOnce(obj, "mockMethod", [arg3], result3, true) + + result = obj.mockMethod(arg1) + m.AssertEqual(result, result1) + + result = obj.mockMethod(arg2) + m.AssertEqual(result, result2) + + result = obj.mockMethod(arg3) + m.AssertEqual(result, result3) + + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(isFail) + +end function + +'@Test can set up multi expects on same method - one invocation with any args +function Simp_expect_multiExpect_success_oneCallsArgsNotTracked() + obj = {} + arg1 = "arg1" + arg2 = "arg2" + arg3 = "arg3" + result1 = 1 + result2 = 2 + result3 = 3 + + m.expectOnce(obj, "mockMethod", [arg1], result1, true) + m.expectOnce(obj, "mockMethod", invalid, result2, true) + m.expectOnce(obj, "mockMethod", [arg3], result3, true) + + result = obj.mockMethod(arg1) + m.AssertEqual(result, result1) + + result = obj.mockMethod("do not care about args", "used in invocation", 2) + m.AssertEqual(result, result2) + + result = obj.mockMethod(arg3) + m.AssertEqual(result, result3) + + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(isFail) + +end function + +'@Test can set up multi expects on same method - multi params +function Simp_expect_multiExpect_multi_args_success() + obj = {} + arg1 = "arg1" + arg2 = "arg2" + arg3 = "arg3" + result1 = 1 + result2 = 2 + result3 = 3 + + m.expectOnce(obj, "mockMethod", [arg1, arg2, arg3], result1, true) + m.expectOnce(obj, "mockMethod", [arg2, arg3, arg1], result2, true) + m.expectOnce(obj, "mockMethod", [arg3, arg2, arg1], result3, true) + + result = obj.mockMethod(arg1, arg2, arg3) + m.AssertEqual(result, result1) + + result = obj.mockMethod(arg2, arg3, arg1) + m.AssertEqual(result, result2) + + result = obj.mockMethod(arg3, arg2, arg1) + m.AssertEqual(result, result3) + + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertFalse(isFail) + +end function + +'@Test can set up multi expects on same method +'@Params["arg1_", "arg2", "arg3"] +'@Params["arg1", "arg2", "arg3_"] +'@Params["arg1", "arg2_", "arg3"] +'@Params["arg1", "arg2_", "arg3"] +'@Params["arg1_", "arg2_", "arg3"] +'@Params["arg1_", invalid, "arg3"] +function Simp_expect_multiExpect_fail(call1, call2, call3) + obj = {} + arg1 = "arg1" + arg2 = "arg2" + arg3 = "arg3" + result1 = 1 + result2 = 2 + result3 = 3 + + m.expectOnce(obj, "mockMethod", [arg1], result1, true) + m.expectOnce(obj, "mockMethod", [arg2], result2, true) + m.expectOnce(obj, "mockMethod", [arg3], result3, true) + + result = obj.mockMethod(call1) + m.AssertEqual(result, result1) + + result = obj.mockMethod(call2) + m.AssertEqual(result, result2) + + result = obj.mockMethod(call2) + m.AssertEqual(result, result3) + + m.assertMocks() + isFail = m.currentResult.isFail + + m.currentResult.Reset() + m.AssertTrue(isFail) + end function 'ASSERTIONS TO WRITE TESTS FOR! 'This is coming soon! -' AssertEqual -' AssertLike -' AssertNotEqual -' AssertInvalid -' AssertNotInvalid -' AssertAAHasKey -' AssertAANotHasKey -' AssertAAHasKeys -' AssertAANotHasKeys -' AssertArrayNotContains -' AssertArrayContainsSubset +' AssertEqual +' AssertLike +' AssertNotEqual +' AssertInvalid +' AssertNotInvalid +' AssertAAHasKey +' AssertAANotHasKey +' AssertAAHasKeys +' AssertAANotHasKeys +' AssertArrayNotContains +' AssertArrayContainsSubset ' AssertArrayNotContainsSubsetet -' AssertArrayCount -' AssertArrayNotCount -' AssertArrayContainsOnly -' AssertType -' AssertSubType -' +' AssertArrayCount +' AssertArrayNotCount +' AssertArrayContainsOnly +' AssertType +' AssertSubType +' ' 'Node extensions -' AssertNodeCount -' AssertNodeNotCount -' AssertNodeEmpty -' AssertNodeNotEmpty -' AssertNodeContains -' AssertNodeNotContains -' AssertNodeContainsFields -' AssertNodeNotContainsFields - -' AssertArray -' AssertAAContainsSubset +' AssertNodeCount +' AssertNodeNotCount +' AssertNodeEmpty +' AssertNodeNotEmpty +' AssertNodeContains +' AssertNodeNotContains +' AssertNodeContainsFields +' AssertNodeNotContainsFields + +' AssertArray +' AssertAAContainsSubset ' ' 'Mocking and stubbing -' AssertMocks -' MockFail \ No newline at end of file +' AssertMocks +' MockFail \ No newline at end of file diff --git a/samples/example/source/tests/rooibos/rooibosDist.brs b/samples/example/source/tests/rooibos/rooibosDist.brs index e164a728..191fa7ea 100644 --- a/samples/example/source/tests/rooibos/rooibosDist.brs +++ b/samples/example/source/tests/rooibos/rooibosDist.brs @@ -94,6 +94,7 @@ function BaseTestSuite() as object this.Mock = RBS_BTS_Mock this.AssertMocks = RBS_BTS_AssertMocks this.CreateFake = RBS_BTS_CreateFake + this.CombineFakes = RBS_BTS_CombineFakes this.MockFail = RBS_BTS_MockFail this.CleanMocks = RBS_BTS_CleanMocks this.CleanStubs = RBS_BTS_CleanStubs @@ -941,34 +942,47 @@ function RBS_BTS_Mock(target, methodName, expectedInvocations = 1, expectedArgs m.__mockId = -1 m.mocks = {} end if - m.__mockId++ - if (m.__mockId > 5) - ? "ERROR ONLY 6 MOCKS PER TEST ARE SUPPORTED!! you're on # " ; m.__mockId - ? " Method was " ; methodName - return invalid - end if - id = stri(m.__mockId).trim() - fake = m.CreateFake(id, target, methodName, expectedInvocations, expectedArgs, returnValue) - m.mocks[id] = fake 'this will bind it to m - allowNonExisting = m.allowNonExistingMethodsOnMocks = true or allowNonExistingMethods - if (type(target[methodName]) = "Function" or type(target[methodName]) = "roFunction" or allowNonExisting) - target[methodName] = m["MockCallback" + id] - target.__mocks = m.mocks - if (allowNonExisting) - ? "WARNING - mocking call " ; methodName; " which did not exist on target object" + fake = invalid + for i = 0 to m.__mockId + id = stri(i).trim() + if m.mocks[id] <> invalid and m.mocks[id].methodName = methodName + fake = m.mocks[id] + exit for end if - else - ? "ERROR - could not create Mock : method not found "; target ; "." ; methodName + end for + if fake = invalid + m.__mockId++ + id = stri(m.__mockId).trim() + if (m.__mockId > 6) + ? "ERROR ONLY 6 MOCKS PER TEST ARE SUPPORTED!! you're on # " ; m.__mockId + ? " Method was " ; methodName + return invalid + end if + fake = m.CreateFake(id, target, methodName, expectedInvocations, expectedArgs, returnValue) + m.mocks[id] = fake 'this will bind it to m + allowNonExisting = m.allowNonExistingMethodsOnMocks = true or allowNonExistingMethods + if (type(target[methodName]) = "Function" or type(target[methodName]) = "roFunction" or allowNonExisting) + target[methodName] = m["MockCallback" + id] + target.__mocks = m.mocks + if (allowNonExisting) + ? "WARNING - mocking call " ; methodName; " which did not exist on target object" + end if + else + ? "ERROR - could not create Mock : method not found "; target ; "." ; methodName + end if + else + m.CombineFakes(fake, m.CreateFake(id, target, methodName, expectedInvocations, expectedArgs, returnValue)) end if return fake end function function RBS_BTS_CreateFake(id, target, methodName, expectedInvocations = 1, expectedArgs =invalid, returnValue=invalid ) as object expectedArgsValues = [] - hasArgs = expectedArgs <> invalid + hasArgs = RBS_CMN_IsArray(expectedArgs) if (hasArgs) defaultValue = m.invalidValue else defaultValue = m.ignoreValue + expectedArgs = [] end if for i = 0 to 9 if (hasArgs and expectedArgs.count() > i) @@ -1021,19 +1035,47 @@ function RBS_BTS_CreateFake(id, target, methodName, expectedInvocations = 1, exp } return fake end function +function RBS_BTS_CombineFakes(fake, otherFake) + if type(fake.expectedArgs) <> "roAssociativeArray" or not fake.expectedArgs.doesExist("multiInvoke") + currentExpectedArgsArgs = fake.expectedArgs + fake.expectedArgs = { + "multiInvoke": [currentExpectedArgsArgs] + } + end if + fake.expectedArgs.multiInvoke.push(otherFake.expectedArgs) + if type(fake.returnValue) <> "roAssociativeArray" or not fake.returnValue.doesExist("multiResult") + currentReturnValue = fake.returnValue + fake.returnValue = { + "multiResult": [currentReturnValue] + } + end if + fake.returnValue.multiResult.push(otherFake.returnValue) + fake.expectedInvocations++ +end function function RBS_BTS_AssertMocks() as void - if (m.__mockId = invalid ) return - lastId = int(m.__mockId) - for each id in m.mocks - mock = m.mocks[id] - methodName = mock.methodName - if (mock.expectedInvocations <> mock.invocations) - m.MockFail(methodName, "Wrong number of calls. (" + stri(mock.invocations).trim() + " / " + stri(mock.expectedInvocations).trim() + ")") - return - else if (mock.expectedInvocations > 0 and RBS_CMN_IsArray(mock.expectedArgs)) - for i = 0 to mock.expectedargs.count() -1 - value = mock.invokedArgs[i] - expected = mock.expectedargs[i] + if (m.__mockId = invalid or not RBS_CMN_IsAssociativeArray(m.mocks)) + return + end if + lastId = int(m.__mockId) + for each id in m.mocks + mock = m.mocks[id] + methodName = mock.methodName + if (mock.expectedInvocations <> mock.invocations) + m.MockFail(methodName, "Wrong number of calls. (" + stri(mock.invocations).trim() + " / " + stri(mock.expectedInvocations).trim() + ")") + m.CleanMocks() + return + else if mock.expectedInvocations > 0 and (RBS_CMN_IsArray(mock.expectedArgs) or (type(mock.expectedArgs) = "roAssociativeArray" and RBS_CMN_IsArray(mock.expectedArgs.multiInvoke))) + isMultiArgsSupported = type(mock.expectedArgs) = "roAssociativeArray" and RBS_CMN_IsArray(mock.expectedArgs.multiInvoke) + for invocationIndex = 0 to mock.invocations - 1 + invokedArgs = mock.allInvokedArgs[invocationIndex] + if isMultiArgsSupported + expectedArgs = mock.expectedArgs.multiInvoke[invocationIndex] + else + expectedArgs = mock.expectedArgs + end if + for i = 0 to expectedArgs.count() -1 + value = invokedArgs[i] + expected = expectedArgs[i] didNotExpectArg = RBS_CMN_IsString(expected) and expected = m.invalidValue if (didNotExpectArg) expected = invalid @@ -1042,14 +1084,16 @@ function RBS_BTS_AssertMocks() as void if (expected = invalid) expected = "[INVALID]" end if - m.MockFail(methodName, "Expected arg #" + stri(i).trim() + " to be '" + RBS_CMN_AsString(expected) + "' got '" + RBS_CMN_AsString(value) + "')") + m.MockFail(methodName, "on Invocation #" + stri(invocationIndex).trim() + ", expected arg #" + stri(i).trim() + " to be '" + RBS_CMN_AsString(expected) + "' got '" + RBS_CMN_AsString(value) + "')") + m.CleanMocks() return end if end for - end if - end for - m.CleanMocks() - end function + end for + end if + end for + m.CleanMocks() +end function function RBS_BTS_CleanMocks() as void if m.mocks = invalid return for each id in m.mocks diff --git a/src/Rooibos_BaseTestSuite.brs b/src/Rooibos_BaseTestSuite.brs index 6c67c1d6..2d9ad3a8 100644 --- a/src/Rooibos_BaseTestSuite.brs +++ b/src/Rooibos_BaseTestSuite.brs @@ -68,6 +68,7 @@ function BaseTestSuite() as object this.Mock = RBS_BTS_Mock this.AssertMocks = RBS_BTS_AssertMocks this.CreateFake = RBS_BTS_CreateFake + this.CombineFakes = RBS_BTS_CombineFakes this.MockFail = RBS_BTS_MockFail this.CleanMocks = RBS_BTS_CleanMocks this.CleanStubs = RBS_BTS_CleanStubs @@ -1527,29 +1528,42 @@ function RBS_BTS_Mock(target, methodName, expectedInvocations = 1, expectedArgs m.__mockId = -1 m.mocks = {} end if - m.__mockId++ + + fake = invalid + 'ascertain if mock already exists + for i = 0 to m.__mockId + id = stri(i).trim() + if m.mocks[id] <> invalid and m.mocks[id].methodName = methodName + fake = m.mocks[id] + exit for + end if + end for + if fake = invalid + m.__mockId++ + id = stri(m.__mockId).trim() + + if (m.__mockId > 6) + ? "ERROR ONLY 6 MOCKS PER TEST ARE SUPPORTED!! you're on # " ; m.__mockId + ? " Method was " ; methodName + return invalid + end if - if (m.__mockId > 5) - ? "ERROR ONLY 6 MOCKS PER TEST ARE SUPPORTED!! you're on # " ; m.__mockId - ? " Method was " ; methodName - return invalid - end if + fake = m.CreateFake(id, target, methodName, expectedInvocations, expectedArgs, returnValue) + m.mocks[id] = fake 'this will bind it to m + allowNonExisting = m.allowNonExistingMethodsOnMocks = true or allowNonExistingMethods + if (type(target[methodName]) = "Function" or type(target[methodName]) = "roFunction" or allowNonExisting) + target[methodName] = m["MockCallback" + id] + target.__mocks = m.mocks - id = stri(m.__mockId).trim() - fake = m.CreateFake(id, target, methodName, expectedInvocations, expectedArgs, returnValue) - m.mocks[id] = fake 'this will bind it to m - allowNonExisting = m.allowNonExistingMethodsOnMocks = true or allowNonExistingMethods - if (type(target[methodName]) = "Function" or type(target[methodName]) = "roFunction" or allowNonExisting) - target[methodName] = m["MockCallback" + id] - target.__mocks = m.mocks - - if (allowNonExisting) - ? "WARNING - mocking call " ; methodName; " which did not exist on target object" + if (allowNonExisting) + ? "WARNING - mocking call " ; methodName; " which did not exist on target object" + end if + else + ? "ERROR - could not create Mock : method not found "; target ; "." ; methodName end if - else - ? "ERROR - could not create Mock : method not found "; target ; "." ; methodName + else + m.CombineFakes(fake, m.CreateFake(id, target, methodName, expectedInvocations, expectedArgs, returnValue)) end if - return fake end function @@ -1568,11 +1582,12 @@ end function ' */ function RBS_BTS_CreateFake(id, target, methodName, expectedInvocations = 1, expectedArgs =invalid, returnValue=invalid ) as object expectedArgsValues = [] - hasArgs = expectedArgs <> invalid + hasArgs = RBS_CMN_IsArray(expectedArgs) if (hasArgs) defaultValue = m.invalidValue else defaultValue = m.ignoreValue + expectedArgs = [] end if for i = 0 to 9 @@ -1631,6 +1646,26 @@ function RBS_BTS_CreateFake(id, target, methodName, expectedInvocations = 1, exp return fake end function +function RBS_BTS_CombineFakes(fake, otherFake) + 'add on the expected invoked args + if type(fake.expectedArgs) <> "roAssociativeArray" or not fake.expectedArgs.doesExist("multiInvoke") + currentExpectedArgsArgs = fake.expectedArgs + fake.expectedArgs = { + "multiInvoke": [currentExpectedArgsArgs] + } + end if + fake.expectedArgs.multiInvoke.push(otherFake.expectedArgs) + + 'add on the expected return values + if type(fake.returnValue) <> "roAssociativeArray" or not fake.returnValue.doesExist("multiResult") + currentReturnValue = fake.returnValue + fake.returnValue = { + "multiResult": [currentReturnValue] + } + end if + fake.returnValue.multiResult.push(otherFake.returnValue) + fake.expectedInvocations++ +end function ' /** ' * @memberof module:BaseTestSuite ' * @name AssertMocks @@ -1639,18 +1674,30 @@ end function ' * @description Will check all mocks that have been created to ensure they were invoked the expected amount of times, with the expected args. ' */ function RBS_BTS_AssertMocks() as void - if (m.__mockId = invalid ) return - lastId = int(m.__mockId) - for each id in m.mocks - mock = m.mocks[id] - methodName = mock.methodName - if (mock.expectedInvocations <> mock.invocations) - m.MockFail(methodName, "Wrong number of calls. (" + stri(mock.invocations).trim() + " / " + stri(mock.expectedInvocations).trim() + ")") - return - else if (mock.expectedInvocations > 0 and RBS_CMN_IsArray(mock.expectedArgs)) - for i = 0 to mock.expectedargs.count() -1 - value = mock.invokedArgs[i] - expected = mock.expectedargs[i] + if (m.__mockId = invalid or not RBS_CMN_IsAssociativeArray(m.mocks)) + return + end if + lastId = int(m.__mockId) + for each id in m.mocks + mock = m.mocks[id] + methodName = mock.methodName + if (mock.expectedInvocations <> mock.invocations) + m.MockFail(methodName, "Wrong number of calls. (" + stri(mock.invocations).trim() + " / " + stri(mock.expectedInvocations).trim() + ")") + m.CleanMocks() + return + else if mock.expectedInvocations > 0 and (RBS_CMN_IsArray(mock.expectedArgs) or (type(mock.expectedArgs) = "roAssociativeArray" and RBS_CMN_IsArray(mock.expectedArgs.multiInvoke))) + isMultiArgsSupported = type(mock.expectedArgs) = "roAssociativeArray" and RBS_CMN_IsArray(mock.expectedArgs.multiInvoke) + + for invocationIndex = 0 to mock.invocations - 1 + invokedArgs = mock.allInvokedArgs[invocationIndex] + if isMultiArgsSupported + expectedArgs = mock.expectedArgs.multiInvoke[invocationIndex] + else + expectedArgs = mock.expectedArgs + end if + for i = 0 to expectedArgs.count() -1 + value = invokedArgs[i] + expected = expectedArgs[i] didNotExpectArg = RBS_CMN_IsString(expected) and expected = m.invalidValue if (didNotExpectArg) expected = invalid @@ -1660,15 +1707,17 @@ function RBS_BTS_AssertMocks() as void expected = "[INVALID]" end if - m.MockFail(methodName, "Expected arg #" + stri(i).trim() + " to be '" + RBS_CMN_AsString(expected) + "' got '" + RBS_CMN_AsString(value) + "')") + m.MockFail(methodName, "on Invocation #" + stri(invocationIndex).trim() + ", expected arg #" + stri(i).trim() + " to be '" + RBS_CMN_AsString(expected) + "' got '" + RBS_CMN_AsString(value) + "')") + m.CleanMocks() return end if end for - end if - end for + end for + end if + end for - m.CleanMocks() - end function + m.CleanMocks() +end function ' /** ' * @memberof module:BaseTestSuite