I was trying to write a unit test case for the action 'passwordActive' in 'LoginController'. But i am not sure which all things to be mocked and how to do that.
def passwordActive={
log.error("Entering into Password Active")
println "Password Active -- Params = "+params
String contextPath = request.contextPath
String username = null
User user = null
try{
if (params.username != null && params.username.trim().length() > 0) {
username = params.username
user = User.findByUsername(username)
}
if (user == null) {
log.error("USER not found.")
flash.errorLogin = "Username is incorrect"
redirect(action:"auth")
return
}else if(user.password != params.password){
log.error("USER password is wrong..")
flash.errorLogin = "Please enter valid password.."
redirect(action:"auth")
return
}else if (!user.active) {
log.error("USER is inactive.")
flash.errorLogin = "User in Inactive"
redirect(action:"auth")
return
}
session["userid"] = user.id
String userRole = user.authority.authority
String url = contextPath + "/" + userRole
println "URL = "+url
redirect(url: url, method: 'post', params: params)
return
}
catch(Exception e){
println e.printStackTrace()
}
log.error("Exit into Password Active")
}
i want to write a test case where username does not exist. in that case flash message should be having the message "username incorrect" and i can assert it.
Here what all things should be mocked and how to do that. Could you please explain the working with a small example (preferably related with above code so that it would be easy for me to understand)
Writing your own security implementation is a terribly bad idea. I suggest you take a look at how the Spring Security plugin does it for educational purposes, then just use that plugin - it is mature, flexible and easy to set up.
For said educational purposes, let's see how we could mock above code using an approach that is loosely along the lines of how Spring Security does it: You would move the user lookup into a service/bean as controllers aren't meant to contain any 'business' logic. Say something like this:
class UserProviderService {
User findUser(String username) {
User.findByUsername(username)
}
}
In the controller you would injected it as a dependency (def userProviderService) on which you would then call your own function, e.g. userProviderService.findUser(username). During normal operation Grails would inject your UserProviderService into the controller while in unit tests you can mock it however you like (e.g. controller.userProviderService = [ findByUsername: { -> return null } ] if you for example wanted to test the controller response for an unknown user.
Similar for password comparison you would use a passwordService that would implement password hashing, comparison and so on and you could mock its response before every test. Note that this stuff is again not trivial if you care about security and very easy to mess up so again: don't do it yourself!
The way you structure your code determines how easy or hard it will be to test it. Sure you could mess with User.metaclass and override methods left and right, but that approach usually proves very fragile. By splitting up your code you can make your life easier! How to structure your code so it is testable is definitely something you learn by experience so keep thinking about it!
Related
I am new in unit-testing in Grails application using Spock. However I would like to ask the following question. Lets say I want to run a test for the following function testfun.
class TestFun{
boolean testfun(long userId, long orderId){
User user = User.findByUserId(userId)
if(user == null)return false
Order order = Order.findByUserAndId(user, orderId)
HashMap<String, Object> = orderContent
orderContent= order.orderContent // the order has an element orderContent for storing the elements that one user orders
if(orderContent!=null){
orderContent.put("meal",1)
order.orderContent = orderContent
return true
}
return false
}
}
The corresponding unit test in that case would be:
class TestFun extends Specification {
def setup() {
GroovySpy(User, global: true)
GroovySpy(Order, global: true)
}
def "test funtest"() {
User user = new User(2).save()
Order order = new Order(3).save()
when:
service.testfun(2,3) == result
then:
2*User.findByUserId(2) >> Mock(User)
1*Order.findByUserAndId(_ as User, 1)>> Mock(Order)
result == true
}
}
However, I think that I have to mock the order.orderContent and I do not know how to mock it. Right now the test fails, because the orderContent is null so the testfun returns false.
Can anyone help me on that?
There are several things going on here, and hopefully fixing them will help you get the test running and passing.
I can't recall for certain, but I believe GroovySpy is an old feature, that isn't used with Spock tests. Instead of using that to mock a domain class, you should be using the #Mock annotation before the class definition, to specify which domain classes you would like to mock.
While you can mock the domain classes, you'll also need to actually populate the in-memory database with those objects, either in a setup: block, or in a setup() method, if they are needed for multiple tests.
You mention creating mocks, and using the Spock Mock() method will create a mock of that object, but you don't want to use that for domain objects. It is more typically used for service classes that will be manually injected into your test class.
When saving a mock domain class, I would suggest including the parameters flush: true, failOnError: true, that way any validation that fails will be indicated immediately and on the appropriate line. Without this, you can get some strange errors to occur later in the tests.
I don't know what you're doing in the when: block, but you should not be doing an assertion with == at that point, do all of those in the then: block.
Given all of this, I think you test class should look more like this:
#Mock([User, Order])
class TestFun extends Specification {
def setup() {
// This object will have an id of 1, if you want a different id, you can either specify it, or create more objects
User user = new User(first: "john", last: "doe").save(flush: true, failOnError: true)
new Order(user: user).save(flush: true, failOnError: true)
}
def "test funtest"() {
User user = new User(2).save()
Order order = new Order(3).save()
when:
boolean result = service.testfun(1, 1)
then:
// Optionally you can just use "result" on the next line, as true will be assumed
result == true
}
}
I have the following piece of code inside a service class named OrderService in Groovy on Grails. I want to make a unit test for this class. User and Order are domain classed. A user has many orders.
boolean testfun(long userId, lond orderId){
User user = User.findByUserId(userId)
if(user == null)return false
Order order = Order.findByUserAndId(user, orderId)
if(order == null)return false
return true
}
The unit test that I am trying to write is the following (using Spock):
#TestFor(OrderService)
#Mock([User, Order])
class OrderServiceSpec extends Specification{
def "test funtest"() {
User user = new User(2)
Order order = new Order()
order.metaClass.id = 3// I want to assign the id of the order in domain
order.save()
user.addToOrders(order)
user.save()
expect:
service.testfun(2,3) == true
}
}
However this test fails because the order is null. Can anyone help me?
Another question is: is this test a unit test? or should I write an integration test in grails?
It depends on what you're actually trying to test, but this can be a unit test—I'd just recommend modifying it a little bit to isolate only the service method that you're interested in testing. You're not looking to test the domain classes at all, so it's best to mock/stub the behavior that you need from them to test the service functionality.
A good way to do this is with Spock's support for interaction based testing via mock objects. Basically we specify that when the service's testfun() method is called, we expect User.findById() to be called once and Order.findByUserAndId() to be called once as well. Spock then allows us to stub out each method call so that we specify what we want the method to return. When we run the test, the stub will be used, not the actual GORM method.
Some complexity lies with stubbing out static methods (like GORM methods), but you can use a GroovySpy to get the job done.
Also, I'm assuming you meant to use User.findById() instead of User.findByUserId()?
Something along these lines should work for you:
def "test funtest"() {
setup:
// Global so that it replaces all instances/references of the
// mocked type for the duration of the feature method.
GroovySpy(User, global: true)
GroovySpy(Order, global: true)
when:
def result = service.testfun(2,3)
then:
// No need to return real objects, so use a mock
1 * User.findById(2) >> Mock(User)
1 * Order.findByUserAndId(_ as User, 3) >> Mock(Order)
result == true
when:
result = service.testfun(2,3)
then:
1 * User.findById(2) >> null
result == false
}
Note that we've isolated the service method. Any collaborating objects (User and Order) are only being interacted with via stubs and we can test the functionality of the service method without worrying about GORM at all.
We have a simple operator object, which uses spring security to encode the password thusly:
class Operator
transient springSecurityService
def siteService
String username
Site site
String password
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
}
Now we want to validate that the password matches a regexp which is defined at runtime. Operators can be created via the UI (i.e. standard CRUD forms). We have a different regexp for each site. The fact that the password gets overwritten with an ecoded one, and we should not test that, makes it more challenging.
Attempt 1: do the validation in the encodePassword():
def beforeInsert() {
protected void encodePassword() {
String regexp = siteService.getInheritedValue(site, "operatorPasswordRegexp")
if (!password.matches(regexp) {
thrown new RuntimeException "invalid password format"
}
password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
}
}
This partially works, in that it stops passwords which don't match a regexp found at runtime being created. The problem is it throws an exception, which generates a 500 error page, when we want the operator edit form to highlight the password field with a nice friendly validation message.
Attempt two, using a custom validator
static constraints = {
password password: true, blank:false, validator: { val, obj ->
if (obj.isDirty(val)) {
return true
}
String regexp = obj.siteService.getInheritedValue(obj.operates, "operatorPasswordRegexp")
if (regexp != null && regexp != "") {
return val.matches(regexp)
}
return true
}
This appears to work, but the save always fails silently. It took me some time to realise why - when you do this:
operator.password="valid1"
opertor.save(failonError:true)
No errors are thrown. Even if you remove failonError, and check the return value,its always null (no errors). BUT IT DOES NOT SAVE THE OPERATOR.
The problem is that the beforeInsert is updating the password to an encoded version which does not pass the validator of course (and isnt supposed to), the validator says no at this point, and the save silently fails. I.e. the validiator is being called twice for a single save.
The question is, how to I get the beforeInsert() code to NOT call the validator, or the validator to ignore being called from beforeInsert?
You can achieve your task using both approaches.
1: Do the validation in the encodePassword(): Instead of throwing the exception, add an error to instance. I think your encodePassword() function is in your same domain, so get errors object associated to it using this.errors. ex:
this.errors.rejectValue("password", "user.password.pattern.error")
There are different rejectValue methods, this one accepts the field name and the message code defined in your message.properties file.
2: Custom Validator:
isDirty() is not a static method, call it using the obj provided in the custom validator. isDirty() accepts the property name to be checked for dirtiness not its value.
obj.isDirty(PropertyName)
constraints is a static block, it would not be able to directly access your service. You need to inject your service using static context.
static SiteService siteService;
I would recommend to do it using custom validator.
I have a several grails controllers that I generated and modified slightly. I'm working with the generated unit tests and getting them to pass, but I think I'm doing it the hard way. This is what I have.
package edu.liberty.swiper
import grails.test.mixin.*
import org.junit.*
#TestFor(AttendanceController)
#Mock([Attendance, Location, Reason, Person, LocCapMode, GuestContactMode, UserAccount])
class AttendanceControllerTests {
def location
def reason
void setUp() {
def capMode = new LocCapMode(description: "loc cap mode", username: "testuser").save(failOnError: true)
def guestMode = new GuestContactMode(description: "Guest Contact Mode", username: "testuser").save(failOnError: true)
location = new Location(description: "foo", locCapMode: capMode, username: "testuser", guestContactMode: guestMode).save(failOnError: true)
reason = new Reason(description: "test reason", username: "testuser").save(failOnError: true)
def person = new Person(firstName: "John", lastName: "Smith", lid: "L12345678", mi: "Q", pidm: 12345).save(failOnError: true)
def userAccount = new UserAccount(pidm: 12345, username: "testuser").save(failOnError:true)
}
def populateValidParams(params) {
assert params != null
params.personId = '12345'
params.username = "testuser"
params["location.id"] = location.id
params["reason.id"] = reason.id
params.timeIn = new Date()
}
void testIndex() {
...
}
void testList() {
...
}
void testCreate() {
...
}
void testSave() {
controller.save()
assert model.attendanceInstance != null
assert view == '/attendance/create'
response.reset()
populateValidParams(params)
controller.save()
assert response.redirectedUrl == '/attendance/show/1'
assert controller.flash.message != null
assert Attendance.count() == 1
}
void testEdit() {
...
}
...
What I'm hoping for is the ability to dynamically mock domain object, i.e. expect(Attendance.save()).andReturn(null) or expect(Attendance.save()).andReturn(testAttendance), so that I don't have to create the web of associated objects in my setUp method that are necessary to validate the domain object that is being manipulated by the controller.
Am I just looking at this all wrong? It seems like I should be able to decouple the controller logic from the validation logic., so that I can just tell the mock to tell the controller that validation passed or failed. Thanks in advance.
I don't think there is a way to tell the mock that the validation of a certain object that is handled by a controller passed or failed but I might be wrong. But as I understand it your main concern is the creation of the web of associated objects right?
Without knowing what your controller looks like I would guess that you are getting needed domain objects in your controller by ID (e.g. Location) and load a Person by pidm and so on.
To simplify the creation of needed domain objects you could use .save(validate: false).
Your setUp method could look like this:
location = new Location().save(validate: false)
reason = new Reason().save(validate: false)
If you only need objects with valid IDs this would be sufficient.
new Person(pidm: 12345).save(validate: false)
new UserAccount(username: "testuser").save(validate: false)
Set certain fields to be able to use a finder like UserAccount.findByUserName().
So if your controller does something like
location = Location.get(params["location.id"])
reason = Reason.get(params["reason.id"])
userAccount = UserAccount.findByUserName(params.username)
...
new Attendance(location: location, reason: reason, userAccount: userAccount, ...)
the aforementioned lines should be satisfactory for your setUp method.
.save(validate: false) is very useful to just set values that are really needed in your test. I hope I got the whole thing right and I could be of help.
When mocking for unit tests, you don't have to have a complete object graph with every required value to test a single domain. For example, you could have something like this..
def department = new Department(name: "Accounting").save(validate: false)
def user = new User(username: "gdboling", department: department).save()
Assuming the only 2 required fields for User are username and department, but department might have many other fields that would fail validation, this will still work if all you really need to test is User.
You still have to specify them in #Mock, you just don't have to populate every bloody field. :)
I'm sure this is a fairly common situation. I'm using the Spring Security Core plugin and want to create a domain model that has a Person limited to certain roles:
class Workgroup {
Person manager
...
static constraints = {
manager(validator: {mgr ->
// it feels like there should be a more elegant, groovy way of doing this.
def auths = mgr.getAuthorities();
def returny = false
auths.each {
if(it.authority == 'ROLE_MANAGER')
{
returny = true
}
}
return returny
})
}
}
This test fails like a mofo:
void testInvalidManager() {
def nick = new Person(username:'Nick')
def nonManagerRole = new Role(authority:'ROLE_EMPLOYEE')
UserRole.create(nick,nonManagerRole)
def awesome = new Workgroup(name:'mooCows', manager:nick)
mockForConstraintsTests(Workgroup, [awesome])
assertFalse awesome.validate()
assertEquals "validator", awesome.errors["manager"]
}
testInvalidManager Error No signature of method: users.UserRole.save() is applicable for argument types: (java.util.LinkedHashMap) values: [[flush:false, insert:true]] Possible solutions: wait(), any(), wait(long), use([Ljava.lang.Object;), isCase(java.lang.Object), each(groovy.lang.Closure)
groovy.lang.MissingMethodException: No signature of method: users.UserRole.save() is applicable for argument types: (java.util.LinkedHashMap) values: [[flush:false, insert:true]]
Possible solutions: wait(), any(), wait(long), use([Ljava.lang.Object;), isCase(java.lang.Object), each(groovy.lang.Closure)
at users.UserRole.create(UserRole.groovy:32)
at users.UserRole.create(UserRole.groovy)
at users.UserRole$create.call(Unknown Source)
at users.WorkgroupTests.testInvalidManager(WorkgroupTests.groovy:17)
Is this better covered in Integration than Unit Testing? Do I need to mock UserRole (if so, how?)? How are these types of tests normally done?
UserRole.create() calls save(), so you need to use mockDomain() instead of just mockForConstraintsTests().
But that's only if you're ok with testing the domain model with mocks, which I would never do. The mocking support in Grails should be used when testing Controllers or other classes that use domain classes but shouldn't be bothered with real persistence, creating a database (even in-memory), etc. By removing that dependency you're concentrating on the current tier, trusting that the other tier is already properly tested. But when you use mocking to test domain classes, you're really just testing the mocking framework. So I always use integration tests for domain classes so they run against a real database.
To answer the implicit question from your code example, I'd write the constraint as
static constraints = {
manager validator: { mgr ->
mgr.authorities.find { it.authority == 'ROLE_MANAGER' } != null
}
}
The issue with its bulk is that you're using each() when a regular for loop would be preferable since you can return from a for loop. Use each() only when you really want to invoke the closure on every instance. Here's one that's less groovy than the other one but uses a for loop:
static constraints = {
manager validator: { mgr ->
for (auth in mgr.getAuthorities()) {
if (it.authority == 'ROLE_MANAGER') {
return true
}
}
return false
}
}