wow!!!, thank you both! I will try those. Another question I am curious
about is that "is it possible to get the shared fields from a spec using
the same approach?"
I have a spec that extends a BaseSpec which has a @Share field named
*testFailed *
BaseSpec extends GebReportingSpec
{
@Shared boolean *testFailed *= false
}
MySpec extends BaseSpec
{
if (someErrorHappens){
*testFailed*=true
}
On Tuesday, January 5, 2021 at 5:54:28 AM UTC-5 [email protected] wrote:
> I have to admit that I sometimes wonder if you ever do any work and not
> just answer people's questions online when I see answers this detailed from
> you, Alexander. :)
>
> On Tue, Jan 5, 2021 at 5:10 AM Alexander Kriegisch <[email protected]>
> wrote:
>
>> Okay, here is a slightly improved version of what Marcin suggested. It
>> only attaches run listener + method interceptor to Spock specs which are
>> actual Geb specs, reducing runtime overhead. It also contains a sample
>> method for taking a screenshot, just to be able to run my sample test.
>> ------------------------------
>>
>> package de.scrum_master.testing.extension
>>
>> import geb.spock.GebSpec
>> import org.openqa.selenium.OutputType
>> import org.openqa.selenium.TakesScreenshot
>> import org.openqa.selenium.WebDriver
>> import org.spockframework.runtime.AbstractRunListener
>> import org.spockframework.runtime.extension.AbstractGlobalExtension
>> import org.spockframework.runtime.extension.IMethodInterceptor
>> import org.spockframework.runtime.extension.IMethodInvocation
>> import org.spockframework.runtime.model.ErrorInfo
>> import org.spockframework.runtime.model.SpecInfo
>>
>> import java.nio.file.Files
>>
>> /**
>> * See https://groups.google.com/g/geb-user/c/vrNZFaRyDQQ
>> */
>> class TestFailureScreenshotExtension extends AbstractGlobalExtension {
>> @Override
>> void visitSpec(SpecInfo spec) {
>> if (GebSpec.isAssignableFrom(spec.reflection)) {
>> def reporter = new ScreenshotOnErrorReporter()
>> spec.addListener(reporter)
>> spec.allFeatures*.addIterationInterceptor(reporter)
>> }
>> }
>>
>> static class ScreenshotOnErrorReporter extends AbstractRunListener
>> implements IMethodInterceptor {
>> GebSpec gebSpec
>>
>> @Override
>> void intercept(IMethodInvocation invocation) throws Throwable {
>> gebSpec = invocation.instance
>> try {
>> invocation.proceed()
>> } finally {
>> gebSpec = null
>> }
>> }
>>
>> @Override
>> void error(ErrorInfo error) {
>> def driver = gebSpec.driver
>> driver.manage().window().maximize()
>> takeScreenshot(driver)
>> }
>>
>> void takeScreenshot(WebDriver driver) {
>> // Not all WebDrivers can take screenshots, e.g. HtmlUnitDriver
>> if (!(driver instanceof TakesScreenshot)) {
>> println "Driver $driver is incapable of taking screenshots"
>> return
>> }
>> def screenshotTempFile = (driver as
>> TakesScreenshot).getScreenshotAs(OutputType.FILE)
>> def targetDir = new File(gebSpec.browser.reportGroupDir,
>> gebSpec.class.name.replaceAll("[.]", "/"))
>> // TODO: generate nicer name for screenshot file, this is just an
>> example
>> def screenshotFile = new File(targetDir, System.nanoTime() + ".png")
>> targetDir.mkdirs()
>> Files.copy(screenshotTempFile.toPath(), screenshotFile.toPath())
>> println "Screenshot saved as $screenshotFile"
>> }
>> }
>>
>> }
>>
>> ------------------------------
>>
>> package de.scrum_master.testing.extension
>>
>> import geb.spock.GebSpec
>> import spock.lang.Ignore
>> import spock.lang.Unroll
>>
>> //@Ignore("test fails on purpose; activate if you want to see screenshots
>> taken via global extension")
>> class TestFailureGebReportingTest extends GebSpec {
>> static url = this.getResource("/simple-form-page.html").toString()
>>
>> def "failing normal feature"() {
>> given:
>> go url
>>
>> expect:
>> 0 == 1
>> }
>>
>> def "passing normal feature"() {
>> given:
>> go url
>>
>> expect:
>> 0 == 0
>> }
>>
>> def "parametrised feature"() {
>> given:
>> go url
>>
>> expect:
>> a == b
>>
>> where:
>> a << [2, 4, 6]
>> b << [3, 5, 6]
>> }
>>
>> @Unroll
>> def "unrolled feature with #a/#b"() {
>> given:
>> go url
>>
>> expect:
>> a == b
>>
>> where:
>> a << [6, 8, 0]
>> b << [7, 9, 0]
>> }
>> }
>>
>> ------------------------------
>>
>> For normal Spock specs there would be no extra log output. For Geb specs
>> you would see something like:
>>
>> Screenshot saved as
>> target\geb-reports\de\scrum_master\testing\extension\TestFailureGebReportingTest\70413528792100.png
>>
>> When running Geb specs using a driver not implementing TakesScreenshot
>> (e.g. HtmlUnitDriver), you would rather see:
>>
>> Driver org.openqa.selenium.htmlunit.HtmlUnitDriver@4893b344 is incapable of
>> taking screenshots
>>
>> --
>> Alexander Kriegisch
>> https://scrum-master.de
>>
>>
>>
>> Alexander Kriegisch schrieb am 05.01.2021 09:28 (GMT +07:00):
>>
>> > I forgot to mention: A SpecInfo is just meta information, the best you
>> > can get from there is the spec class in order wo rewrite the 'if' as
>> >
>> > if (GebSpec.isAssignableFrom(iteration.feature.spec.reflection))
>> >
>> > But then you are at a dead end if you need the actual specification
>> > instance because you cannot get it, so there is no way to access its
>> > fields or methods like 'getDriver()' from there either. This is why
>> > Marcin's approach to use an IMethodInterceptor is exactly right. From
>> > the 'IMethodInvocation invocation' parameter you can easily get the
>> > instance, while from an 'IterationInfo iteration' you cannot. So you
>> > see, my quick & dirty hack without running the code is fundamentally
>> > wrong on several levels.
>> >
>> >
>> > Alexander Kriegisch schrieb am 05.01.2021 08:41 (GMT +07:00):
>> >
>> >>>> I get this error
>> >>>>
>> >>>> Cannot cast object
>> 'org.spockframework.runtime.model.SpecInfo@73f38e62'
>> >>>> with class 'org.spockframework.runtime.model.SpecInfo' to class
>> >>>> 'geb.spock.GebSpec'
>> >>
>> >> The simple answer is: I was sloppy and wish too apologise for it. This
>> >> is the perfect example why I keep telling the teams I coach: "If you
>> are
>> >> sloppy, you either have to do it twice or someone else has to clean up
>> >> after you." The latter was the case: Marcin cleaned up after me and I
>> am
>> >> feeling embarrassed. So in order to learn a lesson from this, let me
>> >> describe what happened:
>> >>
>> >> -- I added the 'afterIteration' method to my existing extension for
>> >> which also a test using it exists.
>> >> -- The existing test is a simple Spock test, not a Geb test.
>> >> -- Looking at 'if (iteration.feature.spec instanceof GebSpec)', first
>> >> of all there is a bug because despite the suggestive property name
>> >> 'spec' (which in reality is a method name 'getSpec') the return
>> >> value is an instance of SpecInfo, not of Specification. Maybe the
>> >> getter should have been named 'getSpecInfo', but anyway it was my
>> >> mistake. I did not actually run or debug is, just quickly hacked it
>> >> into my IDE using code completion. Secondly, even if this piece of
>> >> code would have been correct, I do not really have a GebSpec
>> >> running this extension which would make the code enter the 'if'
>> >> block. Double mess-up!
>> >> -- I actually never planned to run the code because I do not have
>> >> access to your 'takeScreenshot' method, simply because you did not
>> >> provide it in your original question. So for me it was pseudo code
>> >> and I was too lazy to create a dummy 'takeScreenshot' method just
>> >> logging something or whatever.
>> >>
>> >> I wanted to save time and just help getting you started. My main
>> >> objective was to convince you to try GebReportingSpec anyway, like I
>> >> said. But the sample code was just sloppy. Thanks to Marcin for
>> cleaning
>> >> up my mess!
>> >>
>> >>
>> >> Marcin Erdmann schrieb am 05.01.2021 04:10 (GMT +07:00):
>> >>>
>> >>> Unfortunately the code provided by Alexander is not quite right. You
>> >>> cannot obtain an instance of the spec in an AbstractRunListener
>> because
>> >>> it's not available on IterationInfo. You will need to combine an
>> >>> AbstractRunListener with an IMethodInterceptor, like
>> >>> geb.spock.OnFailureReporter which ships with Geb does. The adapted
>> example
>> >>> from Alexander would look like this:
>> >>>
>> >>>
>> >>> import geb.spock.GebSpec
>> >>> import org.spockframework.runtime.AbstractRunListener
>> >>> import org.spockframework.runtime.extension.AbstractGlobalExtension
>> >>> import org.spockframework.runtime.extension.IMethodInterceptor
>> >>> import org.spockframework.runtime.extension.IMethodInvocation
>> >>> import org.spockframework.runtime.model.ErrorInfo
>> >>> import org.spockframework.runtime.model.SpecInfo
>> >>>
>> >>> class TestResultExtension extends AbstractGlobalExtension {
>> >>> @Override
>> >>> void visitSpec(SpecInfo spec) {
>> >>> def reporter = new ErrorReporter()
>> >>> spec.addListener(reporter)
>> >>> spec.allFeatures*.addIterationInterceptor(reporter)
>> >>> }
>> >>>
>> >>> static class ErrorReporter extends AbstractRunListener implements
>> >>> IMethodInterceptor {
>> >>>
>> >>> GebSpec specInstance
>> >>>
>> >>> @Override
>> >>> void error(ErrorInfo error) {
>> >>> if (specInstance) {
>> >>> def driver = specInstance.driver
>> >>> driver.manage().window().maximize()
>> >>> takeScreenshot(driver)
>> >>> }
>> >>> }
>> >>>
>> >>> @Override
>> >>> void intercept(IMethodInvocation invocation) throws Throwable {
>> >>> def specInstance = invocation.instance
>> >>> if (specInstance instanceof GebSpec) {
>> >>> this.specInstance = specInstance
>> >>> }
>> >>>
>> >>> try {
>> >>> invocation.proceed()
>> >>> } finally {
>> >>> this.specInstance = null
>> >>> }
>> >>> }
>> >>> }
>> >>> }
>> >>>
>> >>>
>> >>> On Mon, Jan 4, 2021 at 4:20 PM GebUser <[email protected]
>> >>> <mailto:[email protected]> > wrote:
>> >>>
>> >>>> where is this "driver" coming from?
>> >>>> def driver = (iteration.feature.spec as GebSpec).driver
>> >>>>
>> >>>>
>> >>>> I get this error
>> >>>>
>> >>>> Cannot cast object
>> 'org.spockframework.runtime.model.SpecInfo@73f38e62'
>> >>>> with class 'org.spockframework.runtime.model.SpecInfo' to class
>> >>>> 'geb.spock.GebSpec'
>> >>>>
>> >>>>
>> >>>> On Monday, December 21, 2020 at 8:23:55 PM UTC-5
>> [email protected]
>> >>>> <mailto:[email protected]> wrote:
>> >>>>
>> >>>>> Hi.
>> >>>>>
>> >>>>> I hope it is okay, even though I am not Marcin. ;-)
>> >>>>>
>> >>>>> It looks like you you found the sample code in my SO answer here:
>> >>>>> https://stackoverflow.com/a/50679606/1082681
>> >>>>>
>> >>>>> Before we continue, making a possibly simple thing complicated: Are
>> you
>> >>>>> aware of the fact that if your specification extends
>> GebReportingSpec,
>> >>>>> it will by default have a screenshot reporter, so by a statement
>> like
>> >>>>> "report 'my screenshot'" you can just take a screenshot and it will
>> >>>>> automatically take a screenshot if the test fails?
>> >>>>>
>> >>>>> See https://gebish.org/manual/current/#testing-reporting
>> >>>>> and also search
>> >>>>> the Book of Geb for "reporting" or "reporter". Just lately someone
>> here
>> >>>>> in this group posted about how to write a custom reporter in order
>> to
>> >>>>> use AShot for full page screenshot (if the page is bigger than the
>> >>>>> viewport or even bigger than the screen size).
>> >>>>>
>> >>>>> Now, having the basic information out of the way and assuming for a
>> >>>>> minute that the default screenshot reporter or even a custom
>> reporter
>> >>>>> are inadequate for you (which I don't believe, BTW), here is how
>> from
>> >>>>> your global Spock extension's RunListener you could in principle
>> access
>> >>>>> browser and driver: First you need to get access to the
>> specification
>> >>>>> instance, then check if it is really a Geb specification (because
>> not
>> >>>>> every Spock spec is a Geb spec) and if so, you can use its methods
>> in
>> >>>>> order to access browser and driver:
>> >>>>>
>> >>>>>
>> ------------------------------------------------------------------------
>> >>>>>
>> >>>>> package de.scrum_master.testing.extension
>> >>>>>
>> >>>>> import geb.spock.GebSpec
>> >>>>> import org.spockframework.runtime.AbstractRunListener
>> >>>>> import org.spockframework.runtime.extension.AbstractGlobalExtension
>> >>>>> import org.spockframework.runtime.model.ErrorInfo
>> >>>>> import org.spockframework.runtime.model.IterationInfo
>> >>>>> import org.spockframework.runtime.model.SpecInfo
>> >>>>> /**
>> >>>>> * See https://stackoverflow.com/a/50679606/1082681
>> >>>>> */
>> >>>>> class TestResultExtension extends AbstractGlobalExtension {
>> >>>>> @Override
>> >>>>> void visitSpec(SpecInfo spec) {
>> >>>>> spec.addListener(new ErrorListener())
>> >>>>> }
>> >>>>>
>> >>>>> static class ErrorListener extends AbstractRunListener {
>> >>>>> ErrorInfo errorInfo
>> >>>>>
>> >>>>> @Override
>> >>>>> void beforeIteration(IterationInfo iteration) {
>> >>>>> errorInfo = null
>> >>>>> }
>> >>>>>
>> >>>>> @Override
>> >>>>> void error(ErrorInfo error) {
>> >>>>> errorInfo = error
>> >>>>> }
>> >>>>>
>> >>>>> @Override
>> >>>>> void afterIteration(IterationInfo iteration) {
>> >>>>> if (iteration.feature.spec instanceof GebSpec) {
>> >>>>> def driver = (iteration.feature.spec as GebSpec).driver
>> >>>>> driver.manage().window().maximize()
>> >>>>> takeScreenshot(driver)
>> >>>>> }
>> >>>>> }
>> >>>>>
>> >>>>> }
>> >>>>> }
>> >>>>>
>> >>>>>
>> ------------------------------------------------------------------------
>> >>>>>
>> >>>>> BTW, you could also check if the spec is an instance of
>> GebReportingSpec
>> >>>>> and directly use the reporting feature there. But really, what you
>> >>>>> probably want is just use the plain vanilla GebReportingSpec. Maybe,
>> >>>>> maybe you want to use a custom reporter in combination with that.
>> >>>>>
>> >>>>> Friendly regards
>> >>>>> --
>> >>>>> Alexander Kriegisch
>> >>>>> https://scrum-master.de
>> >>>>>
>> >>>>>
>> >>>>> GebUser schrieb am 22.12.2020 04:56 (GMT +07:00):
>> >>>>> > hi Marcin,
>> >>>>> > I am using a class test status tracker class that extends
>> >>>>> > AbstractGlobalExtension.
>> >>>>> >
>> >>>>> >
>> >>>>> > When an error occurs, I need to take a screenshot. Can you please
>> tell
>> >>>>> how
>> >>>>> > I can access the browser driver here?
>> >>>>> >
>> >>>>> >
>> >>>>> > class TestResultExtension extends
>> >>>>> > AbstractGlobalExtension
>> >>>>> > {
>> >>>>> > @Override
>> >>>>> > void visitSpec(SpecInfo spec) {
>> >>>>> > spec.addListener(new ErrorListener())
>> >>>>> > }
>> >>>>> >
>> >>>>> > static class ErrorListener extends AbstractRunListener {
>> >>>>> > ErrorInfo errorInfo
>> >>>>> > # Browser browser
>> >>>>> >
>> >>>>> > @Override
>> >>>>> > void beforeIteration(IterationInfo iteration) {
>> >>>>> > errorInfo = null
>> >>>>> > }
>> >>>>> >
>> >>>>> >
>> >>>>> > void afterIteration(IterationInfo iteration){
>> >>>>> > if (errorInfo!=null){
>> >>>>> > driver.manage().window().maximize()
>> >>>>> >
>> >>>>> > takeScreenshot(driver)
>> >>>>> > }
>> >>>>> > }
>> >>>>
>> >>>> --
>> >>>> You received this message because you are subscribed to the Google
>> Groups
>> >>>> "Geb User Mailing List" group.
>> >>>> To unsubscribe from this group and stop receiving emails from it,
>> send an
>> >>>> email to [email protected]
>> >>>> <mailto:[email protected]> .
>> >>>> To view this discussion on the web visit
>> >>>>
>> https://groups.google.com/d/msgid/geb-user/bc7bbb02-6f6c-46f0-bd0f-45985f9eae15n%40googlegroups.com
>> >>>> <
>> https://groups.google.com/d/msgid/geb-user/bc7bbb02-6f6c-46f0-bd0f-45985f9eae15n%40googlegroups.com?utm_medium=email&utm_source=footer
>> >
>> >>>> .
>> >>>
>> >>> --
>> >>> You received this message because you are subscribed to the Google
>> Groups
>> >>> "Geb User Mailing List" group.
>> >>> To unsubscribe from this group and stop receiving emails from it,
>> send an
>> >>> email to [email protected]
>> >>> <mailto:[email protected]> .
>> >>> To view this discussion on the web visit
>> >>>
>> https://groups.google.com/d/msgid/geb-user/CA%2B52dQR7c26w6HwkE3boaA6_BSV7NqypUd_62juyH%3DA6GewT%3Dg%40mail.gmail.com
>> >>> <
>> https://groups.google.com/d/msgid/geb-user/CA%2B52dQR7c26w6HwkE3boaA6_BSV7NqypUd_62juyH%3DA6GewT%3Dg%40mail.gmail.com?utm_medium=email&utm_source=footer
>> >
>> >>> .
>> >>
>> >> --
>> >> You received this message because you are subscribed to the Google
>> Groups "Geb
>> >> User Mailing List" group.
>> >> To unsubscribe from this group and stop receiving emails from it, send
>> an
>> >> email
>> >> to [email protected].
>> >> To view this discussion on the web visit
>> >>
>> https://groups.google.com/d/msgid/geb-user/20210105014110.532C33380703%40dd39516.kasserver.com
>> .
>> >>
>> >>
>> >
>> > --
>> > You received this message because you are subscribed to the Google
>> Groups "Geb
>> > User Mailing List" group.
>> > To unsubscribe from this group and stop receiving emails from it, send
>> an email
>> > to [email protected].
>> > To view this discussion on the web visit
>> >
>> https://groups.google.com/d/msgid/geb-user/20210105022816.818683380889%40dd39516.kasserver.com
>> .
>> >
>> >
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "Geb User Mailing List" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected].
>>
> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/geb-user/20210105051006.3FB8133804E6%40dd39516.kasserver.com
>>
>> <https://groups.google.com/d/msgid/geb-user/20210105051006.3FB8133804E6%40dd39516.kasserver.com?utm_medium=email&utm_source=footer>
>> .
>>
>
--
You received this message because you are subscribed to the Google Groups "Geb
User Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/geb-user/8ddd334b-8cdc-4e6c-8992-8f62785b10cdn%40googlegroups.com.