I have a service class and i am trying to call the method of the service in my controller as below.
class LogListController {
def ListLogDetails = {
println "We are inside List log Details-->"+params
def logListHelperService
logListHelperService.getFilePath(params)
}}
Exception Message: Cannot invoke method getFilePath() on null object
what is my mistake there..
def logListHelperService
must be declared outside of the ListLogDetails definition
def logListHelperService
def ListLogDetails = {
println "We are inside List log Details-->"+params
logListHelperService.getFilePath(params)
}
should work
Related
This question is connected with another.
I'd like to add properties to constructor and overwrite getLocalisedMessage() function to get proper translated message with error. First I want to overload constructor to set properties, but when I add:
GroovyCastException.metaClass.constructor = { Object objectToCast, Class classToCastTo ->
def constructor = GroovyCastException.class.getConstructor(Object, Class)
def instance = constructor.newInstance(objectToCast, classToCastTo)
// ... do some further stuff with the instance ...
println "Created ${instance} and executed!"
instance
}
and then get thrown GroovyCastException I don't get println in console.
Why?
How to overload constructor, set properties (objectToCast, classToCastTo) and then overload getLocalizedMessage?
I tried also:
def originalMapConstructor = GroovyCastException.metaClass.retrieveConstructor(Map)
GroovyCastException.metaClass.constructor = { Map m ->
// do work before creation
print "boot do work before creation "
m.each{
print it
}
print "boot do work before creation 2"
def instance = originalMapConstructor.newInstance(m)
// do work after creation
print "boot do work after creation"
instance
}
I 've put it in controller (right before catching exception) and in Bootstrap.groovy. Unfortunatelly there is no printlns in console output.
You're better off not using meta-programming to do internationalization. In grails, you should do it in the view layer with the <g:message> tag if possible. If not, the next best choice is the controller layer.
If you just want to display localized messages on an error page when an exception occurs, the best practice is to have a "500" URL mapping, and render the exception with a <g:renderException> in the view.
If you want to intercept the exception, you can change the "500" URL mapping to a controller and wrap it there before passing it to the view. Example:
// UrlMappings.groovy
class UrlMappings {
static mappings = {
...
"500"(controller:"error", method: "serverError")
}
}
// ErrorController.groovy
class ErrorController {
def serverError() {
def exception = request.exception.cause
if (exception instanceof GroovyCastException) {
exception = new LocalizedGroovyCastException(exception)
}
[exception: exception]
}
}
And then do your localization in a new class LocalizedGroovyCastException.
I have an integration test where I sometimes want to mock the return of a service method. However, I have seen that once I mock that method, the subsequent tests that call it will also use the mocked function.
Is this normal? If so, how can I have test which sometimes use mocked functions and sometimes use the real implementation?
Here is my code:
MyController {
def someService
def save(){
...
def val = someService.methodToMock()//sometimes want to mock other times, not
...
}
}
MyTest {
def "test 1"(){
...
//I want to mock here
myController.someService.metaClass.methodToMock = { [] }
...
myController.save()
}
def "test 2"(){
...
//I don't want to mock here, however
// it is returning the mocked results
myController.save()
}
}
In general you don't want to change anything to do with metaclasses in integration or functional tests, only in unit tests. It's expected that you'll be doing this in unit tests and there's automatic support for restoring the original metaclass after each test or after each test class runs depending on the version of Grails and how things are configured. But this isn't the case in integration tests.
There are several different approaches you can use. If you use untyped dependency injection, e.g. def someService, then you can overwrite the real service instance with anything you want, and as long as it has the method(s) that you'll be invoking during the test method the controller won't know or care that it's not the real service.
I like to use a map of closures in this case, since Groovy will invoke a closure as if it were a method. So for 'test 1' you could do this:
def "test 1"() {
...
def mockedService = [methodToMock: { args -> return ... }]
myController.someService = mockedService
...
myController.save()
}
This works because you get a new instance of the controller for each test, and you change the service just for that instance, but the real service isn't affected at all.
Your controller invokes someService.methodToMock(), which is actually someService.get('methodToMock').call(), but the map access and closure invocation syntax can take advantage of Groovy's syntactic sugar to look like a regular method call.
Another option is to subclass the service and override the method(s) that you want, and replace the injected instance with that. This or something like it would be necessary if you type the dependency injection (e.g. SomeService someService). Either create a named subclass (class TestSomeService extends SomeService { ... }) or create an anonymous inner class:
def "test 1"() {
...
def mockedService = new SomeService() {
def methodToMock(args) {
return ...
}
}
myController.someService = mockedService
...
myController.save()
}
Altering the metaClass in one test will absolutely affect other tests. You're altering the groovy system, and need to perform some special cleanup if you're metaClassing. At the end my methods where I metaClass, I call a function to revoke the metaClass changes, passing in the name of the class that was metaClassed, and the instance metaClassed if there was one.
def "some authenticated method test"() {
given:
def user = new UserDomain(blah blah blah)
controller.metaClass.getAuthenticatedUser = { return user }
when:
controller.authenticatedMethod() // which references the authenticated user
then:
// validate the results
cleanup:
revokeMetaClassChanges(theControllerClass, controller)
}
private def revokeMetaClassChanges(def type, def instance = null) {
GroovySystem.metaClassRegistry.removeMetaClass(type)
if (instance != null) {
instance.metaClass = null
}
}
Alternatively, you can just mock the service in the test. A method similar to that mentioned by Burt could be:
def "some test"() {
given:
def mockSomeService = mockFor(SomeService)
mockSomeService.demand.methodToMock(1) { def args ->
return []
}
controller.someService = mockSomeService.createMock()
when:
controller.save()
then:
// implement your validations/assertions
}
I have a method in service class which has to be called from the controller. Both the controller and the service class codes are as below.
folder grails-app/services, file FileFactoryService.groovy
package edu.rev.document
import grails.transaction.Transactional
Class FileFactoryService{
Document document = new Document() // Domain object
def build(byte[] fileArray){
String str = new String(fileArray, "UTF-8") // UTF encoding as invoice may contain negative values
String[] lines = str.split("\\r?\\n")
document.version = lines[0].substring(0,1)
document.name = lines[1].substring(0,25)
}
return document.properties.collect()
}
Controller Code: folder: grails-app/controllers, file: FileController.groovy
package edu.rev.document
Class FileController{
def fileFactoryService
def save(){
def file = request.getFile('file')
if(file.empty) {
flash.message = "File cannot be empty"
} else {
def myList = fileService.build(file.getBytes())
}
}
The error thrown is
NullPointer exception when processing [POST]/../save
Cannot invoke method build() on NULL object
Can you please point me to the mistake I might be committing? Let me know if you need any other information
EDIT:This is the code. Just a heads up, the same logic when taken out of the service and implemented in the controller itself works perfectly alright. One more thing, when I use the "." operator inside the service (say document.), it doesnt show may any auto complete options like document.name.
Posting all of your code helps in finding the error. This line in your controller class
def myList = fileService.build(file.getBytes())
should be
def myList = fileFactoryService.build(file.getBytes())
In your controller class, you declared the service as :
def fileService
But the name of your service class is :
Class FileFactoryService
For Grails dependency injection to work, you need to name the variable like your class name :
def fileFactoryService
Then this should work.
Within a Spock unit test, I am trying to test the behaviour of a method findRepositoriesByUsername independent of getGithubUrlForPath, both belonging to the same service.
Repeated attempts to use the metaClass have failed:
String.metaClass.blarg produces an error No such property: blarg for class: java.lang.String
service.metaClass.getGithubUrlForPath to modify the service instance doesn't work
GithubService.metaClass.getGithubUrlForPath to modify the service class doesn't work
Tried adding/modifying methods on the metaClass in the test methods' setup and when blocks, neither worked as expected
The test:
package grails.woot
import grails.test.mixin.TestFor
#TestFor(GithubService)
class GithubServiceSpec extends spock.lang.Specification {
def 'metaClass test'() {
when:
String.metaClass.blarg = { ->
'brainf***'
}
then:
'some string'.blarg == 'brainf***'
}
def 'can find repositories for the given username'() {
given:
def username = 'username'
def requestPathParts
when: 'the service is called to retrieve JSON'
service.metaClass.getGithubUrlForPath = { pathParts ->
requestPathParts = pathParts
}
service.findRepositoriesByUsername(username)
then: 'the correct path parts are used'
requestPathParts == ['users', username, 'repos']
}
}
The service:
package grails.woot
import grails.converters.JSON
class GithubService {
def apiHost = 'https://api.github.com/'
def findRepositoriesByUsername(username) {
try{
JSON.parse(getGithubUrlForPath('users', username, 'repos').text)
} catch (FileNotFoundException ex) {
// user not found
}
}
def getGithubUrlForPath(String ... pathParts) {
"${apiHost}${pathParts.join('/')}".toURL()
}
}
I've tested the String.metaClass.blarg example in the groovy shell (launched by grails), and it did as expected.
Do I have a fundamental misunderstanding here? What am I doing wrong? Is there a better way to handle the desired test (replacing a method on the service under test)?
This is how the tests can be written to make them pass:
def 'metaClass test'() {
given:
String.metaClass.blarg = { -> 'brainf***' }
expect:
// note blarg is a method on String metaClass
// not a field, invoke the method
'some string'.blarg() == 'brainf***'
}
def 'can find repositories for the given username'() {
given:
def username = 'username'
def requestPathParts
when: 'the service is called to retrieve JSON'
service.metaClass.getGithubUrlForPath = { String... pathParts ->
requestPathParts = pathParts
[text: 'blah'] // mimicing URL class
}
service.findRepositoriesByUsername(username)
then: 'the correct path parts are used'
requestPathParts == ['users', username, 'repos']
}
Why don't you use Spock's great Mocking abilities?
Look at http://spockframework.github.io/spock/docs/1.0/interaction_based_testing.html#_creating_mock_objects
There is no need to peek inside metaclass itself, you can create some stub object, and demanded method will be called instead of original one. Also you can use Groovy's MockFor and StubFor, they can be a little bit easier.
You cannot fully trust metaclass inside spock tests specification.
There is some complex logic inside it, which can easyly mess thing's up. Try run some tests under debugger, and you will see it.
Spock uses metaclasses under the hood. It can override your own try's.
I have one service in that i have a method to call and how can i acces this service.
I have seen sms plugin and installed it and How can i send sms from my application to the different mobiles.I followed the grails sms plugin but i didn't get any results ecxept ecxeptions
class SipgateService {
static transactional = true
def serviceMethod() {
def sipgateService
//def phoneNumber = 'XXXXXXXXXX' //phoneNumber according to E.164 specification
//working alternative:
println "service"
def phoneNumber = 'XXXXXXXXXX'
def result = sipgateService.sendSMS(phoneNumber, 'This is my Text to send!')
result ? 'Sending Successful':'Sending failed'
println "after service"
}
}
Please explain me with an example.
thanks alot in advance.
If you want to call the plugin from a service method, you would need to do:
change the name of your service (so it isn't called SipgateService)
Add the def sipgateService as a class definition, not a method one
Does this work?
class MySMSService {
static transactional = true
def sipgateService // This will be injected from the SMS plugin
def serviceMethod() {
println "service"
def phoneNumber = 'XXXXXXXXXX'
def result = sipgateService.sendSMS(phoneNumber, 'This is my Text to send!')
result ? 'Sending Successful':'Sending failed'
println "after service"
}
}
Then, from a controller, define the link to MySMSService at class level, and call your serviceMethod method ie:
class MyController {
def mySMSService // this will be injected from your service
// then, when you want to use it (from an action)
def someAction = {
...
mySMSService.serviceMethod()
...
}
}