Grails how to perform unit test on exceptions - unit-testing

I am trying to test that the account expired exception.
def authfail() {
String msg = ''
def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]
// println("print exception: ${exception} | ${session} | ${springSecurityService.getCurrentUser()}")
if (exception) {
if (exception instanceof AccountExpiredException) {
msg = message(code: 'springSecurity.errors.login.expired')
}
else if (exception instanceof CredentialsExpiredException) {
msg = message(code: 'springSecurity.errors.login.passwordExpired')
}
else if (exception instanceof DisabledException) {
msg = message(code: 'springSecurity.errors.login.disabled')
}
else {
msg = message(code: 'springSecurity.errors.login.fail')
}
}
if (springSecurityService.isAjax(request)) {
render([error: msg] as JSON)
}
else {
flash.message = msg
redirect action: 'auth', params: params
}
}
I tried writing the test case above before realising i was stuck as I have no idea how to trigger an expired login so that the unit test criteria of having the AccountExceptionExpired exception thrown be fulfilled.
void "test authFail"() {
when:
session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new AccountExpiredException( 'This account has expired' )
def logexp = controller.authfail()
then:
logexp == 'springSecurity.errors.login.expired'
when:
session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new CredentialsExpiredException( 'This credentials have expired' )
def passexp = controller.authfail()
then:
passexp == 'springSecurity.errors.login.passwordExpired'
when:
session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new DisabledException( 'The account is disabled' )
def logdis = controller.authfail()
then:
logdis == 'springSecurity.errors.login.disabled'
when:
session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new UnsupportedOperationException( 'Sorry, we were not able to find a user with that username and password.' )
def logfail = controller.authfail()
then:
logfail == 'springSecurity.errors.login.fail'
when:
controller.authfail()
then:
1 * springSecurityService.isAjax( _ ) >> true
controller.response.json == [error :'springSecurity.errors.login.fail']
}
}

The following will test the majority of your method:
import grails.plugin.springsecurity.SpringSecurityService
import grails.test.mixin.TestFor
import org.springframework.security.authentication.AccountExpiredException
import org.springframework.security.authentication.CredentialsExpiredException
import org.springframework.security.authentication.DisabledException
import org.springframework.security.web.WebAttributes
import spock.lang.Specification
#TestFor(YourController)
class YourControllerSpec extends Specification {
def springSecurityService = Mock( SpringSecurityService )
void setup() {
controller.springSecurityService = springSecurityService
}
void "test authFail"() {
given:
session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new AccountExpiredException( 'This account has expired' )
when:
controller.authfail()
then:
flash.message == 'springSecurity.errors.login.expired'
when:
session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new CredentialsExpiredException( 'This credentials have expired' )
controller.authfail()
then:
flash.message == 'springSecurity.errors.login.passwordExpired'
when:
session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new DisabledException( 'The account is disabled' )
controller.authfail()
then:
flash.message == 'springSecurity.errors.login.disabled'
when:
session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new UnsupportedOperationException( 'Bad stuff' )
controller.authfail()
then:
flash.message == 'springSecurity.errors.login.fail'
when:
controller.authfail()
then:
1 * springSecurityService.isAjax( _ ) >> true
response.json == [error :'springSecurity.errors.login.fail']
}
}
The session is just a map to which we add a key of the string constant and a value of the exception.
For all tests bar the final one we're falling through to your final else block, in the final test we return true for `isAjax'.

While this is not Grails, it is SpringBoot 2.0.
If the failureHandler is exposed as a bean, one can just spy on it.
#SpyBean
AuthenticationFailureHandler failureHandler;
and simply verify that the exception has been thrown
Mockito.verify(failureHandler).onAuthenticationFailure(
any(),
any(),
any(AccountExpiredException.class)
);
A simple test can look like this:
#Test
public void accountExpired() throws Exception {
doReturn(user
.username("expired")
.accountExpired(true)
.build()
).when(userDetailsService).loadUserByUsername(any(String.class));
mvc.perform(
MockMvcRequestBuilders.post("/login")
.param("username", "expired")
.param("password", "password")
)
.andExpect(status().is4xxClientError())
.andExpect(unauthenticated())
;
Mockito.verify(failureHandler).onAuthenticationFailure(
any(),
any(),
any(AccountExpiredException.class)
);
}
All samples at https://github.com/fhanik/spring-security-community/

Related

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

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.

Grails: criteria unit testing fails

