Controller always null in Spock unit test using Grails 2.5.1 - unit-testing

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

Related

Spock testing SpringSecurity methods

I'm trying to test the LoginController of SpringSecurity (using the spring-test version 3.1.0.RELEASE) using Spock in Grails. Specifically I'm trying to do the unit test for the following method:
def index() {
if (springSecurityService.isLoggedIn()) {
redirect uri: SpringSecurityUtils.securityConfig.successHandler.defaultTargetUrl
}
else {
redirect action: 'auth', params: params
}
}
I've tried using the following test:
void "Calling index"(){
given:
controller.springSecurityService = Mock(SpringSecurityService)
when:
controller.index()
then:
1 * controller.springSecurityService.isLoggedIn() >> false
response.redirectedUrl == '/login/auth'
when:
controller.response.reset()
controller.index()
then:
1 * controller.springSecurityService.isLoggedIn() >> true
response.redirectedUrl == '/login'
}
When I run it, I get the following error:
java.lang.RuntimeException: java.lang.ClassNotFoundException: DefaultSecurityConfig
Does anyone know why it happens and how it's solved?
Thank you in advance.

Grails 2.4.4 controller test on save() return empty model

I have a test for a PersonController, it just executes save() with no params, so an invalid person is created. It should return the invalid person in the model and show the create view. But the model is empty.
The test:
import org.junit.*
import grails.test.mixin.*
#TestFor(PersonController)
#Mock(Person)
class PersonControllerTests {
...
void testSave() {
controller.save() // creates invalid person, redirects to create
assert model.personInstance != null
assert view == '/person/create'
response.reset()
populateValidParams(params)
controller.save()
assert response.redirectedUrl == '/person/show/1'
assert controller.flash.message != null
assert Person.count() == 1
}
...
}
The controller:
class PersonController {
...
def save() {
def personInstance = new Person(params)
if (!personInstance.save(flush: true)) {
render(view: "create", model: [personInstance: personInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'person.label', default: 'Person'), personInstance.id])
redirect(action: "show", id: personInstance.id)
}
...
}
The output:
junit.framework.AssertionFailedError: Assertion failed:
assert model.personInstance != null
| | |
[:] null false
at demographic.PersonControllerTests.testSave(PersonControllerTests.groovy:43)
How can I get the right model?
Is this the expected behavior or is this a Grails bug?
The test is failing (rightly so, I believe) because of the HTTP method restrictions in the controller, i.e. the line:
static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
Set the HTTP method in the test, and the test passes:
void testSave() {
controller.request.method = 'POST'
controller.save() // creates invalid person, redirects to create
...

Migrating from Grails 1.x to 2.3.9 and fixing Spock tests to run in the process

I have a question about Spock testing the list() method in a controller. I am also migrating from Grails 1.x to Grails 2.3.9. The question is how do I get the mock object I am creating visible to the controller so that when I call list() on it it sees the mock object.
Here is the code from the controller under test:
class XxCatalogFormController {
def list() {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[dpCatalogFormInstanceList: XxCatalogForm.list(), dpCatalogFormInstanceTotal: XxCatalogForm.count()]
}
Here is the Spock test I have:
#TestFor(XpCatalogFormController)
#Mock([XxCatalogForm, DpCatalog])
#TestMixin(GrailsUnitTestMixin)
class XpCatalogFormControllerSpec extends Specification {
def 'list action: 1 dpCatalogForm'() {
setup:
mockDomain(XxCatalogForm, [dpCatalogFormInstance])
mockDomain(DpCatalog, [catalog])
params.max = 1
when:
expect:
controller.list() == [dpCatalogFormInstanceList: [dpCatalogFormInstance], dpCatalogFormInstanceTotal: 1]
where:
catalog = new DpCatalog(name: 'TestCatalog')
dpCatalogFormInstance = new XxCatalogForm(url: 'catalog_testForm.gsp',
catalog: catalog, confirmMessage: 'test', introBannerUrl: '/site/test.gsp',
successUrl: 'test.gsp', name: 'test')
}
And here is the test result:
Condition not satisfied: controller.list() == [dpCatalogFormInstanceList: [dpCatalogFormInstance], dpCatalogFormInstanceTotal: 1] | | | | | | false com.kpi.dp.catalog.XxCatalogForm : (unsaved) | [dpCatalogFormInstanceList:[], dpCatalogFormInstanceTotal:0] com.kpi.dp.catalog.XxCatalogFormController#784f6502
junit.framework.AssertionFailedError: Condition not satisfied:
controller.list() == [dpCatalogFormInstanceList: [dpCatalogFormInstance], dpCatalogFormInstanceTotal: 1]
| | | |
| | false com.kpi.dp.catalog.XxCatalogForm : (unsaved)
| [dpCatalogFormInstanceList:[], dpCatalogFormInstanceTotal:0]
com.kpi.dp.catalog.XxCatalogFormController#784f6502
at com.kpi.dp.catalog.XxCatalogFormControllerSpec.list action: 1 dpCatalogForm(XxCatalogFormControllerSpec.groovy:64)
Unable to confirm which mixin is used in the test class, modifying the test class to use #Mock should work. Use #Mock instead of mockDomain(). Expecting the class to be under test/unit.
import grails.test.mixin.*
import spock.lang.Specification
#TestFor(XxCatalogFormController)
#Mock([DpCatalog, XxCatalogForm])
class XxCatalogFormControllerSpec extends Specification {
def 'list action: 1 dpCatalogForm'() {
given:
def catalog = new DpCatalog(name: 'TestCatalog')
def dpCatalogFormInstance = new XxCatalogForm(
url: 'catalog_testForm.gsp',
catalog: catalog,
confirmMessage: 'test',
introBannerUrl: '/site/test.gsp',
successUrl: 'test.gsp', name: 'test'
)
and:
params.max = 1
expect:
controller.list() == [
dpCatalogFormInstanceList: [dpCatalogFormInstance],
dpCatalogFormInstanceTotal: 1
]
}
}

Unit test failing in Intellij 13 but passes via command line

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.

grails Unit test spock issue

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.