Skip to content

render exceptions in conditions as condition failure#49

Closed
Fuud wants to merge 2 commits into
spockframework:groovy-2.3from
Fuud:groovy-2.3
Closed

render exceptions in conditions as condition failure#49
Fuud wants to merge 2 commits into
spockframework:groovy-2.3from
Fuud:groovy-2.3

Conversation

@Fuud

@Fuud Fuud commented Sep 17, 2014

Copy link
Copy Markdown
Contributor

Idea

Spock has the best exception rendering I have ever see. For example, lets take a spec:

    def "responce code should starts with 200"(){
        when:
        def map = new HashMap<String, String>()
        map.put("responceCode", "404 NOT FOUND")
        then:
        map.get("responceCode").startsWith("200")
    }

It fails and has good rendering:

Condition not satisfied:

map.get("responceCode").startsWith("200")
|   |                   |
|   404 NOT FOUND       false
[responceCode:404 NOT FOUND]

    at org.spockframework.guice.JavaStubs.responce code should starts with 200(JavaStubs.groovy:98)

But how it will be rendered if map miss value for key "responceCode"? For example in this spec:

    def "responce code should starts with 200"(){
        when:
        def map = new HashMap<String, String>()
        map.put("ResponceCode", "404 NOT FOUND")
        then:
        map.get("responceCode").startsWith("200")
    }

just an exception:

java.lang.NullPointerException: Cannot invoke method startsWith() on null object
    at Test.responce code should starts with 200(Test.groovy:8)

we can understand that map does not contains entry for "responceCode" but we can not inspect what map really contains.

With my modifications it will become more friendly:

Condition not satisfied:

map.get("responceCode").startsWith("200")
|   |                   |
|   null                java.lang.NullPointerException: Cannot invoke method startsWith() on null object
[ResponceCode:404 NOT FOUND]

    at org.spockframework.guice.Test.responce code should starts with 200(Test.groovy:11)
Caused by: java.lang.NullPointerException: Cannot invoke method startsWith() on null object
    ... 1 more

So exceptions that occured during evaluating condition become part of rendering.

Implementation

Lets take for example this condition:

map.get("key") == "abc"

Before my changes it will be transformed to (I omit unnecessary parts)

SpockRuntime.verifyCondition(
                valueRecorder.reset(),
                "map.get(\"key\") == \"abc\"",
                8,
                9,
                null,
                valueRecorder.record(
                        6,
                        Boolean.valueOf(
                                ScriptBytecodeAdapter.compareEqual(
                                        valueRecorder.record(4,
                                                ScriptBytecodeAdapter.invokeMethodN(
                                                        Test.class,
                                                        valueRecorder.record(0, map),
                                                        (String)valueRecorder.record(1, "get"),
                                                        [valueRecorder.record(2, "key") ]
                                                )
                                        ),
                                        valueRecorder.record(5, "abc")
                                )
                        )
                )
        );

We need to:

  1. handle exception, so surround this expression with try-catch.
  2. to understand number of value that was not recorded because of exception, so lets remember value number before recording value (and before evaluating expression for this value).
try {
    SpockRuntime.verifyCondition(
            $spock_valueRecorder.reset(), 
            "map.get(\"key\") == \"abc\"", 
            14,
            7,
            null,
            $spock_valueRecorder.record(
                    $spock_valueRecorder.startRecordingValue(6),
                    Boolean.valueOf(
                            ScriptBytecodeAdapter.compareEqual(
                                    $spock_valueRecorder.record(
                                            $spock_valueRecorder.startRecordingValue(4),
                                            ScriptBytecodeAdapter.invokeMethodN(
                                                    ExceptionsInConditionsSpec.class,
                                                    $spock_valueRecorder.record(
                                                            $spock_valueRecorder.startRecordingValue(0),
                                                            map
                                                    ),
                                                    (String) ShortTypeHandling.castToString(
                                                            $spock_valueRecorder.record(
                                                                    $spock_valueRecorder.startRecordingValue(1),
                                                                    "get")
                                                    ),
                                                    [$spock_valueRecorder.record(
                                                            $spock_valueRecorder.startRecordingValue(2),
                                                            "key"
                                                    )]
                                            )
                                    ),
                                    $spock_valueRecorder.record(
                                            $spock_valueRecorder.startRecordingValue(5),
                                            "abc")
                            )
                    )
            )
    );
}
catch (Throwable throwable) {
    SpockRuntime.conditionFailedWithException($spock_valueRecorder, "map.get(\"key\") == \"abc\"", 14, 7, null, throwable);
}

