someone, please, explain why domain class behaves different when is mocked with new MockFor(..)?
I use grails 2.4.1.
I have create domain class GTest:
class GTest {
static constraints = {
}
String amethod() {
return "GTest.amethod"
}
}
And a groovy class DTest:
class DTest {
String amethod() {
return "DTest.amethod"
}
}
And here a groovy test:
class GroovyJUnitTest {
#Test
void testDTestAmethod() {
def mock = new MockFor(DTest)
mock.demand.amethod {
return "Mock DTest"
}
mock.use {
def dtest = new DTest()
// Here dtest.metaClass=groovy.mock.interceptor.MockProxyMetaClass#...
def ret = dtest.amethod()
// This assertation successes
assertEquals "Mock DTest", ret
}
}
#Test
void testGTestAmethod() {
def mock = new MockFor(GTest)
mock.demand.amethod {
return "Mock GTest"
}
mock.use {
def gtest = new GTest()
// Here dtest.metaClass=groovy.lang.ExpandoMetaClass#...
def ret = gtest.amethod()
// This assertation fails
assertEquals "Mock GTest", ret
}
}
}
So, this question is how should Domain class be mocked programmatically?
Thank you for explanation.
You should annotation your test with grails.test.mixin.TestFor.
#grails.test.mixin.TestFor(MyDomainClass)
class MyDomainClassTest {
// ...
}
See http://grails.org/doc/latest/guide/testing.html#unitTestingDomains for more details.
Related
Given the following, how do I mock processMessage() using Spock, so that I can check that processBulkMessage() calls processMessage() n times, where n is the number of messages within a BulkMessage?
class BulkMessage {
List messages
}
class MyService {
def processBulkMessage(BulkMessage msg) {
msg.messages.each {subMsg->
processMessage(subMsg)
}
}
def processMessage(Message message) {
}
}
You can use spies and partial mocks (requires Spock 0.7 or newer).
After creating a spy, you can listen in on the conversation between the caller and the real object underlying the spy:
def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
subscriber.receive(_) >> "ok"
Sometimes, it is desirable to both execute some code and delegate to the real method:
subscriber.receive(_) >> { String message -> callRealMethod(); message.size() > 3 ? "ok" : "fail" }
In my opinion this is not a well designed solution. Tests and design walk hand in hand - I recommend this talk to investigate it better. If there's a need to check if other method was invoked on an object being under test it seems it should be moved to other object with different responsibility.
Here's how I would do it. I know how visibility works in groovy so mind the comments.
#Grab('org.spockframework:spock-core:0.7-groovy-2.0')
#Grab('cglib:cglib-nodep:3.1')
import spock.lang.*
class MessageServiceSpec extends Specification {
def 'test'() {
given:
def service = new MessageService()
def sender = GroovyMock(MessageSender)
and:
service.sender = sender
when:
service.sendMessages(['1','2','3'])
then:
3 * sender.sendMessage(_)
}
}
class MessageSender { //package access - low level
def sendMessage(String message) {
//whatever
}
}
class MessageService {
MessageSender sender //package access - low level
def sendMessages(Iterable<String> messages) {
messages.each { m -> sender.sendMessage(m) }
}
}
It does not use Spock built-in Mocking API (not sure how to partially mock an object), but this should do the trick:
class FooSpec extends Specification {
void "Test message processing"() {
given: "A Bulk Message"
BulkMessage bulk = new BulkMessage(messages: ['a', 'b', 'c'])
when: "Service is called"
def processMessageCount = 0
MyService.metaClass.processMessage { message -> processMessageCount++ }
def service = new MyService()
service.processBulkMessage(bulk)
then: "Each message is processed separately"
processMessageCount == bulk.messages.size()
}
}
For Java Spring folks testing in Spock:
constructorArgs is the way to go, but use constructor injection. Spy() will not let you set autowired fields directly.
// **Java Spring**
class A {
private ARepository aRepository;
#Autowire
public A(aRepository aRepository){
this.aRepository = aRepository;
}
public String getOne(String id) {
tryStubMe(id) // STUBBED. WILL RETURN "XXX"
...
}
public String tryStubMe(String id) {
return aRepository.findOne(id)
}
public void tryStubVoid(String id) {
aRepository.findOne(id)
}
}
// **Groovy Spock**
class ATest extends Specification {
def 'lets stub that sucker' {
setup:
ARepository aRepository = Mock()
A a = Spy(A, constructorArgs: [aRepository])
when:
a.getOne()
then:
// Stub tryStubMe() on a spy
// Make it return "XXX"
// Verify it was called once
1 * a.tryStubMe("1") >> "XXX"
}
}
Spock - stubbing void method on Spy object
// **Groovy Spock**
class ATest extends Specification {
def 'lets stub that sucker' {
setup:
ARepository aRepository = Mock()
A a = Spy(A, constructorArgs: [aRepository]) {
1 * tryStubVoid(_) >> {}
}
when:
...
then:
...
}
}
I'm rendering a GSP in a Spock unit test with Grails 2.4.4. The GSP has a number of custom taglibs, and many of them call external services. The problem is that I can't inject a stubbed service into those taglibs, and when I call the service, it's always null. Here's what I'm trying to get simplest case working:
#TestMixin(GroovyPageUnitTestMixin)
#Mock(FooTagLib)
class MyGspSpec extends Specification {
def setup() {
def barStub = Stub(BarService)
def tagLibStub = GroovyStub(FooTagLib, global:true) {
}
tagLibStub.barService = barStub
}
def 'test method'() {
when: String result = render('myView', model:[:])
then: assert result != null
}
}
Taglib:
class FooTagLib {
static String namespace = "baz"
BarService barService
Closure<StreamCharBuffer> foo = { GroovyPageAttibutess attrs ->
out << barService.something()
}
}
_foo.gsp contents:
<baz:foo/>
I've also tried this:
FooTagLib.metaClass.barService = Stub(BarService) //also tried GroovyStub
I even tried putting a getter on the taglib and stubbing that, but it didn't work either:
In setup:
def barService = Stub(BarService)
GroovyStub(FooTagLib, global:true) {
getBarService() >> barService
}
In taglib:
BarService barService
BarService getBarService() { return barService }
//....
out << getBarService().something()
Finally, the only thing that worked (with the getter) is this:
FooTagLib.metaClass.getBarService = { -> return Stub(BarService) }
But this seems like a really bad hack.
I'm not sure how I can inject a stubbed version of it into the taglib. In my mind at least one of these should work, but obviously I'm doing something wrong.
This is how I would pass unit specs for the tagLib:
#TestFor(FooTagLib)
class FooTagLibSpec extends Specification {
def setup() {
// tagLib is available by default of above annotation is used
tagLib.barService = Mock(BarService) {
// Mock the service and stub the response
1 * serviceMethod() >> { "Hello World Returns" }
}
}
void "test something"() {
expect: 'applying template would result in mocked response'
applyTemplate('<baz:foo/>') == "Hello World Returns"
}
void "test taglib method call"() {
expect:
tagLib.foo() == "Hello World Returns"
}
}
class FooTagLib {
static defaultEncodeAs = [taglib:'html']
static String namespace = "baz"
BarService barService
Closure<StreamCharBuffer> foo = { attrs ->
out << barService.serviceMethod()
}
}
class BarService {
def serviceMethod() { "Hello World" }
}
I hope I was able to meet the checkpoints you were looking for in your tests. You can also see spec is able to mock BarService without issues. Give a shout if you need for info.
I am updating unit tests in a Grails project. We were originally using version 1.3.9 and now we are updating to version 2.3.9. I am using Spock.
I keep getting this error:
results:
junit.framework.AssertionFailedError: Condition not satisfied:
controller.edit() == [filterCategoryInstance: filterCategoryInstance]
| | | |
| null false John
com.xxxxxx.xxxxx.FilterCategoryController#20574000
Here is the controller code:
#Secured(["hasAnyRole('CM_ADMIN')"])
def edit() {
def filterCategoryInstance = FilterCategory.get(params.id)
if (!filterCategoryInstance) {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'dpFilterCategory.label', default: 'FilterCategory'), params.id])}"
redirect(action: "list")
}
else {
return [filterCategoryInstance: filterCategoryInstance]
}
}
and here is the test code:
#Mock([FilterCategory, FilterCategoryTag])
#TestFor(FilterCategoryController)
#TestMixin(DomainClassUnitTestMixin)
class FilterCategoryControllerSpec extends ExtendedControllerSpec {
def 'edit action: existing FilterCategory'() {
setup:
mockI18N(FilterCategoryController)
params.id = filterCategoryInstance.id
expect:
controller.edit() == [filterCategoryInstance: filterCategoryInstance]
where:
tag = new FilterCategoryTag(name: 'tag1')
filterCategoryInstance = new FilterCategory(name: "John",
submissionText:"John", sortOrder:0, 'filterCategoryTags': [tag])
}
And here is the ExtendedControllerSpec code. I hope I have included enough code:
I have looked at the following web pages for guidance:
#Mixin(MetaClassMixin)
class ExtendedControllerSpec extends Specification {
def props
protected void setup() {
//super.setup()
props = new Properties()
File file = new File("grails-app/i18n/messages.properties")
if (file.exists()) {
def stream = new FileInputStream(file)
props.load stream
stream.close()
}
mockI18N(controller)
}
def mockI18N = { controller ->
controller.metaClass.message = { Map map ->
if (!map.code)
return ""
if (map.args) {
def formatter = new MessageFormat("")
if (props.getProperty(map.code)) {
formatter.applyPattern props.getProperty(map.code)
}
return formatter.format(map.args.toArray())
} else {
if (props && props.hasProperty(map.code)) {
return props.getProperty(map.code)
} else {
return map.code
}
}
}
}
/**
* add dynamic methods in test setup.
*/
protected void addDynamicMethods() {
registerMetaClass(String)
String.metaClass.mixin StringUtils
}
protected GrailsUser mockGrailsUser() {
return Mock(GrailsUser)
}
...
/**
* must call AFTER mockDpSercurityService
*/
protected void setHasRoleTrue() {
if (controller?.dpSecurityService?.metaClass) {
controller.dpSecurityService.metaClass.hasRole = {return true}
}
}
protected void setHasRoleFalse() {
if (controller?.dpSecurityService?.metaClass) {
controller.dpSecurityService.metaClass.hasRole = {return false}
}
}
protected void mockUserService() {
controller.dpUserService = new MockFor(UserService)
}
}
http://sanjaykanwar.blogspot.com/2012/07/grails-controller-test-with-spock.html
http://naleid.com/blog/2012/05/01/upgrading-to-grails-2-unit-testing
Looks like the if branch gets executed in edit() instead of the else branch because FilterCategory does not get saved and therfore does not get a proper id.
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???
}
}
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
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>")
}
How to mock an object that implements an interface and methods not belonging to the latter? I need aplicacaoPT to implement BindingProvider and buscaAplicacaoPorCodigo. The cast works but the buscaAplicacaoPorCodigo call gives a MissingMethodException. I tried with a map and an Expando:
import static org.junit.Assert.*
import grails.test.mixin.*
import grails.test.mixin.support.*
import org.junit.*
import javax.xml.ws.BindingProvider
#TestMixin(GrailsUnitTestMixin)
class CatalogClientServiceTests {
void testFetch() {
AplicacaoSoapService.metaClass.getAplicacaoSoapPort = {
[ getRequestContext: { new HashMap<String, Object>() },
buscaAplicacaoPorCodigo: { String appCode -> "carrapato!" } ] as BindingProvider
}
def applicationDataFromCatalog = service.fetch("dino")
}
void testFetchFailsWithInvalidApplicationCode() {
AplicacaoSoapService.metaClass.getAplicacaoSoapPort = {
def aplicacaoSoapPort = new Expando()
aplicacaoSoapPort.getRequestContext = { new HashMap<String, Object>() }
aplicacaoSoapPort.buscaAplicacaoPorCodigo = { String appCode -> "castanha!" }
aplicacaoSoapPort as BindingProvider
}
shouldFail(IllegalArgumentException) { service.fetch("dino") }
}
}
import javax.xml.ws.BindingProvider
class CatalogClientService {
def aplicacaoPT = new AplicacaoSoapService().getAplicacaoSoapPort()
def grailsApplication
def fetch(String appCode) {
Map<String, Object> req_ctx = ((BindingProvider)aplicacaoPT).getRequestContext();
req_ctx.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, grailsApplication.config.catalogAppEndPointAddress);
def response = aplicacaoPT.buscaAplicacaoPorCodigo(appCode)
println "response: $response"
}
}
I also tried adding buscaAplicacaoPorCodigo to BindingProvider's metaclass but it sticks with one of the implementations for all tests. In the example below response will be printed (above) either "castanha!" or "carrapato!" on both tests for each run.
import static org.junit.Assert.*
import grails.test.mixin.*
import grails.test.mixin.support.*
import org.junit.*
import javax.xml.ws.BindingProvider
#TestMixin(GrailsUnitTestMixin)
class CatalogClientServiceTests {
void testFetch() {
BindingProvider.metaClass.buscaAplicacaoPorCodigo = { String appCode -> "carrapato!" }
AplicacaoSoapService.metaClass.getAplicacaoSoapPort = {
[ getRequestContext: { new HashMap<String, Object>() } ] as BindingProvider
}
def applicationDataFromCatalog = service.fetch("dino")
}
void testFetchFailsWithInvalidApplicationCode() {
BindingProvider.metaClass.buscaAplicacaoPorCodigo = { String appCode -> "castanha!" }
AplicacaoSoapService.metaClass.getAplicacaoSoapPort = {
def aplicacaoSoapPort = new Expando()
aplicacaoSoapPort.getRequestContext = { new HashMap<String, Object>() }
aplicacaoSoapPort as BindingProvider
}
shouldFail(IllegalArgumentException) { service.fetch("dino") }
}
}
I stripped parts of the code above for the sake of simplicity.
I hope some of may help me! :)
I have zero experience with grails, but this seems a problem related with proxies. The resulting proxy doesn't have the buscaAplicacaoPorCodigo available.
AFAIK, you have two choices: either create a interface Buscador, with buscaAplicacaoPorCodigo, and create a super interface for both BindingProvider and Buscador, or, for a pure dynamic solution, metaClass the resulting proxied expando:
I mocked some of your classes, hope I got it right.
BindingProvider.groovy:
interface BindingProvider {
def getRequestContext()
}
And the rest:
class AplicacaoSoapService {
static BindingProvider getAplicacaoSoapPort() {
assert false, "Nope, must be mocked"
}
}
AplicacaoSoapService.metaClass.static.getAplicacaoSoapPort = {
def e = new Expando(
getRequestContext: { [:] }) as BindingProvider
e.getMetaClass().buscaAplicacaoPorCodigo = { String a -> "amendoa!" }
e
}
def provider = AplicacaoSoapService.aplicacaoSoapPort
assert provider.buscaAplicacaoPorCodigo("pudim") == "amendoa!"