I have created a brand new Grails 4.0.0 app and created a domain / controller using the grails cmd. I've also created a simple service that returns "Hello World" to the controller, which then renders this to the screen. However I get "Cannot invoke method on null object" when trying to call the service method - seems like the dependency injection isn't working properly.
I've tried declaring the service using "def", I've also tried declaring by class name - neither of which seem to work.
package uk.org.pmms
import grails.gorm.transactions.Transactional
#Transactional
class HelloWorldService {
def hello() {
return "Hello World"
}
}
package uk.org.pmms
class ClientController {
//static scaffold = Client
def helloWorld
def show(Long id){
Client clientInstance = Client.get(id)
respond ("client": clientInstance, "message": helloWorld.hello())
}
}
I expect the controller to return the clientInstance data and a string "Hello World" which are displayed on a GSP page.
When I remove the "message:" part of the respond statement it displays the client information correctly so it is definitely just the service call that is the problem.
The name of the bean created for your service would be helloWorldService
class ClientController {
def helloWorldService // <--- corrected bean name for auto wire by name.
def show(Long id){
Client clientInstance = Client.get(id)
respond ("client": clientInstance, "message": helloWorldService.hello())
}
}
Related
I recently posted a question about multiple data sources. Things were going well until I hit this issue:
Controller
def doSomething() {
def user=userService.getCurrentUser()
}
Service
class UserService {
def getCurrentUser() {
def principal = springSecurityService.principal
String username = principal.username
return find(username)
}
def find(String user) {
return User.find{username==user}
}
}
This had been working previously on single DataSource but now with both enabled I see this on the browser:
Error 500: Internal Server Error URI /xxx/xxx Class
org.springframework.beans.factory.NoUniqueBeanDefinitionException
Message No qualifying bean of type
[org.springframework.transaction.PlatformTransactionManager] is
defined: expected single matching bean but found 3:
transactionManager,transactionManager_countrycity,$primaryTransactionManager
Okay this is now resolved.
I think I found the issue: under grails 3 with multiple data sources if you have this import :
import org.springframework.transaction.annotation.Transactional
You will run into the above problems:
If you how ever have :
import grails.transaction.Transactional
things will work as expected. I hadn;t paid attention and let ide choose wrong declaration
I want to use g.formatNumber in service, I have tried a below method, Which i got online. This is not working, its giving me the error "Cannot invoke method formatNumber() on null object", The code is below
import org.springframework.beans.factory.InitializingBean
class MyService implements InitializingBean {
boolean transactional = false
def gspTagLibraryLookup // being automatically injected by spring
def g
public void afterPropertiesSet() {
g = gspTagLibraryLookup.lookupNamespaceDispatcher("g")
assert g
}
def getFormattedNumber(){
def number = g.formatNumber(number: 5000,234 , type: "number" , maxFractionDigits: 2)
return number
}
}
How to do this.
I want to use g.formatNumber in service
Rather than jumping through the hoops you need to use a taglib within a service, it would be simpler to just use java.text.NumberFormat directly
NumberFormat format = NumberFormat.getNumberInstance()
format.maximumFractionDigits = 2
def number = format.format(5000.234)
If the service method is being called from a web request handling thread then you may wish to use the LocaleContextHolder to get the correct locale for the current web request, rather than just using the server's default.
This should work
def g = grailsApplication.mainContext.getBean('org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib');
You will of course need grailsApplication injected by defining it ala
def grailsApplication
I have many working/passing functional geb/spock tests (each extending GebReportingSpec) that are testing a web application with test data all created from the BootStrap.groovy at the beginning of the functional test suite.
I want to move the test data creation into startup() / teardown() methods within each Spec, well actually I wanted to have them inherit it from a base class but apparently the StepWise has issues with inheritance.
So, at present each of my test spec classes look something like:
#Stepwise
class ExampleSpec extends GebReportingSpec {
def "valid root user logs in"() {
given: "I am at the login page"
to LoginPage
when: "I enter root's credentials"
username = "root"
password = "password"
and: "I click the login button"
loginButton.click()
then: "I am logged in and directed to the welcome page"
at WelcomePage
}
}
Now, my problem is that I can't seem to create a new test (above the first test) that can create test data. Without having a valid given/when/then statement the test doesnt appear to be executed and calling a method from within the existing test also doesnt appear to work. I have looked into the grails-remote-control plugin to help me and I believe this will allow me to successfully envoke closures to setup data but I am not sure on the best mechanism for calling this from within the GebReportSpecs (or some abstract parent).
Below is a brief outline of the kind of thing I want to be able to do, either by making 'setupData()' the first test or by calling that method from within a test... Neither appears to work.
def remote = new RemoteControl()
def setupData() {
def id = remote {
def ShiroUser user = new ShiroUser(username: "root", ...)
user.save()
user.id
}
println(id)
}
.... Tests then follow
Are there any annotations like #before etc that can force these methods to be invokved?
Any suggestions are appreciated.
Solution:
I have accepted dmahapatro's response below at the correct answer, but have also provided an example of my final solution below for those who may find it useful.
(Untested)
GebReportingSpec extends GebSpec which ultimately extends spock.lang.Specification which has Fixture Methods.
You can use them like:
#Stepwise
class ExampleSpec extends GebReportingSpec {
def setupSpec(){
super.setupSpec()
//setup your data
}
def cleanupSpec(){
super.cleanupSpec()
//I do not think you would need anything else here
}
def "This is test 1"(){
}
def "This is test 2"(){
}
}
You cannot use setup as one of your test method because the sate is not maintained for a single test case. It goes like this:-
setup called -> test1 -> teardown called
setup called -> test2 -> teardown called
setup called -> test3 -> teardown called
.........
## Solved ##
Thanks to dmahapatro (and erdi). I specifically glossed over setupSpec() and cleanup() as they are private in GebReportingSpec.
Just for completion sake I am going to post a simplified version of my final solution using the grails remote control plugin just in-case it helps anyone else. The only thing to note is that the setup/teardown appears to be called once per Spec, not before each test. Which for me is actually preferably as my test data is quite complex and takes time to be created. So you have a set of test data from the Spec which is modified through the tests in the Spec and then finally cleared down before your next Spec is executed.
#Stepwise
class TestDataBaseSpec extends GebReportingSpec {
protected void createTestUsers() {
def remote = new RemoteControl()
def created = remote {
def createUser = { name, roles, pwHash ->
def user = new ShiroUser(username: name, passwordHash: pwHash, passwordSetDate: new Date())
roles.each { user.addToRoles(it) }
user.save(failOnError: true)
return user
}
createUser("root", [ShiroRole.findByName("base_user")], pwHash)
// .. repeat for more
}
}
protected void deleteTestUsers() {
def remote = new RemoteControl()
def created = remote {
ShiroUser.findAll().each {
it.delete(flush: true)
}
return true
}
}
}
#Stepwise
class ExampleSpec extends TestDataBaseSpec {
def setupSpec() {
super.createTestUsers()
}
def cleanupSpec() {
super.deleteTestUsers()
}
def "valid root user logs in"() {
given: "I am at the login page"
to LoginPage
when: "I enter root's credentials"
username = "root"
password = "password"
and: "I click the login button"
loginButton.click()
then: "I am logged in and directed to the welcome page"
at WelcomePage
}
}
I have a very simple Grails Service:
class UserService {
def returnHi() { return "Hi" }
}
I'm trying to get access to the service in an integration test, like this:
def testService() {
UserService userService
assertEquals( "Hi", userService.returnHi() )
}
Why do I get the failure:
java.lang.NullPointerException: Cannot invoke method returnHi() on null object?
Thanks for your time
It's enough to put 'def userService' as your class field instead of putting in inside of the method. In integration tests, beans are injected the same as in controllers, services and other beans.
Do something like:
class MyTests {
def userService
void serviceTest(){
assert userService.returnHi(), 'Hi'
}
}
P.S. Make sure the name of the service is correct and written in camelCase.
Add the following lines to the integration test file:
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
def userService = AH.application.mainContext.userService
as described here: Service is not getting injected into Grails domain class , Grails 2.0.0 and 2.0.3
Hi there i am still very new to grails and I have not been able to figure out why this is happening.
I have a domain class:
package scheduler
class Client {
String name
static constraints = {}
}
And a controller:
package scheduler
class AdminController {
def create() {
def client = new Client(name:"John")
println client
}
}
Currently I am always getting null for client. Originally the above was a little more complex on the domain class side but I systematically dumbed it down to see if it was a problem there. I still can not get the above working.
The output is always
scheduler.Client : null
Please let me know if I need to provide anymore information.
It's not null, that's just the default output of the toString method that Grails adds. It prints the class name and the id. Since you haven't saved the instance, the id is null. If the instance was null the output would have been null, not scheduler.Client : null
If you want to see the data in the instance, use the Groovy dump() method, e.g.
def client = new Client(name:"John")
println client.dump()
You could also add a toString method that includes the name attribute, e.g.
package scheduler
class Client {
String name
String toString() { name }
}