Probable TestBox bug. Just sanity checking it befo...
# testing
a
Probable TestBox bug. Just sanity checking it before I commit to raising it. I've seen what seems to be some incorrect behaviour in the
notToThrow
matcher. Here are some tests: https://gist.github.com/adamcameron/e1674e216bd8b759fb34e663fc8a6603
Copy code
import test.BaseSpec

component extends=BaseSpec {

    function run() {
        describe("Tests TestBox assertions", () => {
            describe("Tests toThrow", () => {
                it("should pass because it DOES throw an exception with the matching message", () => {
                    expect(() => throwExceptionWithMatchingMessage()).toThrow(regex="^.*MATCH_THIS.*$")
                })
            })
            describe("Tests notToThrow", () => {
                it("should pass because it is throwing a different exception", () => {
                    expect(() => throwDifferentException()).notToThrow(regex="^.*MATCH_THIS.*$")
                })
                it("should fail because it DOES throw an exception with the matching message", () => {
                    expect(() => throwExceptionWithMatchingMessage()).notToThrow(regex="^.*MATCH_THIS.*$")
                })
                it("should fail because it DOES throw an exception with the matching detail", () => {
                    expect(() => throwExceptionWithMatchingDetail()).notToThrow(regex="^.*MATCH_THIS.*$")
                })
                it("should fail because it DOES throw an exception that matches on both message and detail", () => {
                    expect(() => throwExceptionWithMatchingMessageAndDetail()).notToThrow(regex="^.*MATCH_THIS.*$")
                })
            })
        })
    }

    function throwExceptionWithMatchingMessage() {
        throw(message="MESSAGE_MATCH_THIS")
    }

    function throwExceptionWithMatchingDetail() {
        throw(detail="DETAIL_MATCH_THIS")
    }

    function throwExceptionWithMatchingMessageAndDetail() {
        throw(message="BOTH_MATCH_THIS", detail="BOTH_MATCH_THIS")
    }

    function throwDifferentException() {
        throw(message="DIFFERENT_MESSAGE", detail="DETAIL_MATCH_THIS")
    }
}
The results are:

https://i2.paste.pics/36545bd5ab233a462046e1bd02431eef.png

The first one should pass, that's fine. The latter three should not pass because I am throwing an exception matching the criteria: either the message, or the detail, or both match the regex.
The problem seems to be here: https://github.com/Ortus-Solutions/TestBox/blob/development/system/Assertion.cfc#L665-L667
Copy code
if (
    len( arguments.regex ) AND
    (
        !arrayLen( reMatchNoCase( arguments.regex, e.message ) )
        OR
        !arrayLen( reMatchNoCase( arguments.regex, e.detail ) )
    )
) {
    return this;
}
The problem here seems to stem from the assertion using the same regex for both message and detail which isn't that sensible for real world situations, as they're unlikely to be the same. So for this logic to actually work, the regex provided needs to cover BOTH message and detail simultaneously. For example: if I only want to match the message... then the detail match will fail. So: the assertion will determine that it's a different exception, so the assertion passes. Even though my regex is only supposed to be targeting the message. This is especially clear in situations where there isn't any detail... the logic still checks it, it doesn't match, so the condition passes and the exception is deemed to be different. On the other hand if I only want to match the detail with my regex... this logic fails for the same reason: the message won't match, so the exception is deemed to be different. Even though my desire was to only match the detail. I get that the assertion has no way of knowing which of message or detail I am actually wanting to match... this is the whole problem. It needs to know.
I think what you need here is along these lines:
Copy code
// if the type is different, the exception is different: pass
if ( len( arguments.type ) AND e.type neq arguments.type ) {
    return this;
}

if (! len( arguments.regex )) {
    // we have to infer it's the same exception as there's nothing else to go on
    fail( arguments.message );
}

if (arrayLen( reMatchNoCase( arguments.regex, e.message ) )) {
    // we found the regex in the message so we have to infer that's what they were checking. So it's the same exception
    fail( arguments.message );
}

if (arrayLen( reMatchNoCase( arguments.regex, e.detail ) )) {
    // we found the regex in the detail so we have to infer that's what they were checking. So it's the same exception
    fail( arguments.message );
}

// if we get to here there was no match on either message or detail, soit was a different exception: all good
return this;
(note: PLEASE don't put compound negative conditions in the same if statement. It makes the logic much harder to follow. Just check one thing at a time, and early exit) This approach won't work in the one situation where the regex was intended to match the
details
and it wouldn't have, but it does coincidentally match the message, so
details
are never checked. I think this is the minimum cost of using one regex for both. And it's very unlikely to be true. I think you might also want to add an extra optional param that is
detailsRegex
, and the behaviour of checking
regex
is still applied to
message
and
detail
, but
detailsRegex
can be used to only check the
details
. This works-around the shortfall in my logic above.
Ping @lmajano
@bdw429s given you're around, and reading my shite ATM... thoughts on this? NP if you can't be arsed.
b
@Adam Cameron What you said above makes sense. The logic works in a positive sense, but when you turn it negative, it has the wrong effect. I'd say it's worth a ticket and possibly a pull with your suggested workaround for Luis to chew on.
a
I'm hesitant to do a pull req cos this has really killed my productivity (and motivation) today already, and I'm pretty much over all the frickin' shite I have had to wade through with CFML crap this weekend. I can't be arsed grabbing testbox, working out what the workflow is, where the tests go, etc etc. I made a point of dropping the code and the tests here... pretty easy for someone who knows what they're doing to lift & shift those. I know this sounds a bit lazy, but I have already put a coupla hours of my weekend time into this. So not completely lazy 😕
b
I understand, Yak shaving for 3 hours to complete a 5 minute task is soul sucking
a
Thanks for not guilt-tripping me dude 😄
ticket raised, reproducing this thread: https://ortussolutions.atlassian.net/browse/TESTBOX-349
👍 1