问题
We combine Spock tests with Spring's @ContextConfiguration so we can build beans in a spring context and then use Spock for the actual testing. We would like to inject spock mocks into our spring beans. For Mockito there is an extension which allows you to do things like:
<mockito:mock id="accountService" class="org.kubek2k.account.DefaultAccountService" />
and then reference this mock to other spring beans. There seems to be no such extension for Spock. Then again building this is probably not too much effort if you know how to create Mocks outside of the Specification class. The only way of creating a Spock mock that I'm aware of is:
T Mock(Class<T> type)
in Specification. Is there some API in Spock to create Mocks when not being inside the Specification class, so I could create Spock mocks for a spring context?
回答1:
Creating mocks outside a spec class (and using them in another spec class) isn't currently possible. There's an open feature request for this. It shouldn't be too hard to implement this, but it would require some changes to spock-core. At the very least, there would need to be a way to manually attach a mock object to another spec instance. Probably, it would also make sense to move the user-facing mock creation API out of the MockingApi
base class.
You should be able to use Mockito with Spock, as long as you wrap all verification code contained in a then-block with a call to a helper method that returns true
(because Spock will consider it an assertion). Something like then: mockito { /* mockito verifications go here */ }
.
回答2:
Found a simple workaround to use Spock mock objects in a Spring application. Here my spring configuration to use a mock for the basar bean:
@Configuration @Profile("mocking")
class MockingContext {
@Bean Basar basar(){ new DelegatingBasar() }
}
class DelegatingBasar implements Basar {
@Delegate Basar delegate
}
And here a simple Spock specification which creates and use a mock:
@Autowired
Basar basar
Basar basarMock
def setup() {
basarMock = Mock(Basar)
basar.delegate = basarMock;
}
def "create a new seller"(User seller) {
given:
basarMock.findAllUsers() >> []
when:
go "/static/sellers.html"
waitFor { $("#newUser") }
$("#newUser").click()
waitFor { $("#basarNumber") }
$("#basarNumber").value(seller.basarNumber)
$("#name").value(seller.name)
$("#lastname").value(seller.lastname)
$("#email").value(seller.email)
$("#saveUser").click()
waitFor { $("#successfullCreated") }
then:
1 * basarMock.saveUser({ newUser ->
newUser.basarNumber == seller.basarNumber
newUser.name == seller.name
newUser.lastname == seller.lastname
newUser.email == seller.email
})
where:
seller << [ new User(basarNumber: "100", name: "Christian", lastname: "", email: ""),
new User(basarNumber: "ABC", name: "", lastname: "", email: "")]
}
回答3:
Creation of mocks outside of a specification class is possible since Spock 1.1 with DetachedMockFactory
and SpockMockFactoryBean
. spock
namespace for XML-based configuration is supported as well. You can find usage examples in the documentation.
A Spring test using Java-based configuration and DetachedMockFactory
looks like this:
@ContextConfiguration(classes = [TestConfig, DiceConfig])
class DiceSpec extends Specification {
@Autowired
private RandomNumberGenerator randomNumberGenerator
@Subject
@Autowired
private Dice dice
def "uses the random number generator to generate results"() {
when:
dice.roll()
then:
1 * randomNumberGenerator.randomInt(6)
}
static class TestConfig {
private final mockFactory = new DetachedMockFactory()
@Bean
RandomNumberGenerator randomNumberGenerator() {
mockFactory.Mock(RandomNumberGenerator)
}
}
}
@Configuration
class DiceConfig {
@Bean
Dice dice(RandomNumberGenerator randomNumberGenerator) {
new Dice(randomNumberGenerator)
}
}
And XML-based configuration would look like this:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:spock="http://www.spockframework.org/spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.spockframework.org/spring http://www.spockframework.org/spring/spock.xsd">
<spock:mock id="randomNumberGenerator" class="RandomNumberGenerator"/>
</beans>
Make sure to include the spock-spring
dependency:
testCompile group: 'org.spockframework', name: 'spock-spring', version: '1.1-groovy-2.4-rc-3'
回答4:
This is pretty straight-forward with "pure Spring":
def parentAppCtx = new StaticApplicationContext()
parentAppCtx.beanFactory.registerSingleton("myBean", Mock(MyClass))
parentAppCtx.refresh()
def appCtx = new ClassPathXmlApplicationContext("spring-config.xml", parentAppCtx)
Of course that assumes that you're properly decomposing your Spring configuration. (e.g., If you redefine "myBean"
in spring-config.xml
then the definition in spring-config.xml
will be used, since ApplicationContext
is essentially a Map and the most recent definition put in it will win.)
来源:https://stackoverflow.com/questions/16810646/how-to-create-spock-mocks-outside-of-a-specification-class