I have a unit test for my UserController but since upgrading to Grails 2.0, the flash variable always seems to return an emtpy map with no message.
Here are some code snippets of the UserControllerTests:
#TestFor(UserController)
#Mock(User)
class UserControllerTests {
...
void testSaveSucceeds() {
params.userName = 'Joe'
...
controller.save()
assert null != flash.message
assert '/user/list' == response.redirectedUrl
}
}
In UserController:
def save = {
def userInstance = new User(params)
if (userInstance.validate()) {
flash.message = message(code: 'default.created.message', args: [userInstance.userName ])
...
}
But my test result is as follows:
assert null != flash.message
| | |
| [:] null
false
I have tried as an integration test as well because otherwise the response was null as weill but it did not fix the flash issue. The same problem also exists with view and model.
What am I missing? Any help highly appreciated.
Regards
Jonas
EDIT:
Here's a weird scenario:
My controller has the following:
def test = {
flash.message = "Message"
}
def save = {
flash.message = "Message"
}
My Test looks like that:
void testSaveSucceeds() {
controller.save()
println ">>> ${flash}"
controller.test()
println ">>> ${flash}"
}
The output like that:
>>> [:]
>>> [message:Message]
Interesting to mention is also that the debugger in IntelliJ stops at a breakpoint in the test() action but not in save()
HOW can that be????
Regards
Jonas
For me it means that userInstance.validate() return false ie the validation failed.
Related
I could use some advice in how to mock an auto wired dependency used in a Grails unit test. I've omitted most of the unnecessary code and just given the test class and the relevant methods in the file class under test
class UserService {
def springSecurityService // spring bean
def passwordEncoder // auto wired as per
//https://stackoverflow.com/questions/33303585/spring-//security-encode-password-with- bcrypt-algorithm
.....
def passwordPreviouslyUsed(String newPassword, def userId){
def passwordExists = false
def usersPasswords = findPasswordsForUser(userId)
usersPasswords.each{ password ->
if (passwordEncoder.isPasswordValid(oldPassword, newPassword, null)) {
passwordExists = true
}
}
return passwordExists
}
.....
def findPasswordsForUser(def userId){
User foundUser = User.findById(userId)
def passwordsForUser = UserPasswords.createCriteria().list {
eq('user', foundUser)
projections{
property('password')
}
}
passwordsForUser
}
My test
class UserServiceSpec extends Specification implements DataTest, ServiceUnitTest<UserService> {
def passwordEncoder
def setupSpec() {
mockDomains User, UserPasswords
}
def setup() {
def stubPasswordEncoder = Stub(passwordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder
}
void "test for user passwordPreviouslyUsed"() {
given: "a user already exists"
setup()
service.createNewUser("testName", "testy#test.com", "Secret1234" )
//^(does some validation, then User.save())
User foundUser = User.findByEmail("testy#test.com")
foundUser.fullName == "testName"
long testUserId = foundUser.id
and: "we update the password for that user, and it to the userPasswords"
UserPasswords newUserPassword = new UserPasswords(
user: foundUser,
password: "newPassword1"
)
newUserPassword.save()
//use passwordPreviouslyUsed method to check a string with the same value as the
//previously
//updated password to check if it has already been used
when: "we check if the password has been used before"
def response = service.passwordPreviouslyUsed("newPassword1", fundsyId)
then:
response == true
}
Without stubbing or mocking this dependency, I get the error
Cannot invoke method isPasswordValid() on null object
I tried to stub password encoder and have it return true
def stubPasswordEncoder = Stub(passwordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder
But this gives an error message:
Stub in 'spock.mock.MockingApi' cannot be applied to '(java.lang.Object, groovy.lang.Closure)'
Is there any way to mock this dependency with Spock?
Stub and Mock take a class - you're giving it an instance that is null - hence the exception.
You should be able to mock it as so:
def mockPasswordEncoder = Mock(PasswordEncoder)
// note this is the class
// org.springframework.security.crypto.password.PasswordEncoder
I tried enrichelgeson's approach, and it worked!
I first imported PasswordEncoder to the test class
import org.springframework.security.crypto.password.PasswordEncoder
then implemented the normal mocking procedure. I was initially confused because they class under test just implicitly created an instance of the class by defining it.
def stubPasswordEncoder = Stub(PasswordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder
I also found another solution that didn't require mocking
service.passwordEncoder = [ isPasswordValid: { String rawPass, String salt, Null -> true } ]
Both approaches work fine. Thanks for the help!
Hi guys i am on trouble about getting the current user provided by spring.
Here's my unit test code
void "Test if adding project will sucess"() {
given:
def createProjectMock = mockFor(UserService)
createProjectMock.demand.createNewProject { Map projectMap ->
return true
}
controller.userService = createProjectMock.createMock()
when: "saveProject is execute"
controller.saveProject()
then: "page will to the list to view the saved project"
response.redirectedUrl == '/user/index2'
}
Here's my controller
def saveProject(ProjectActionCommand projectCmd) {
def currentUser = springSecurityService.currentUser
if (projectCmd.hasErrors()) {
render view: 'createProject', model: [projectInstance: projectCmd, user:currentUser]
} else {
def getProjectMap = [:]
getProjectMap = [
projectName: params.projectName,
user: currentUser
]
def saveProject = userService.createNewProject(getProjectMap)
if (saveProject) {
redirect view: 'index2'
} else {
render 'Error upon saving'
}
}
}
And here's my service
Project createNewProject(Map projectMap){
def createProject = new Project()
createProject.with {
projectName = projectMap.projectName
user = projectMap.user
}
createProject.save(failOnError:true, flush: true)
}
And i always getting this error:
Cannot get property 'currentUser' on null object.
Hope you can help me. Thanks
Cannot get property 'currentUser' on null object.
means that you haven't mocked springSecurityService. Let's do it in setup section (I assume it may be useful also in other methods in this class):
def springSecurityService
def setup() {
springSecurityService = Mock(SpringSecurityService)
controller.springSecurityService = springSecurityService
}
At this point your code is going to work. However remember that you can always mock also the actual logged user and test it at any point:
User user = Mock(User)
springSecurityService.currentUser >> user
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
...
I have been struggling with this for quite sometime and it seems like a common problem, with no solution that worked for me.
What I am trying to do is test the controller which calls a save(), and inside the save() the method calls a service to save the employee with the given command objects.
This is how the code looks like:
def save(EmployeeInputCommand employeeCommand, AddressCommand addressCommand) {
if (addressCommand.address.validate()) {
println "Validated address input"
employeeManagerService.save(employeeCommand, addressCommand)
println "Employee should be saved"
println "------------------------->"
println "${employeeGetterService.each {errors}}"
flash.message = "Employee has been successfully created!"
redirect(action: "index")
} else {
println "Addrescommand didnt validate: ${addressCommand.errors}"
flash.message = "Please input valid data!!!"
redirect(action: "create", model: [employee: employeeCommand, address: addressCommand])
}
}
The service contains this:
def save(EmployeeInputCommand employeeCommand, AddressCommand addressCommand) {
def employee = new Employee(employeeCommand.properties)
employee.address = addressCommand.address
for (id in employeeCommand.s) {
employee.addToSkills(Skill.get(id))
}
if (employee.validate()) {
employee.address.save()
employee.save()
}
else {
return false
}
}
I know this works when I try to actually save the employee in my application, but during the Unit Test process nothing happens.
My Test:
def "Create an Employee with good params"() {
given: "A valid employee command object"
def employeeCommand = new EmployeeInputCommand(
name: "John",
surname: "Smith",
birthdate: Date.parse("yyyy-MM-dd", "1992-06-08"),
salary: 21000,
s: [1, 3, 5]
)
and: "Also a valid address command object"
def addressCommand = new AddressCommand(
street: "Green Alley",
houseid: "42",
city: "Odense",
county: "Fyn",
postCode: "5000 C"
)
expect:
addressCommand.validate()
!Employee.findByNameAndSurname("John", "Smith")
when: "Saving employee"
request.method = "POST"
controller.save(employeeCommand, addressCommand)
Employee employee = Employee.findByNameAndSurname("John", "Smith")
println Employee.list()
then: "The employee is created, and browser redirected"
Employee.count == 4
employee
employee.skills.size() == 3
employee.address.postCode == "5000 C"
}
The test is failing with a null error when the controller.save() is called. I have spend too much time trying to solve this, which has all been in vain
This is the output screenshot
I don't believe testing a controller should cover the service logic. I'd mock the service, if I were to write a unit test for a controller, and test the service separately in its own unit/integration tests.
For example:
/*
* in controller spec
*/
def setup() {
def serviceMock = new MockFor(EmployeeManagerService)
serviceMock.ignore('save') { ec, ac ->
Employee.buildWithoutSave().save(validate: false)
}
serviceMock.use {
controller.employeeManagerService = new EmployeeManagerService()
}
}
def 'testFoo'() {
when:
// ...
controller.employeeManagerService.save()
// ...
then:
// ...
}
Note that the code is using the excellent Build Test Data plugin.
I think the issue is with service in unit testing. In unit testing, you need defineBeans closure to use spring beans-
void "test the filter"() {
setup:
defineBeans {
employeeManagerService(EmployeeManagerService) { bean ->
bean.autowire = true
}
}
when:
...
then:
...
}
Ref# Inject Services in Grails Unit Test
You need to perform your tests as Integration Tests, not Unit Tests. Integration testing is needed when you're testing database-related logic (CRUD ops), not just mocking the CRUD ops which is what unit tests do.
Am getting the following error message when testing the controller - see below for code.
How can I correct this?
When I invoke the service method from the controller (run-app) and it works fine.
Exception:
groovy.lang.MissingMethodException: No signature of method:
grails.test.GrailsMock.isOk() is applicable for argument types:
(java.lang.String) values: [H] at ...VControllerSpec.test
something(VControllerSpec.groovy:)
class: VControllerSpec
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(VController)
#Mock(VService)
class VControllerSpec extends Specification {
void "test something"() {
given:
def vServiceMock = mockFor(VService)
vServiceMock.demand.isOk { String yeah -> return true }
controller.vService = vServiceMock.createMock()
when:
def isO = vServiceMock.isOk("H")
then:
isO == true
}
}
class:VService
import grails.transaction.Transactional
#Transactional
class VService {
def isOk = { String yeah ->
def isO = false
return isO
}
}
Thanks,
Steve
Assuming there is an action in VController as:
def myAction() {
vService.isOk('Hello')
}
below test should pass
void 'test service'() {
given:
def vServiceMock = mockFor(FormatService)
vServiceMock.demand.isOk { String yeah -> return true }
controller.vService = vServiceMock.createMock()
when:
def isO = controller.myAction()
then:
isO == true
}
There are few things to optimize here including using a method isOk instead of a closure as best practices.
One is not expected to test a method which is being mocked. When we mock a method, we just assume its implementation is correct and has already been tested (in some other unit test). The purpose of mocking is to limit our focus of testing to limited lines of code (mostly commonly one method), in your case the your controller action. So the above test case could have been written as:
Assuming your action is like this:
def myAction(){
[iso: vServiceMock.isOk()] // assuming isOk returns boolean true
}
void "test myAction"() {
given:
def vServiceMock = mockFor(VService)
vServiceMock.demand.isOk { String yeah -> return true }
controller.vService = vServiceMock.createMock()
when:
def model = controller.myAction()
then:
model.iso //you can skip comparison with true
}