JSON rendering fails for domain objects in ControllerUnitTestCase - unit-testing

I'm writing a unit test for a Grails controller that renders a domain class to a JSON response:
class MyController {
def find = {
def domainInst = MyDomainClass.get(params.id)
render ([data: domainInst] as JSON)
}
}
The unit test extends ControllerUnitTestCase and provides a mock for the domain object:
class MyControllerTests extends ControllerUnitTestCase {
#Before
void setUp() {
super.setUp()
mockDomain(MyDomainClass, [new MyDomainClass(id: 7)])
}
#Test
void testFind() {
def inst = MyDomainClass.get(7)
controller.params.id = inst.id
controller.find()
assert(controller.response.json.data.id == inst.id)
}
This all seems to be working nicely except for the JSON rendering, which spits out a nasty stack trace:
| Failure: testFind(MyControllerTests)
| org.apache.commons.lang.UnhandledException:
org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Error converting Bean with class MyDomainClass
Caused by: org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Error converting Bean with class MyDomainClass
at grails.converters.JSON.value(JSON.java:199)
at grails.converters.JSON.convertAnother(JSON.java:162)
at grails.converters.JSON.value(JSON.java:199)
at grails.converters.JSON.render(JSON.java:134)
... 5 more
Caused by: java.lang.reflect.InvocationTargetException
... 9 more
Caused by: groovy.lang.MissingMethodException: No signature of method: MyDomainClass.isAttached() is applicable for argument types: () values: []
Possible solutions: isAttached(), attach()
... 9 more
Changing the return to a Map instead of a domain class works:
render ([data: [id: domainInst.id]] as JSON)
What's causing the JSON marshaller to die on the domain class? It works in a normal environment, but not in the mock test environment. Is there a way to make this test work?

Looks like you might need to do some fine tuning to make the converters realize that you're trying to render a domain class as a JSON object. It works when you manually put your id into a map because it is rendering the response from a Map object instead of a Grails domain class, which needs to go through a special ObjectMarshaller.
Something like this:
// Domain Class
class Foo {
String foo
}
// Controller class
class MyController {
def find = {
def domainInst = Foo.get(params.id)
render domainInst as JSON
}
}
// Controller Test Class
class MyControllerTests extends ControllerUnitTestCase {
static application
#Before
void setUp() {
super.setUp()
// Register some common classes so that they can be converted to XML, JSON, etc.
def convertersInit = new ConvertersConfigurationInitializer()
convertersInit.initialize(application)
[ List, Set, Map, Errors ].each { addConverters(it) }
def xmlErrorMarshaller = new ValidationErrorsMarshaller()
XML.registerObjectMarshaller(xmlErrorMarshaller)
def jsonErrorMarshaller = new ValidationErrorsMarshaller()
JSON.registerObjectMarshaller(jsonErrorMarshaller)
ApplicationHolder.application.addArtefact("Domain", Foo)
mockDomain(Foo, [new Foo(foo: "foo")] )
}
#Test
void testJSON() {
def inst = Foo.list()[0]
controller.params.id = inst.id
def model = controller.find()
assert controller.response.json.foo == "foo"
}
#Override
protected def bindMockWebRequest(GrailsMockHttpServletRequest mockRequest, GrailsMockHttpServletResponse mockResponse) {
MockApplicationContext ctx = new MockApplicationContext()
application = new DefaultGrailsApplication([testClass] as Class[], getClass().classLoader)
application.initialise()
ctx.registerMockBean("grailsApplication", application)
ctx.registerMockBean(testClass.name, testClass.newInstance())
def lookup = new TagLibraryLookup(applicationContext: ctx, grailsApplication: application)
lookup.afterPropertiesSet()
ctx.registerMockBean("gspTagLibraryLookup", lookup)
ctx.registerMockBean(GroovyPagesUriService.BEAN_ID, new DefaultGroovyPagesUriService())
mockRequest.servletContext.setAttribute(ApplicationAttributes.APPLICATION_CONTEXT, ctx)
mockRequest.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx)
webRequest = new GrailsWebRequest(mockRequest, mockResponse, mockRequest.servletContext)
mockRequest.setAttribute(GrailsApplicationAttributes.WEB_REQUEST, webRequest)
RequestContextHolder.setRequestAttributes(webRequest)
}
}
Hope this helps!

Related

Grails 3 Controller Unit Testing: Domain class validation not working in a ControllerUnitTest

