Description:
Please refer to the following sample codes. These test cases are failing with the misleading error messages.
@test:Config {}
function test1() {
string[] keys = ["A"];
test:assertEquals(keys, ["A"]);
}
@test:Config {}
function test2() {
int[] arr = foo();
test:assertEquals(arr, [1]);
}
function foo() returns int[] {
int[] arr = [1];
return arr;
}
_Output_
[fail] test1:
Assertion Failed!: expected 'A' but found 'A'
at ballerina.test:createBallerinaError(assert.bal:41)
ballerina.test:assertEquals(assert.bal:82)
[fail] test2:
Assertion Failed!: expected '1' but found '1'
at ballerina.test:createBallerinaError(assert.bal:41)
ballerina.test:assertEquals(assert.bal:82)
Affected Versions:
Checked with Ballerina 1.2.0
Seems like improving the failure message is being tracked with https://github.com/ballerina-platform/ballerina-lang/issues/12892.
As for the tests themselves though the following would pass.
@test:Config {}
function test1() {
string[] keys = ["A"];
var expected = ["A"]; // no contextually expected type, so the type is set as `[string]`
test:assertEquals(keys, expected);
}
But IMO getting a comparison with an in-line definition to pass would be useful.
The current logic for test:assertEquals is as follows.
public function assertEquals(any|error actual, any|error expected, public string msg = "Assertion Failed!") {
boolean isEqual = false;
if (actual is anydata|error && expected is anydata|error) {
isEqual = actual == expected;
} else {
isEqual = actual === expected;
}
if (!isEqual) {
string expectedStr = io:sprintf("%s", expected);
string actualStr = io:sprintf("%s", actual);
string errorMsg = string `${msg}: expected '${expectedStr}' but found '${actualStr}'`;
panic createBallerinaError(errorMsg, assertFailureErrorCategory);
}
}
For something like test:assertEquals(keys, ["A"]) the second parameter's type any|error is set as the contextually expected type for ["A"]. Thus ["A"]'s type is set as any[].
This results in expected is anydata|error to evaluate to false which in turn results in a reference equality check === for this test (which then results in false).
IMO, we can improve this a bit by introducing separate test functions to assert based on value equality vs reference equality.
For example,
public function assertValueEquals(anydata|error actual, anydata|error expected, public string msg = "Assertion Failed!") - test:assertValueEquals(keys, ["A"]) will pass.public function assertReferenceEquals(any|error actual, any|error expected, public string msg = "Assertion Failed!")Maybe we can have the current assertEquals also as a convenience function for those who want the current behaviour.
@hevayo @azinneera FYA please.
At minimum I'd like to see the value vs. reference equality explained in:
When one knows that's the problem then one might be able to find equality comparison explanation in https://ballerina.io/v1-2/learn/by-example/equality.html
Following doc[1] contains an analysis on how the AssertEquals function implementation works against data types available in Ballerina 1.2.x with examples.
[1] [https://docs.google.com/document/d/11CVryMHclAX3KMX7_GLP0099cUeNKvvxawHqrFUewec/edit?usp=sharing](https://docs.google.com/document/d/11CVryMHclAX3KMX7_GLP0099cUeNKvvxawHqrFUewec/edit?usp=sharing)
In other test frameworks such as Junit5, following functionalities are available.
Based on these frameworks, the usual expectation from an "AssertEquals" function is to have deep value equality check.
I suggest that we have the following options.
assertEquals - deep value equality for (any|error) i.e. for all available types in Ballerina
Also, as @MaryamZi mentioned we can have the following too.
assertReferenceEquals - check whether expected and actual refer to same object/record/table etc.
@hevayo @azinneera @Ibaqu
Appreciate your thoughts.
Based on the current specification, we are unable to support value equality for behavioral values. So, we are planning to introduce the following functions instead of "assertEquals".
public function assertValueEquals(anydata|error actual, anydata|error expected, public string msg = "Value Assertion Failed!")
public function assertExactEquals(any|error actual, any|error expected, public string msg = "Exact Assertion Failed!")
Until there is a way to support value equality for behavioral values, we plan to remove the "assertEquals" function. See the discussion at [1].
Update: We decided to change the function signature for "assertEquals" without introducing a new "assertValueEquals"
Following is the list of tasks.
[1] https://groups.google.com/g/ballerina-dev/c/hNlQLAu3b4M/m/wOiRvJhvBAAJ
[2] https://ballerina.io/learn/testing-ballerina-code/
[3] https://ballerina.io/learn/by-example/testerina-assertions.html
[4] https://ballerina.io/learn/api-docs/ballerina/test/index.html
[5] https://github.com/ballerina-platform/ballerina-spec/blob/master/test/test-framework-spec.md
Test specification updated with https://github.com/ballerina-platform/ballerina-spec/pull/587
Most helpful comment
At minimum I'd like to see the value vs. reference equality explained in:
When one knows that's the problem then one might be able to find equality comparison explanation in https://ballerina.io/v1-2/learn/by-example/equality.html