I'm trying to test the following action:
def search = {
//keep these values so we can rerender on the filters
def cal=Calendar.getInstance()
def startCal=Calendar.getInstance()
def endCal=Calendar.getInstance()
startCal.set(Calendar.HOUR, 0);
startCal.set(Calendar.MINUTE, 0);
startCal.set(Calendar.SECOND, 0);
endCal.set(Calendar.HOUR, 0);
endCal.set(Calendar.MINUTE, 0);
endCal.set(Calendar.SECOND, 0);
startCal.set(params.int('startDate_year')?:Calendar.getInstance().get(Calendar.YEAR) -10,params.int('startDate_month')?params.int('startDate_month')-1:Calendar.JANUARY,params.int('startDate_day')?:1)
def startDate=startCal.getTime()
endCal.set(params.int('endDate_year')?:Calendar.getInstance().get(Calendar.YEAR),params.int('endDate_month')?params.int('endDate_month')-1:cal.get(Calendar.MONTH),params.int('endDate_day')?:cal.get(Calendar.DATE))//cal.get(Calendar.MONTH) cal.get(Calendar.DATE)
def endDate=endCal.getTime()
flash.customer = params.customer
flash.siteName = params.siteName
flash.review_filter=params.review_filter
flash.message=""
flash.startDate=""
flash.endDate=""
if(params.startDate_year){
flash.startDate=startDate
}
if(params.endDate_year){
flash.endDate=endDate
}
flash.success_filter=params.success_filter
//Validation Check
if(endDate>new Date()|| startDate>new Date()){
flash.message=message(code: "date.endDate.greaterror")
render(view:'list',model:[ auditInstanceList: null,auditInstanceTotal:0 ])
return
}
if(startDate>= endDate){
flash.message=message(code:"date.endDate.aftererror")
render(view:'list',model:[ auditInstanceList: null,auditInstanceTotal:0 ])
return
}
if(!params.max) {
params.max = grailsApplication.config.AUDIT_SEARCH_MAX
}
def query
def criteria = Audit.createCriteria()
query = {
and{
//between("dateCreated",startDate,endDate)
// le("dateCreated",endDate)
if(params.customer){
ilike("customerName", params.customer + '%')
}
if(params.siteName){
ilike("siteName", params.siteName + '%')
}
max:params.max
offset:params.offset
if(startDate){
ge("dateCreated",startDate)
}
if(endDate){
le("dateCreated",endDate)
}
if(params.review_filter=='true' ||params.review_filter=='false'){
eq("reviewed", params.review_filter=='true' )
}
if(params.success_filter=='0'||params.success_filter=='1'){
eq("success",params.success_filter.toInteger())
}
}
}
def results = criteria.list(params, query)
render(view:'list', model:[ auditInstanceList: results,auditInstanceTotal:results.totalCount ])
[auditInstanceList: results, auditInstanceTotal:results.totalCount]
}
I used #Mock(Audit) annotation but still when I run the test I got null pointer exception. I read that there is problems with criteria testing and grails 2.1.0. This is my Test which fails on the last assertion(It returns null).
def populateValidParams(params) {
assert params != null
def now = new Date();
def byesterday = new Date(now.getYear(), now.getMonth(), now.getDate()-2);
println("byesterday"+byesterday)
params["customerName"] = 'sky'
params["siteName"] = '123456'
params["userName"] = 'user'
params["controllerName"] = 'control'
params["actionName"] = 'action'
params["arguments"] = 'arguments'
params["success"] = 0
params["msg"] = 'message'
params["dateCreated"] = byesterday
params["reviewed"] = false
}
void testSearch() {
populateValidParams(params)
def audit=new Audit(params)
audit.save(flush:true,failOnError: true)
def now = new Date();
def yesterday = new Date(now.getYear(), now.getMonth(), now.getDate()-1);
//Search non existing customer
params.customer='ks'
params.siteName=''
controller.search()
assert model.auditInstanceTotal == 0
//Search case insensetive
params.customer='SK'
params.siteName=''
params.endDate_year=''
controller.search()
assert model.auditInstanceTotal == 1
}
Any ideas How to solve this?

Metaclass delete domain method in Grails 2 unit test

I am trying to test the throwing of an exception in my unit test. I tried to metaclass the delete method, but it doesn't want to stick. Can you tell from the code what I'm doing wrong?
Unit Test code:
#TestFor(ProductController)
#TestMixin(DomainClassUnitTestMixin)
class ProductControllerTests {
void testDeleteWithException() {
mockDomain(Product, [[id: 1, name: "Test Product"]])
Product.metaClass.delete = {-> throw new DataIntegrityViolationException("I'm an exception")}
controller.delete(1)
assertEquals(view, '/show/edit')
}
Controller action code:
def delete(Long id) {
def productInstance = Product.get(id)
if (!productInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'product.label', default: 'Product'), id])
redirect(action: "list")
return
}
try {
productInstance.delete(flush: true)
flash.message = message(code: 'default.deleted.message', args: [message(code: 'product.label', default: 'Product'), id])
redirect(action: "list")
}
catch (DataIntegrityViolationException e) {
flash.message = message(code: 'default.not.deleted.message', args: [message(code: 'product.label', default: 'Product'), id])
redirect(action: "show", id: id)
}
}
When I run the test, productInstance.delete(flush: true) does not throw the exception I'm expecting. Instead it redirects to action: "list". Does anyone know how to override the Product.delete() method so I can force the exception?
You're mocking delete without any arguments, but your controller calls delete(flush: true). Try mocking out delete(Map) like this:
Product.metaClass.delete = { Map params ->
throw new DataIntegrityViolationException("...")
}

Grails 2.0: Issue testing flash.message

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.