I'm upgrading an inherited Grails 2 app to 3.3.10. We have controller methods that rely on Domain class validation to control logic flow, but we can't get DomainClass validation to work in ControllerUnitTests when running in Grails 3
For example,
Gift and RecipientAddress are DomainClasses and RecipientAddress.hasErrors() is used to validate against the RecipientAddress constraints.
def confirmAddress() {
Gift gift = Gift.get(params.giftId)
if (!gift) {
render(view: "index", model: [invalid: true])
return
}
recipientAddress = recipientAddressService.storeAddressInformation(params, gift)
if (recipientAddress.hasErrors()) {
render(view: "index", model: getAddressErrorModel(gift, recipientAddress))
return;
} else {
return [
recipientAddress : recipientAddress,
gift : gift
]
}
}
In the following test, when I debug the controller method it does everything expected but recipientAddress.hasErrors() always returns true and the test fails.
ie:
#Transactional
class GiftDetailsControllerTest extends Specification implements ControllerUnitTest<GiftDetailsController> {
#Shared
#AutoCleanup
SimpleMapDatastore dataStore = new SimpleMapDatastore([ConnectionSource.DEFAULT, "reporting"],
RecipientAddress, Gift)
def setup() {
controller.recipientAddressService = Mock(RecipientAddressService)
}
void "test RecipientAddress Bad PhoneNumber"() {
given:
RecipientAddress recipientAddress = new RecipientAddress(
phone: '123-456-789'
)
UnitTestDataFactory dataFactory = UnitTestDataFactory.getDataFactory()
Gift gift = dataFactory.getMockGift()
gift.save()
params.giftId = gift.id
when:
recipientAddress.validate()
controller.confirmAddress()
then:
recipientAddress.hasErrors()
recipientAddress.getErrors().getFieldError('phone')
1 * controller.recipientAddressService.storeAddressInformation(params, gift) >> recipientAddress
view == '/giftDetails/index'
}
}
Implementing DataTest ie: ...implements ControllerUnitTest<GiftDetailsController>, DataTest { fixes the DomainClass validation, but breaks the controllers ability to get the saved Gift.
Is there a way get DomainClass validation working in a ControllerUnit test?
Fix
Implemented DataTest with mockDomains and had to remove the custom dataStore.
#Transactional
class GiftDetailsControllerTest extends Specification implements ControllerUnitTest<GiftDetailsController>, DataTest {
// #Shared
// #AutoCleanup
// SimpleMapDatastore dataStore = new SimpleMapDatastore([ConnectionSource.DEFAULT, "reporting"], RecipientAddress, Gift)
void setupSpec() {
mockDomains RecipientAddress, Gift
}
....
Is there a way get DomainClass validation working in a ControllerUnit
test?
Yes.
You probably want something like this...
class GiftDetailsControllerTest extends Specification implements ControllerUnitTest<GiftDetailsController>, DataTest {
Class[] getDomainClassesToMock() {
[Gift]
}
// ...
}

How to mock springSecurityService in an unit test

I am unit testing a Grails controller method that internally creates an user instance. User domain class uses the springSecurityService of the Spring Security plugin to encode the password before inserting it into the database.
Is there a way to mock that springSecurityService from my unit test in order to get rid of that error?
Failure: Create new individual member(MemberControllerSpec)
| java.lang.NullPointerException: Cannot invoke method encodePassword() on null object
Please find my unit test below.
#TestMixin(HibernateTestMixin)
#TestFor(MemberController)
#Domain([User, IndividualPerson])
class MemberControllerSpec extends Specification {
void "Create new individual member"() {
given:
UserDetailsService userDetailsService = Mock(UserDetailsService)
controller.userDetailsService = userDetailsService
def command = new IndividualPersonCommand()
command.username = 'scott#tiger.org'
command.password = 'What ever'
command.firstname = 'Scott'
command.lastname = 'Tiger'
command.dob = new Date()
command.email = command.username
command.phone = '89348'
command.street = 'A Street'
command.housenumber = '2'
command.postcode = '8888'
command.city = 'A City'
when:
request.method = 'POST'
controller.updateIndividualInstance(command)
then:
view == 'createInstance'
and:
1 * userDetailsService.loadUserByUsername(command.username) >> null
and:
IndividualPerson.count() == 1
and:
User.count() == 1
cleanup:
IndividualPerson.findAll()*.delete()
User.findAll()*.delete()
}
}
One way to mock a service is to use Groovy's MetaClass
import grails.test.mixin.Mock
import grails.plugin.springsecurity.SpringSecurityService
...
#Mock(SpringSecurityService)
class MemberControllerSpec extends Specification {
def setupSpec() {
SpringSecurityService.metaClass.encodePassword = { password -> password }
}
def cleanupSpec() {
SpringSecurityService.metaClass = null
}
....
In this example, the call to SpringSecurityService.encodePassword() will simply return the password in plain text.
An approach using Mocks is discussed here.
You can to use this code to encode password in User:
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
}
When springSecurityService is null, encodePassword is not called and NPE is not raised
When you use controller unit test with spring security rest plugin in Grails v4/v3, if your controller method reference springSecurityService methods like 'athenticatedUser', there will be NullPointException, because springSecurityService is not autowired into the spring application context.
Add code like below, you can inject springSecurityService and mock it's methods.
class GuessControllerSpec extends Specification implements ControllerUnitTest<GuessController> {
#Override
Closure doWithSpring() {
return {
// mock method
SpringSecurityService.metaClass.getCurrentUser = {return new User()}
// inject into spring context
springSecurityService(SpringSecurityService)
}
}
...
}

Grails / Spock: How to mock single method within class where method is called from within the class itself?

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:
...
}
}

