Putting an object on a Request in a Grails Integration Test - grails

In a Grails project I am looking at here, a filter puts a Domain object on the request...
class TokenFilters {
def filters = {
all( uri: '/hiphop/**' ) {
before = {
MyToken myToken = ...
request.myToken = myToken
MyToken looks like:
class MyToken {
String id
String token
static mapping = {
token( index: true )
id( generator: 'uuid' )
}
...
}
In my controller, the myToken is pulled off the request.
MyController {
myaction {
MyToken accessToken = request.myToken
All fine. I wish to write an integration test for the controller.
#Test
void testLogin() {
def mc = new MyController()
def myToken = new MyToken(1234);
// set the request parameters
mc.request.parameters = [myToken:myToken];
def message = mc.action();
assertTrue(message.indexOf("trans") > 0)
}
When I run this, I get:
Failure: testLogin(MyTests)
| java.lang.IllegalArgumentException: Parameter map value must be single value or array of type [java.lang.String]
at testLogin(MyTests.groovy:40)
So it looks like Grails will only let me a String or a single value and doesn't like me putting an object on the request in the Filter. Even thou it lets me put on the same object type in a Filter.
I'd really like to test this without going to Functional tests. Please help. I am using Grails 2.2.1
Thanks

The problem is that your code is passing parameters to the controller. Your emulating an HTTP request which can't handle objects. What you can do is:
mc.request.parameters = [myToken: '1234']
and then you're controller/filter would pull out the 1234 and look up MyToken. If you were testing the controller forwarding then you can put objects in the request. Not the other way around.
I see now that part of the problem is that you're trying to test a controller that is assuming data coming from a filter.
You've omitted some code, but assuming you are extending ControllerUnitTestCase then you have access to a mock request object. You should be able to simply do:
#Test
void testLogin() {
def mc = new MyController()
def myToken = new MyToken(1234);
// set the request parameters
request.myToken = myToken
def message = mc.action();
assertTrue(message.indexOf("trans") > 0)
}

Related

Unable to bind Http Form data to HashMap collection

I am trying to post form data as
"student.id=1&courses[10].course.id=10"
The command object classes we are trying to bind are as below ...
class StudentEnrollmentCmd {
Student student
Map<String,CourseCmd> courses;
}
class CourseCmd {
CourseDomain course
}
class CourseDomain {
Long id
}
was hoping that it will bind to
"StudentEnrollmentCmd -> courses -> course.id"
which seems to work in grails 2.2.4 but fails in 3.3.7 with the following exception
No such property: course.id for class: student.CourseCmd
Here is the test case which illustrates the problem
void 'databinding from request parameters'() {
given:
// request with simple formdata: student.id=1&courses[10].course.id=10
MockHttpServletRequest request = buildMockRequestWithParams('POST',['student.id':'1','courses[10].course.id':'10']);
DataBindingSource source = bindingSourceCreator.createDataBindingSource(null,null,request);
// databinder & command object
def binder = new SimpleDataBinder()
def obj = new StudentEnrollmentCmd()
when:
binder.bind(obj,source)
then:
// this should not throw an exception, but throws an exception
MissingPropertyException ex = thrown()
System.out.println ( "Exception again:" + ex.message );
// the following should work, but does not work
obj.student.id == 1
obj.courses['10'].course.id == 10
}
Here is the link to full spec... https://github.com/swzaidi/sample/blob/master/grails3.3.7/src/test/groovy/student/DataBindingSpec.groovy
Looking for some help on how to pass the form data so that it binds to above command objects properly.

How can I access the id parameter in a #Secured() expression when the id is pulled from the url

I have a controller that uses #Secured() to restrict access to an action based on the specific domain object being processed:
#Secured('#permissionChecker.isLocationAdmin()')
public edit(Location location) {
// [code]
}
Based on a previous answer, I know I can't pass the location object to the permission checker. When using the proposed workaround (in the permissionChecker.isLocationAdmin code),
import org.springframework.web.context.request.RequestContextHolder
...
def params = RequestContextHolder.requestAttributes.params
I can get access to the id parameter if I use the url https://.../edit?id=42.
However, if I use https://.../edit/42, params object is empty. When I look at the params in the controller, controller, action, and id are set. The params object I get in the controller is also not the same (according to the debugger).
My UrlMappings are pretty straight forward, with the relevant entry:
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
What seems to work so far is pulling the id out of the url by hand:
def params = RequestContextHolder.requestAttributes.params
String idStr = params.id
if (!idStr) {
String url = ((ServletRequestAttributes) RequestContextHolder.requestAttributes).request.getRequestURI()
int idx = url.lastIndexOf('/')
if (url && idx >= 0) {
idStr = url.substring(idx + 1)
}
}
but this seems wrong and fragile. Is there a better way?

Spock No thread-bound request found

I have created a simple util class UtilService.groovy and when I tried to run write test case for it.
class UtilService {
static transactional = true
def messageSource
HttpServletRequest getCurrentRequest() {
GrailsWebRequest webUtils = WebUtils.retrieveGrailsWebRequest()
def request = webUtils.getCurrentRequest()
return request
}
String getMessage(String code, Object[] args = null, String defaultMessage = null) {
HttpServletRequest request = currentRequest
Locale locale = request.locale
if (defaultMessage) {
return messageSource.getMessage(code, args, defaultMessage, locale)
} else {
return messageSource.getMessage(code, args, locale)
}
}
Test case
#TestMixin(GrailsUnitTestMixin)
class UtilServiceSpec extends Specification {
void "Test get message"() {
setup:
def utilService = new UtilService()
when:
String data = utilService.getMessage("payCode.label")
then:
data == "Pay Code"
}
}
Error:
No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread?
I have searched and try some links from google but they did not work for me.
Obviously, you are doing it outside of web request, WebUtils.retrieveGrailsWebRequest() will fail because there's no web request going on. If your service is called from a controller during tests it should work.
Instead of you service depending on WebRequest, why don't you pass the Locale as an argument to getMessage, controllers can call getMessage and pass the request Locale, or else the service will choose the default locale. Its generally not good idea to have services be aware of web apis (eg requests, response, session etc). And you will be able to test both service and controller.
I have written Integration test for this requirement rather than Unit test and able to run my test cases successfully.

grailsApplication null in Service

I have a Service in my Grails application. However I need to reach the config for some configuration in my application. But when I am trying to use def grailsApplication in my Service it still gets null.
My service is under "Services".
class RelationService {
def grailsApplication
private String XML_DATE_FORMAT = "yyyy-MM-dd"
private String token = 'hej123'
private String tokenName
String WebserviceHost = 'xxx'
def getRequest(end_url) {
// Set token and tokenName and call communicationsUtil
setToken();
ComObject cu = new ComObject(tokenName)
// Set string and get the xml data
String url_string = "http://" + WebserviceHost + end_url
URL url = new URL(url_string)
def xml = cu.performGet(url, token)
return xml
}
private def setToken() {
tokenName = grailsApplication.config.authentication.header.name.toString()
try {
token = RequestUtil.getCookie(grailsApplication.config.authentication.cookie.token).toString()
}
catch (NoClassDefFoundError e) {
println "Could not set token, runs on default instead.. " + e.getMessage()
}
if(grailsApplication.config.webservice_host[GrailsUtil.environment].toString() != '[:]')
WebserviceHost = grailsApplication.config.webservice_host[GrailsUtil.environment].toString()
}
}
I have looked on Inject grails application configuration into service but it doesn't give me an answer as everything seems correct.
However I call my Service like this: def xml = new RelationService().getRequest(url)
EDIT:
Forgot to type my error, which is: Cannot get property 'config' on null object
Your service is correct but the way you are calling it is not:
def xml = new RelationService().getRequest(url)
Because you are instantiating a new object "manually "you are actually bypassing the injection made by Spring and so the "grailsApplication" object is null.
What you need to do is injecting your service using Spring like this:
class MyController{
def relationService
def home(){
def xml = relationService.getRequest(...)
}
}

Writing an Integration Test in Grails that will test DB persistence

My Integration-Test for my grails application is returning a null object when I try to get a domain object using grails dynamic get method.
This is a simplified example of my problem. Lets say I have a controller TrackerLogController that uses a service TrackerLogService to save an updated Log domain for another Tracker domain.
Domain Tracker:
class Tracker {
int id
String name
static hasMany = [logs: Log]
}
Domain Log:
class Log {
int id
String comment
static belongsTo = [tracker: Tracker]
}
Controller TrackerLogController save:
def TrackerLogService
def saveTrackerLog() {
def trackerId = params.trackerId
def trackerInstance = Tracker.get(trackerId)
Log log = TrackerLogService.saveTrackerLogs(trackerInstance, params.comment)
if( log.hasErrors() ){
//render error page
}
//render good page
}
Service TrackerLogService save:
Log saveTrackerLogs( Tracker tracker, String comment) {
Log log = new Log(tracker: tracker, comment: comment)
log.save()
return log
}
So now I want to write an Integration-Test for this service but I'm not sure if I should be writing one just for the simple logic in the controller (if error, error page else good page) I would think I would write a Unit test for that, and an Integration-Test to check the persistence in the Database.
This is what I have for my Integration-Test:
class TrackerLogServiceTests {
def trackerLogService
#Before
void setUp(){
def tracker = new Tracker(id: 123, name: "First")
tracker.save()
//Now even if I call Tracker.get(123) it will return a null value...
}
#Test
void testTrackerLogService() {
Tacker trackerInstance = Tracker.get(123) //I have tried findById as well
String commit = "This is a commit"
//call the service
Log log = trackerLogService.saveTrackerLogs(trackerInstance , commit)
//want to make sure I added the log to the tracker Instance
assertEquals log , trackerInstance.logs.findByCommit(commit)
}
}
So for this example my trackerInstance would be a null object. I know the Grails magic doesn't seem to work for Unit tests without Mocking, I thought for Intigration-Tests for persistence in the DB you would be able to use that grails magic.
You can't specify the id value unless you declare that it's "assigned". As it is now it's using an auto-increment, so your 123 value isn't used. It's actually ignored by the map constructor for security reasons, so you'd need to do this:
def tracker = new Tracker(name: "First")
tracker.id = 123
but then it would get overwritten by the auto-increment lookup. Use this approach instead:
class TrackerLogServiceTests {
def trackerLogService
private trackerId
#Before
void setUp(){
def tracker = new Tracker(name: "First")
tracker.save()
trackerId = tracker.id
}
#Test
void testTrackerLogService() {
Tacker trackerInstance = Tracker.get(trackerId)
String commit = "This is a commit"
//call the service
Log log = trackerLogService.saveTrackerLogs(trackerInstance , commit)
//want to make sure I added the log to the tracker Instance
assertEquals log , trackerInstance.logs.findByCommit(commit)
}
}
Also, unrelated - don't declare the id field unless it's a nonstandard type, e.g. a String. Grails adds that for you, along with the version field. All you need is
class Tracker {
String name
static hasMany = [logs: Log]
}
and
class Log {
String comment
static belongsTo = [tracker: Tracker]
}

Resources