I have a simpler controller method that does the following:
def updateName(){
def idUser = params.id
User changeUser = User.get(idUser)
changeUser.name = "newname"
changeUser.save(flush: true)
}
And the following Spock test:
def "updateName should edit user and save it"(){
given: "the id of the user"
User currentUser = new User([name: "hector" , age: 12]).save(flush: true)
params.id = User.last().id
when: "the updateName method is called"
controller.updateName()
then: "name should have change it and save has to be called just one time"
assert currentUser.name == "newname" // Success
1 * _.save() // Too few invocations, 0.
}
I have seen all the related questions in SO about this topic, and people suggest using Mocks and Spy, but I don't really see why should I be using them and anyway I didn't get them to work for me, I tried to create a User mock and change my cardinality assert to:
1 * userMock.save()
But it didn't work for me... can I have some help with this?
To make it work, I create a GroovyMock that allows me to access to static functions such as save.
def "updateName should edit user and save it"(){
given: "the id of the user"
params.id = 1
User mockUser = GroovyMock(User, global: true)
when: "the updateName method is called"
controller.updateName()
then: "set Name and save has to be called just one time"
1 * User.get(1) >> mockUser
1 * mockUser.setName("newname")
1 * mockUser.save()
} //All test passed!
Not sure if it's the best way to do it, but it works for me! I'll leave the question in case someone offers a better solution, or throws some good information about this
Related
In Spock, you can set time out, after which the test fails.
#Timeout(value = 1500, unit = TimeUnit.MILLISECONDS)
def "this method should not run longer than 2 seconds"() {
given:
userFilter = buildUserFilter(); // criteria to search users.
exportDestination = buildDestination(); // where to export users.
expect: "should not take long"
userService.exportUsers(userDetails);
}
Now I need something the opposite: the test should pass after timeout.
def "should block and wait until user-microservice is up"() {
given:
userExportMicroserviceClient = mock()
userExportMicroserviceClient.getUsers(_) >> httpNotFound
expect: "should block the current thread because userMicroservice is down"
failHere()
}
I know, it is a bad thing to depend on the other service availability. This thing runs only during data migration and it is not related to daily tasks.
Again, I have a method that gives users from user microservice, and it blocks until user microservice is up. How do I test it?
It depends how did you implement the exportUsers() method.
Assume it pulls a microservice every second. If so, you can emulate the response of the microservice in sequence: down, down, up.
// The timeout includes 2 seconds of microservice down.
#Timeout(value = 3500, unit = TimeUnit.MILLISECONDS)
def "should block and wait until user-microservice is up"() {
given: "mocked microservice which is not available for first 2 calls and available on 3rd call"
userExportMicroserviceClient = mock()
userExportMicroserviceClient.getUsers(_) >>> [httpNotFound, httpNotFound, httpOk]
when: "calling the user getting should take long time"
def actualResult = userService.exportUsers(userDetails)
then:
def expectedResult = <prepare>
actualResult == expectedResult
}
Sure things, this is a pseudo-code and requires yours project specific corrections.
I have a set of basic unit tests in grails and I am getting some strange, inconsistent behaviour. So basically the unit tests are for a Service for interacting with my User Domain.
The issue is at a high level - If I run the full Spec of tests - 4 tests fail each time. If I run them individually - they pass. The obvious thing here is that there is state being held between tests - but given this is a unit test - there shouldn't be (and I have verified that with logging).
The components in play here are:
UserService
User Domain
Address Domain
UserCommand (Verifiable)
So looking at the Spec I have the following in setup:
User user
def setup() {
println "================ Setting Up User (${User.count()}) ================"
// Set the roles
new Role(authority: "ROLE_ADMIN").save()
new Role(authority: "ROLE_OPERATOR").save()
def role = new Role(authority: "ROLE_USER").save()
def userAddress = new Address()
userAddress.with {
name = "Name"
address1 = "Address 1"
address2 = "Address 2"
townCity = "Town"
postcode = "BT19"
county = "Down"
}
// Create an admin user
user = new User()
user.with {
christianName = "Phil"
surname = "Preston"
email = "p#p.com"
username = "admin"
password = "password123"
phone = ""
skype = ""
address = userAddress
}
println(user.properties)
// Save the test user
if (user.validate()) {
println "Saving"
user.save(flush: true, failOnErrors: true)
UserRole.create(user, role)
}
else {
user.errors.allErrors.eachWithIndex { i, x ->
println "${i} - ${x}"
}
}
assert user
}
And the two simple tests are as follows:
void "Test updateUser - changing a user password"() {
given: "An update to the user"
UserCommand cmd = new UserCommand()
cmd.with {
id = user.id
christianName = user.christianName
surname = user.surname
username = user.username
email = user.email
skype = user.skype
phone = user.phone
password = "newPassword"
passwordCheck = "newPassword"
isAdmin = user.isAdmin()
isOperator = user.isOperator()
}
when: "attempt to update"
User updated = service.updateUser(cmd)
then: "the password should be update - but nothing else"
updated.password == cmd.password
}
void "Test updateUser - changing a user detail"() {
given: "An update to the user"
UserCommand cmd = new UserCommand()
cmd.with {
id = user.id
christianName = user.christianName
surname = user.surname
username = user.username
email = "update#update.com"
skype = user.skype
phone = user.phone
password = user.password
isAdmin = user.isAdmin()
isOperator = user.isOperator()
}
when: "attempt to update"
User updated = service.updateUser(cmd)
then: "the email should be update - but nothing else"
updated.email == cmd.email
}
(There are others - but this is all to demonstrate the problem)
So the strangeness is as follows:
The first test passes, the second one fails.
If I swap the order - the first one still passes, second fails
If I run both individually - they both will pass
The code in setup() prints the number of User objects (0) each time
It verifies the object is created each time (assert and logging)
The unit test fails because I throw an exception when validation fails forthe User domain object I am udating. So the following:
def updateUser(UserCommand userCommand) {
assert userCommand
// Get the current
User foundUser = User.findById(userCommand.id)
def wasAdmin = foundUser.admin
def wasOperator = foundUser.operator
// Bind Data
bindData(foundUser, userCommand, ['class', 'operator', 'admin', 'address'])
foundUser?.address?.with {
name = userCommand.name
address1 = userCommand.address1
address2 = userCommand.address2
townCity = userCommand.townCity
county = userCommand.county
postcode = userCommand.postcode
}
// THIS VALIDATION FAILS ON SECOND TEST
if (foundUser.validate()) {
foundUser.save()
// ... removed
return foundUser.refresh()
}
else {
Helper.copyErrorToCmd(foundUser, userCommand)
log.error("Errors: ${foundUser.errors.allErrors}")
throw new UserServiceException(cmd: userCommand, message: "There were errors updating user")
}
The errors on the User domain object are basically (shortened):
'skype': rejected value [null]
'phone': rejected value [null]
So you can see that I have empty strings for these fields - and with conversion to null (as Grails will do), this is the reason. However the issue with that is:
It should fail for both, and certainly fail when run individually
Both fields are marked as nullable: true in the constraints
In UserService I have used the debugger to check the objects that are being saved when running both tests - the phone and skype are null for both tests, yet one fails and one doesn't
So the constraints in the User domain object are as follows:
static constraints = {
christianName blank: false
surname blank: false
username blank: false, unique: true, size: 5..20
password blank: false, password: true, minSize: 8
email email: true, blank: false
phone nullable: true
skype nullable: true
address nullable: true
}
And I am using a Command object which has the following constraints:
static constraints = {
importFrom User
importFrom Address
password blank: false, password: true, minSize: 8
passwordCheck(blank: false, password: true, validator: { pwd, uco -> return pwd == uco.password })
}
Note I have even tried adding the Skype and phone fields in the UserCommand constraints closure as well.
This issue goes away if I add text to the fields in setup, but thats not going to possible in the actual app as these fields can be left blank.
Any help on how this inconsistency happens would be greatly appreciated.
RECREATION
I have added a minimal GitHub project which recreates the issue: Github Project Recreating Issue. To see the problem:
./grailsw test-app
Will run the two test, first passes second fails. To run the failed test individually run:
./gradlew test --tests "org.arkdev.bwmc.accountmission.UserServiceSpec.*Test updateUser - changing a user detail"
And you can see the test pass.
The issue seems to be that the skype and phone fields are nullable:true for the first test, and nullable:false on the second test (I step into user.validate(), and step into the GrailsDomainClassValidator.java, in the validate(Object,Errors,boolean) call I can see the constrainedProperties Map (which is basically field -> constraints). This shows that skype is NullableConstraint.nullable == true, on the first call of setup, but is showing that NullableConstraint.nullable == false on the setup call for the second test.). This makes no sense.
After the first call
bindData(foundUser, userCommand, ['class', 'operator', 'admin', 'address'])
in UserService.updateUser(UserCommand) (called by feature method Test updateUser - changing a user password) the static member User.constraints becomes null. Go figure. I have no idea whatsoever how Grails works, so maybe someone else can make sense of it. But to me it seems that this should not happen. Maybe you are somehow making a mistake in that call.
You shouldn't have to test the constraints in the framework technically but I've had similar issues before so I can see why you would want to.
Is it possible to post your full test? Are you using #TestFor/#Mock at all? Just as a check, but I assume you must have #Mock otherwise you would get a different error creating the domain classes? #Mock is required for the constraints to be used (or #GrailsUnitTestMixin afaik).
You say in your comment "The values in your app can be blank"; it should be noted that this is not the same as nullable as well. If it really can be blank you should change/add it and then "" will work
You also are not using 'failOnError: true' as you have typoed it.
Sorry I don't have enough rep to write as a comment.
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!
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 am trying to be a good little programmer and set up Unit tests for my Grails 2.2.3 app. The unit tests that use GORM's injected .save() method are apparently not persisting to the mock test DB. For an example, here is what one test consists of:
#TestFor(TermService)
#Mock(Term)
class TermServiceTests {
void testTermCount() {
def t = new Term(code: "201310").save(validate: false, flush: true, failOnError: true)
println "Printing Term: " + t.toString()
assert 1 == Term.count() // FAILS
assert service.isMainTerm(t) // FAILS
}
}
I did a println that ends up printing Printing Term: null, meaning the Term did not save and return a Term instance. The first assertion is false with Term.count() returning 0.
Does anyone know why this might be? I have a mock Term and TermService (via the TestFor annotation, I believe), so I'm not quite sure why this wouldn't work. Thanks!
Edit: Here is my Term class.
class Term {
Integer id
String code
String description
Date startDate
Date endDate
static mapping = {
// Legacy database mapping
}
static constraints = {
id blank: false
code maxSize: 6
description maxSize: 30
startDate()
endDate()
}
}
Looks like id generator is assigned since you have mentioned about using legacy database. Plus id is not bindable by default in domain class (map construct won't work for id). So, I think you have to end up using like below:
def t = new Term(code: "201310")
t.id = 1
t.save(...)