how to avoid a domain call in unit testing with grails / spock

I have the following lines of code
username = username.stripIndent()
user = User."${databaseInstance}".findByUsername(username)
if (user == null){
return "User does not exist"
}
I'm trying to unit test this functionality with:
def setup() {
def mockUser = Mock(User)
myClass.user = mockUser
}
void "userNotFoundGetUserInfo"(){
given:
myClass.databaseInstance = 'X'
_ * mockUser._ >> null
when:
def result = myClass.getUserInfo(username)
then:
result == "User does not exist"
}
but I keep getting an error of "No such property: X for class mypackage.User"
I realize this is because i'm mocking the "user" object and not the "User" class, so how do I get around the fact that my code is making a direct call to a domain class?
Use Grails built-in #Mock annotation instead of Spock's Mock() method. Go like:
#Mock(User)
class YourTestSpecification extends Specification {
def setup() {
myClass.user = new User()
}
}
#Mock is meant to mock Grails domain classes.

How do you test default sort in a grails unit test with mocked domains

Is it possible to test the sort propertyName which is defined in the staticMappingBlock?
This works during the integration phase but not during the unit phase where my domain has:
static mapping = {
sort 'lastName'
}
void testDefaultSortOrder(){
def agent1 = new CommissionAgent(firstName: 'fred', lastName: 'b', active:true).save()
def agent2 = new CommissionAgent(firstName: 'fred2', lastName:'a', active:false).save()
def agents = CommissionAgent.list()
assertEquals 'sort order is wrong', agents[0].lastName, agent2.lastName
assertEquals 'sort order is wrong', agents[1].lastName, agent1.lastName
}
Grails version is 1.3.1
There isn't any good way to test the actual sorting in unit tests that I'm aware of. What you're trying to test is really such an integral part of the GORM integration that testing it on mocked domain objects, even if they support the sort mapping, doesn't test the actual code that will be run.
The closest thing that you could do in a unit test would be to take a look at the static mapping object to assert that the value of "sort" is set to what you expect it to be.
I put together a blog post a while ago on how to interrogate groovy closures for values. This technique could be used to assert the sort order is set to what you expect like this:
Foo domain object:
package com.example
class Foo {
String name
static mapping = {
sort "name"
}
}
FooTests unit test:
package com.example
import grails.test.*
class FooTests extends GrailsUnitTestCase {
void testFooSort() {
def mappingValues = ClosureInterrogator.extractValuesFromClosure(Foo.mapping)
assertEquals "name", mappingValues.sort
}
}
ClosureInterrogator class that allows you to see what a closure does:
package com.example
class ClosureInterrogator {
private Map closureValueMap = [:]
static Map extractValuesFromClosure(Closure closure) {
def interrogator = new ClosureInterrogator(closure)
return interrogator.closureValueMap
}
private ClosureInterrogator(Closure closure) {
def oldResolveStrategy = closure.getResolveStrategy()
def oldDelegate = closure.getDelegate()
closure.delegate = this
closure.resolveStrategy = Closure.DELEGATE_FIRST
try {
closure()
} finally {
closure.setDelegate(oldDelegate)
closure.setResolveStrategy(oldResolveStrategy)
}
}
// property getter
def propertyMissing(String name) {
return closureValueMap[name]
}
// property setter
def propertyMissing(String name, value) {
closureValueMap[name] = value
}
def methodMissing(String name, args) {
if (args.size() == 1) {
closureValueMap[name] = args[0]
} else {
closureValueMap[name] = args
}
}
}