How to unit test logging error with Spock framework in groovy

17,187

Solution 1

you could check for an invocation of error on the logger

@Grab(group='org.spockframework', module='spock-core', version='0.7-groovy-2.0')
@Grab(group='org.slf4j', module='slf4j-api', version='1.7.7')
@Grab(group='ch.qos.logback', module='logback-classic', version='1.1.2')

import org.slf4j.Logger

class MockLog extends spock.lang.Specification {

    public class Car {
        private Logger logger = org.slf4j.LoggerFactory.getLogger(Car.class);
        void startCar() {
            logger.error('car stopped working');
        }
    }

    def "mock log"() {
    given:
        def car = new Car()
        car.logger = Mock(Logger)
    when:
        car.startCar()
    then:
        1 * car.logger.error('car stopped working')
    }
}

edit: Full example https://github.com/christoph-frick/spock-test-logging

Solution 2

My Loggers are private static final so I cannot use solution mentioned above and rather not use Reflection.

If you are using Spring, you have acces to OutputCaptureRule.

@Rule
OutputCaptureRule outputCaptureRule = new OutputCaptureRule()

def test(){
outputCaptureRule.getAll().contains("<your test output>")
}
Share:
17,187
letter Q
Author by

letter Q

Updated on July 28, 2022

Comments

  • letter Q
    letter Q almost 2 years

    So I have a class that has a method that logs a message:

    class Car {
        private Logger logger = LoggerFactory.getLogger(Car.class);
    
    
        void startCar() {
            logger.error("car stopped working");
        }
    }
    

    How can I test that the error was logged using the spock testing framework?

    class CarTest extends Specification {
        def "test startCar"() {
            given:
            Car newCar = new Car();
    
            when:
            newCar.startCar();
    
            then:
            // HOW CAN I ASSERT THAT THE MESSAGE WAS LOGGED???
        }
    }
    
  • xetra11
    xetra11 almost 6 years
    groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: LOG . In your example you have access to car.logger - but how to this if the class is not within the Spock Testclass?
  • matt freake
    matt freake almost 6 years
    This example seems misleading. In a real life scenario, your logger would not be acccessible to your test code (class Car would not be inside the Specification)
  • cfrick
    cfrick almost 6 years
    @mattfreake I added a link to a full example to show, that this does not rely on the class to be inside the test. This was done to have a short example. If you mean something else, please elaborate. If you are against writing tests like this, then I am with you - but please blame OP and not the answer. If you need something so badly logged, that it merits writing a test, then provide a proper infrastructure for it. If the test is hairy or hard to write, it's not the tests fault but the fault of the SUT.
  • L. Dai
    L. Dai over 4 years
    How does your car instance have access to the logger? isn't it private?
  • cfrick
    cfrick over 4 years
    @L.Dai Groovy and/or Spock don't care. Also private on the jvm is not really private (can at least be accessed via reflection)
  • randy
    randy almost 4 years
    Not only does this work with Spring, but it also works with Travis CI. Previously, OutputCapture was the class I would use in Spring/Spock, but it was deprecated and would fail the CI build. I'm glad I searched for another answer and found this! Also, I feel I should mention we're using @Slf4j, and this is perfect. Thanks!
  • BeshEater
    BeshEater over 2 years
    You may also need to add dependency 'org.spockframework:spock-junit4:2.0-groovy-3.0' and also set the parameter follow="true" for your Console appender in the log4j2 configuration file (see stackoverflow.com/a/70350184/10592946)