Hello! Using Spock, I try to use Mockito's static mocking feature. Here's a simple example for mocking an SLF4J/Logback Logger:
given:
Logger loggerMock = Mock()
MockedStatic loggerFactoryMockitoMock = Mockito.mockStatic(LoggerFactory)
loggerFactoryMockitoMock.when(LoggerFactory.getLogger(ClassThatLogs)).thenReturn(loggerMock)
and:
def objectThatLogs = new ClassThatLogs()
when:
objectThatLogs.logSomethingWithInfo()
then:
1 * loggerMock.info('some expected message')
cleanup:
loggerFactoryMockitoMock.close()
This works, but not with @Unroll
tests, because from the second test on the class ClassThatLogs
still references the loggerMock
from the first test. To me it looks like as if the class ClassThatLogs
has not been reloaded. Do you how I could make it work?
(BTW: ClassThatLogs
has a private static final Logger LOG = LoggerFactory.getLogger(ClassThatLogs.class);
)
Logger
interface and provide a delegating logger, something similar to LoggerWrapper
, but with a non-final delegate and a setter for updating it. Then in each iteration you could set the new Spock mock as a delegate and then verify interactions on it without using detached mocks or shared fields. Sometimes doing a little bit of development work for a custom mock is better than pulling new dependencies into your project just for testing one thing. The mockStatic
would return the delegating wrapper, of course.
@Delegate
would be even better, I didn't really think it through and/or try. In my time zone I am about to start dinner. But it would be interesting to try later.
@sevenlist Maybe just refactor your ClassThatLogs
into
class ClassThatLogs {
private static final Logger LOG_INSTANCE = LoggerFactory.getLogger(ClassThatLogs.class);
private final Logger log;
ClassThatLogs() {
log = LOG_INSTANCE;
}
ClassThatLogs(Logger logger) {
log = logger;
}
}
And just use the constructor to inject your mocked logger and just use log
instead of LOG_INSTANCE
in the class.
If you don't want to expose it in the constructor you could also use groovys ability to access private fields.
class ClassThatLogs {
private static final Logger LOG_INSTANCE = LoggerFactory.getLogger(ClassThatLogs.class);
private Logger log = LOG_INSTANCE;
}
To modify the log
field from the spec.
IMHO this is much a much cleaner approach then the other variants.
https://twitter.com/spockframework/status/1377558567130107906
We have been advised of a copyright issue with the Spock name, to avoid future problems in this regard we have decided to rebrand. Henceforth, the Spock Framework will be renamed to GWT Framework (Given-When-Then).
Read the full announcement here https://spockframework.org/rebrand-announcement.html
I'm taking a class on Reactive Spring, and I've been able to use Spock no problem so far, but now there's a test using StepVerifier.withVirtualTime()
and it passes no matter what. Here's the whole test:
def "With virtual time"() {
given:
VirtualTimeScheduler.getOrSet()
def longFlux = Flux.interval(Duration.ofSeconds(1))
.take(3)
.log()
expect:
StepVerifier.withVirtualTime({ longFlux.log() })
.expectSubscription()
.thenAwait(Duration.ofSeconds(3))
.expectNext(0L, 1L, 2L)
.verifyComplete()
}
I also don't see the logs from the flux. Any ideas?
withVirtualTime
method, see https://www.baeldung.com/reactive-streams-step-verifier-test-publisher#3-testing-time-based-publishers as an example. Also you don't need VirtualTimeScheduler.getOrSet()
AFAIK.
class Testy extends Specification {
Helper helper = new Helper()
void "testy"() {
expect:
verify()
helper.verify()
}
void verify() {
assert 1 == 2
}
}
class Helper {
void verify() {
assert 1 == 3
}
}
class ClosureTest extends Specification {
Helper helper = new Helper()
void "testy"() {
expect:
verify.call()
}
}
class Helper {
Closure verify= { assert 1 == 2 }
}
runner {
parallel {
enabled true
defaultSpecificationExecutionMode ExecutionMode.SAME_THREAD
defaultExecutionMode ExecutionMode.SAME_THREAD
}
}
DESKTOP-CIDB3LU MINGW64 /e/projects/spock (master)
$ ./gradlew spock-core:test
Configure project :spock-core
[versioning] WARNING - the working copy has unstaged or uncommitted changes.
Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.8.3/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 1s
5 actionable tasks: 5 up-to-date
A build scan was not published as you have not authenticated with server 'ge.spockframework.org'.