Grails Controller Unit Test doesn't render page to response.text - unit-testing

My env configs: Java 1.7u51, Grails 2.3.7
I'm trying to assert response.text in Controller Test but it always brings "".
What's happening?
This is my UserController
class UserController {
def index() {
flash.errors = "$params.secret"
render view: "index", model: [model: params.toModel,
text: params.username]
}
}
This is /user/index.gsp file
${text}
This is my Specification
#TestFor(UserController)
class UserControllerSpec extends Specification {
def setup() {
}
def cleanup() {
}
void "test something"() {
given:
params.username = "USERNAME"
params.password = "SECRET"
params.toModel = "Model"
when:
controller.index()
then:
flash.errors
view == "/user/index"
params.username == response.text
model.model == params.toModel
}
}
And test report is:
Failure: |
test something(teste.UserControllerSpec)
|
Condition not satisfied:
params.username == response.text
| | | | |
| USERNAME | | ""
| | org.codehaus.groovy.grails.plugins.testing.GrailsMockHttpServletResponse#46f29a61
| false
| 8 differences (0% similarity)
| (USERNAME)
| (-------)
[username:USERNAME, password:SECRET, toModel:Model]

It is only the case of a template rendering where the content of the template is directly rendered to response as String. Hence response.text can only be used when a template is rendered unlike in this case where a view is being rendered.
In order to test rendering a view, GroovyPageUnitTestMixin has to be used as below:
import grails.test.mixin.TestMixin
import spock.lang.Specification
import grails.test.mixin.web.GroovyPageUnitTestMixin
#TestMixin(GroovyPageUnitTestMixin)
class UserControllerSpec extends Specification {
def controller
def setup(){
controller = testFor(UserController)
}
void "test something"() {
given:
params.username = "USERNAME"
params.password = "SECRET"
params.toModel = "Model"
when:
controller.index()
then:
flash.errors
view == "/user/index"
model.model == params.toModel
//method provided by mixin which mimics render method in
//controller. Make sure model is also passed in the map arg
//since view uses model.text
render(view: "/user/index", model: model) == 'USERNAME'
}
}
Note:
#TestFor is replaced with the Mixin in the test. Therefore, controller has to be mocked as seen in setup() using testFor() method.
render() can also be used to test template rendering with the key template similar to the usage of key view in the map argument.

Related

How to mock springSecurityService in an unit test

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)
}
}
...
}

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
]
}
}

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.

How to use VndErrorJsonRenderer in grails unit test

I'm writing controller unit tests and I'd like to test json result when creation fails.
How can I register VndErrorJsonRenderer in unit test ? I tried simply defineBeans in setup() but it doesn't work :(
import com.vividsolutions.jts.geom.Coordinate
import grails.transaction.Transactional
import org.codehaus.groovy.grails.web.servlet.mvc.GrailsParameterMap
import static org.springframework.http.HttpStatus.CREATED
import static org.springframework.http.HttpStatus.NO_CONTENT
#Transactional(readOnly = true)
class UserController {
static namespace = "v1"
static allowedMethods = [profile: 'GET', create: "POST", update: "PUT", delete: "DELETE"]
static responseFormats = ['json', 'vnd.error+json']
def springSecurityService
def geometryFactory
/**
* Saves a resource
*/
#Transactional
def create() {
User instance = createResource(params)
instance.validate()
if (instance.hasErrors()) {
respond instance.errors, view: 'create' // STATUS CODE 422
return
}
instance.save flush: true
respond instance, [status: CREATED]
}
protected User createResource(GrailsParameterMap params) {
Double x = params.double("location.x", 0)
Double y = params.double("location.y", 0)
User user = new User()
bindData(user, params, [include: ['username', 'password', 'profile.*']])
if (x > 0 && y > 0)
user.location = geometryFactory.createPoint(new Coordinate(x, y))
else
user.location = null
user.roles = []
user.roles.add(Role.findByAuthority(Role.ROLE_USER))
return user
}
}
And my test :
#Before
void setup() {
defineBeans {
vndJsonErrorRenderer(VndErrorJsonRenderer)
}
}
void "Test the create action with a non unique username"() {
User.metaClass.encodePassword = {
"aaa"
}
// Create first user
assertNotNull getValidUser().save(flush: true)
when: "The create action is executed with a username already used"
def user = getValidUser()
controller.request.addHeader("Accept", "application/vnd.error+json,application/json")
controller.request.contentType = "application/json"
controller.request.content = JsonMapperUtil.mapAsJson(user)?.getBytes()
controller.create()
then: "The response status is UNPROCESSABLE_ENTITY and the username unique error is returned"
println response.text
response.status == UNPROCESSABLE_ENTITY.value
def json = JSON.parse(response.text)
assertNull "VND format not returned", json.errors
}
I'm using grails 2.3.6 with restful controller.
Thanks
In the case you are showing where you depend on respond it would be best to test this more as an integration test so all components that may interact with respond are all wired for you.
In a unit test for what ever beans are needed in the class under test I find it easiest to directly set them on the class under test.