ValueRecorder,startRecording method:

  private final Stack<Integer> startedRecordings = new Stack<Integer>();

  public int startRecordingValue(int index){
    startedRecordings.push(index);
    return index;
  }

So we add value number to stack before evaluating expression for this value. And remove value number from top of stack when value is written to ValueRecording. And when exception is thrown we can learn where exception is thrown by peeking value from top of stack.

@pniederw

Copy link
Copy Markdown
Contributor

Thanks a lot for your pull request. I like the idea, and will review over the weekend.

@Fuud

Fuud commented Oct 6, 2014

Copy link
Copy Markdown
Contributor Author

@pniederw any review?

@jbaruch

jbaruch commented Oct 26, 2014

Copy link
Copy Markdown

👍

@pniederw

pniederw commented Nov 3, 2014

Copy link
Copy Markdown
Contributor

I've started work on this pull request, and should have it integrated soon. Thanks for your patience.

@PascalSchumacher

Copy link
Copy Markdown

very nice enhancement 👍

@Fuud

Fuud commented Mar 11, 2015

Copy link
Copy Markdown
Contributor Author

@pniederw Any progress?

@leonard84

Copy link
Copy Markdown
Member

👍

@Fuud

Fuud commented Apr 21, 2015

Copy link
Copy Markdown
Contributor Author

@pniederw Any progress?

@jirutka

jirutka commented Apr 21, 2015

Copy link
Copy Markdown

👍

1 similar comment
@stavytskyi

Copy link
Copy Markdown

👍

@Fuud

Fuud commented May 13, 2015

Copy link
Copy Markdown
Contributor Author

@pniederw Why this pull request was not merged yet? Is anything wrong with code?

@PascalSchumacher

Copy link
Copy Markdown

@robfletcher Maybe you can have a look a this pull request (as a spock user this seems like a very nice feature)?

@robfletcher

Copy link
Copy Markdown
Contributor

Yeah, this looks excellent. I'm going to merge it if I don't hear a good reason why I shouldn't in the next few hours.

@Fuud

Fuud commented Jun 22, 2015

Copy link
Copy Markdown
Contributor Author

@robfletcher Did you found "good reason" not to merge?

@Fuud

Fuud commented Sep 3, 2015

Copy link
Copy Markdown
Contributor Author

Open Question: What to do with invocation of void methods and assignments? Should such constructions became conditions because them can throw an exception.

@PascalSchumacher

Copy link
Copy Markdown

Maybe this can be merged before the next release?

@szpak

szpak commented Feb 20, 2016

Copy link
Copy Markdown
Member

Yeah, this looks excellent. I'm going to merge it if I don't hear a good reason why I shouldn't in the next few hours.

@robfletcher Time flies. Any chance to get it merged in the foreseeable future?

@dpost

dpost commented May 5, 2016

Copy link
Copy Markdown

any update on this?

robfletcher added a commit that referenced this pull request May 27, 2016
…n-failure__over_spock_master

render exceptions in conditions as condition failure (copy of #49)
@robfletcher

Copy link
Copy Markdown
Contributor

Superseded by #549 which is now merged

@robfletcher robfletcher modified the milestone: 1.1-rc-1 Aug 24, 2016
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.

10 participants