I have a very simple unit test that test a controller action that renders the current user as JSON. Here is my test class:
#TestFor(UserController)
class UserControllerSpec extends Specification {
def setup() {
def springSecurityService = mockFor(SpringSecurityService)
springSecurityService.demand.getCurrentUser { -> [name: "Jim"] }
controller.springSecurityService = springSecurityService.createMock()
}
def cleanup() {
}
void "test current"() {
when:
request.method = 'GET'
controller.current()
then:
response.json.name == "Jim"
}
}
Here is the controller (omitted non related code)
def springSecurityService
static responseFormats = ['json']
static allowedMethods = [current: "GET"]
def current() {
respond(springSecurityService.currentUser)
}
I am executing this test alone via grails test -unit package.class and it works via the command line:
|Running 1 unit test... 1 of 1
|Completed 1 unit test, 0 failed in 0m 11s
.
|Tests PASSED - view reports in ...\target\test-reports
This does not work via IntelliJ because springSecurityService.currentUser is always null. Here is the resulting exception:
org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Error parsing JSON
at grails.converters.JSON.parse(JSON.java:292)
at org.codehaus.groovy.grails.plugins.testing.AbstractGrailsMockHttpServletResponse.getJson(AbstractGrailsMockHttpServletResponse.groovy:97)
at massemailsystem.UserControllerSpec.test current(UserControllerSpec.groovy:28)
Caused by: org.codehaus.groovy.grails.web.json.JSONException: Missing value. at character 0 of
at org.codehaus.groovy.grails.web.json.JSONTokener.syntaxError(JSONTokener.java:470)
at org.codehaus.groovy.grails.web.json.JSONTokener.nextValue(JSONTokener.java:357)
at grails.converters.JSON.parse(JSON.java:283)
... 2 more
Any ideas?
You need to run this test case as grails test case in IntelliJ.
Related
I am new using Grails 2.5.1. I need to run some unit and integration test but I can't make them work.
My domain class is:
class Role {
String roleName
Role(String _roleName) {
roleName = _roleName
}
static constraints = {
roleName(blank: false, nullable: false)
}
String toString(){
"$roleName"
}
}
My controller class is:
class RoleController {
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond Role.list(params), model:[roleInstanceCount: Role.count()]
}
def show(Role roleInstance) {
respond roleInstance
}
def create() {
respond new Role(params)
}
...
}
Under test/unit I have the class RoleControllerSpec:
import grails.test.mixin.*
import spock.lang.*
#TestFor(RoleController)
#Mock(Role)
class RoleControllerSpec extends Specification {
def 'index action: 1 role'() {
setup:
roleInstance.save()
expect:
controller.index() == [roleInstanceList: [roleInstance], roleInstanceTotal: 1]
where:
roleInstance = new Role(roleName: "Role1")
}
def "create action"() {
setup:
controller.params.roleName = roleName
when:
def model = controller.create()
then:
model.roleInstance != null
model.roleInstance.roleName == roleName
where:
roleName = "Role1"
}
}
When I run the test with test-app -unit RoleController it give me the following exceptions:
|Configuring classpath
.
|Environment set to test
....................................
|Running without daemon...
..........................................
|Compiling 1 source files
.
|Running 2 unit tests...
|Running 2 unit tests... 1 of 2
Failure: |
index action: 1 role(accessmanagement.RoleControllerSpec)
|
Condition not satisfied:
controller.index() == [roleInstanceList: [roleInstance], roleInstanceTotal: 1]
| | | |
| null false Role1
role(RoleControllerSpec.groovy:17)
|Running 2 unit tests... 2 of 2
Failure: |
create action(accessmanagement.RoleControllerSpec)
|
java.lang.NullPointerException: Cannot get property 'roleInstance' on null object
at accessmanagement.RoleControllerSpec.create action(RoleControllerSpec.groovy:34)
|Completed 2 unit tests, 2 failed in 0m 6s
.Tests FAILED
|
Error |
Forked Grails VM exited with error
It seems that controller is null in my tests.
In the first test controller.index() is null. In the second test def model = controller.create() is not creating the object, then when I try to access model.roleInstance it cannot get the property.
Any idea would be appreciated.
Thanks!
Since you are using respond and not simply returning a map from the controller, you need to check the model property
def 'index action: 1 role'() {
setup:
Role roleInstance = new Role(roleName: "Role1").save()
when:
controller.index()
then:
model == [roleInstanceList: [roleInstance], roleInstanceTotal: 1]
}
I would suggest you read the documentation on testing controllers https://grails.github.io/grails-doc/2.5.x/guide/testing.html#unitTestingControllers
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)
}
}
...
}
I have a Grails controller that expects an XML payload.
I fetch the XML payload like this in a Grails controller.
def xmlPayload = request.reader.text
That part works fine, but I'm struggling to mock this payload in a unit test.
I've tried both of the following, but the debugger is showing 'request.reader' to be null in both approaches.
Approach #1:
void "test controller method"(){
setup:
def mockBufferedReader = Mock( BufferedReader )
mockBufferedReader.getText() >> '<hello/>'
request.getReader() >> mockBufferedReader
....
Approach #2:
void "test controller method"(){
setup:
def mockBufferedReader = Mock( BufferedReader )
mockBufferedReader.getText() >> '<hello/>'
request.metaClass.getReader = { -> mockBufferedReader }
....
'request' in a unit test is a GrailsMockHttpServletRequest, so I presumed I could mock its methods like this (3rd line of both approaches), but so far no luck.
Thanks for any ideas.
You can do:
class EchoController {
def echo () {
render (request.reader.text)
}
}
#TestFor(EchoController)
class EchoSpec extends Specification {
def "echos XML data" () {
request.XML = '<hello/>'
when:
controller.echo ()
then:
response.text == '<hello/>'
}
}
See Testing XML and JSON Requests in Unit Testing Controllers.
If you only need to provide contents for a request, then you don't need to mock anything.
def "Spock works as expected"() {
given:
def request = new GrailsMockHttpServletRequest(content: '<hello/>')
when:
def result = request.getReader().getText()
then:
result == '<hello/>'
}
One purpose of such Mock classes (as in Spring Test etc.) is to avoid explicit mocking with external libraries.
i am new to grails.
i just scaffold a domain class employee, which given below
class Employee {
String firstName
String lastName
static constraints = {
}
}
I am trying to write a unit test on list action in EmployeeController using spock. The controller is given below
class EmployeeController {
static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
def index() {
redirect(action: "list", params: params)
}
def list(Integer max) {
params.max = Math.min(max ?: 10,100)
[employeeInstanceList: Employee.list(params), employeeInstanceTotal: Employee.count()]
}
}
then i wrote a test case given below
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(EmployeeController)
class EmployeeControllerUnitSpec extends Specification {
def 'test index'() {
when:
controller.index()
then:
// httpcode ? 200
//println response (GrailsMockHttpServletResponse)
response.redirectedUrl == '/employee/list'
}
def 'test list empty'() {
when:
controller.list( 10 )
// Employee.list()
then:
model.employeeInstanceTotal == 0;
}
}
Here test case for index works correctly ,but test for list empty render some error in console.
The error decription in console is
| Running 2 spock tests... 2 of 2
| Failure: test list empty(com.test.EmployeeControllerUnitSpec)
| groovy.lang.MissingMethodException: No signature of method: com.test.Employee.list() is applicable for argument types: () values: []
Possible solutions: list(), list(java.util.Map), last(), last(java.lang.String), last(java.util.Map), first()
at com.test.EmployeeController.list(EmployeeController.groovy:15)
at com.test.EmployeeControllerUnitSpec.test list empty(EmployeeControllerUnitSpec.groovy:21)
| Completed 2 spock tests, 1 failed in 3231ms
| Tests FAILED - view reports in /mnt/hdd2/home/T-2060/workspace/testing/target/test-reports
Can any one suggest , how can resolve this issue
thankz in advance
Unit test environment will not have the domain available until it is mocked.
Use #Mock(Employee) and setup test data in Employee to test list() action.
in the controller there is an action:
def delete = {
withDomain {
it.delete()
flash.message = "${message(code: 'default.deleted.message', args: [message(code: 'chocolateBar.label', default: 'ChocolateBar'), it.name])}"
redirect action: 'list'
}
}
which can be tested in development. while in unit test, the message(..) method throws exception ( groovy.lang.MissingMethodException: No signature of method: longtest.ChocolateBarController.message() is applicable for argument types: (java.util.LinkedHashMap) values: [[code:chocolateBar.label, default:ChocolateBar]]):
public void testDelete() {
controller.params.id = '3'
controller.delete()
assert 'list'==controller.redirectArgs.action
}
After study, a mockTagLib method should be called during setup. But found no correct class name for built-in message(..). Please help.
I've solved the problem in unit controller test. like this:
//This is inside Spock test
#Shared
ResourceBundleMessageSource messageSource = null
#Shared
Closure mockMessage = {Map map ->
return messageSource.getMessage((String)map.code, (Object[])map.args, Locale.default)
}
def setupSpec(){
URL url = new File('grails-app/i18n').toURI().toURL()
messageSource = new ResourceBundleMessageSource()
messageSource.bundleClassLoader = new URLClassLoader(url)
messageSource.basename = 'messages'
messageSource.setDefaultEncoding("utf-8")
}
def setup(){
controller.metaClass.message = mockMessage
}
This code is for spock test, but main idea is also available for normal grails test.
In running phase(not test),
calling "message" in controller class results in calling "message" of ValidationTagLib class,
but they are not bind in unit test phase.
So I made almost same logic of "message" of ValidationTagLib,
and bind it(named "mockMessage") to controller.message.
With this code, you can execute "message" correctly in controller class in test.