I am attempting to create tests for domain objects. The test is simple, see below. However it is throwing an error
Error creating bean with name 'grailsDatastore'
class AccountSpec extends Specification implements DataTest {
void setupSpec(){
mockDomain Account
}
void "test basic persistence mocking"() {
setup:
def account = new Account(name: 'Robert Fripp', username: "robert", password: "robert", email: "robert#mail.com").save(flush:true)
expect:
Account.count() == 1
}
}
You have not provided enough information to know for sure what is wrong with your project.
See the project at https://github.com/jeffbrown/mcroteauaccount. That contains a test that does what you are trying to do:
https://github.com/jeffbrown/mcroteauaccount/blob/2110545083b3b41dca61eb77f1e4d5dfccc8508a/src/test/groovy/mcroteauaccount/AccountSpec.groovy
package mcroteauaccount
import grails.testing.gorm.DataTest
import spock.lang.Specification
class AccountSpec extends Specification implements DataTest {
void setupSpec() {
mockDomain Account
}
void "test basic persistence mocking"() {
setup:
def account = new Account(name: 'Robert Fripp', username: "robert", password: "robert", email: "robert#mail.com").save(flush: true)
expect:
Account.count() == 1
}
}
That test compiles, runs, and passes. If your question is how do you write a test which saves an instance and then verifies that the instance is included in what is returned from Account.count(), the code above demonstrates how to do that.
Related
I'm upgrading an existing application from Grails 2.4.4 to Grails 4.0.10 and I'm trying to test the logout code that works in the Grails 2.4.4 app now. However, when I call findAllByUsername after deleting all the tokens for that user, GORM returns apparently stale data.
To test this, I tried
AuthenticationToken.list().find { it.username == username }, and it returns null. However, AuthenticationToken.findAllByUsername(username) returns the 3 records that were just deleted.
How can I prevent findAllByUsername from returning stale data in this unit test?
package us.cloudcard.api
import grails.testing.gorm.DataTest
import grails.testing.gorm.DomainUnitTest
import groovy.time.TimeCategory
import spock.lang.Specification
class AuthenticationTokenSpec extends Specification implements DomainUnitTest<AuthenticationToken>, DataTest {
def setup() {
[AuthenticationToken, Photo, Person].each { mockDomain(it) }
}
def "test logout"() {
setup:
String username = "bacon"
[username, "eggs", "biscuits", "gravy", username, "hashbrowns", username].each {
new AuthenticationToken(username: it).save(validate: false)
}
when:
List<AuthenticationToken> tokensForUser = AuthenticationToken.findAllByUsername(username)
AuthenticationToken.deleteAll(tokensForUser, flush: true)
then:
AuthenticationToken.list().find { it.username == username } == null // this passes
AuthenticationToken.findAllByUsername(username).empty // this fails
}
}
This is the page object.
package myapp.pages
import geb.Page
class LoginPage extends Page {
static url = "http://localhost:8080/login/auth"
//static at = {title.contains("Login")}
static at = {
waitFor {title.contains("Login")} // Add waitFor here to verify on page
}
static content = {
loginForm { $( 'form') }
usernameField { $('form').userName }
passwordField { $('form').password }
submitButton { $('input#submit' )}
}
void loginSubmit(String email, String password) {
usernameField = "email#something.com"
assert $('form').username == "email#something.com"
passwordField = "secret"
assert $('form').password == "secret"
submitButton.click()
}
}
And this is the LoginSpec test file
package myapp.login
import geb.spock.GebSpec
import grails.testing.mixin.integration.Integration
import grails.transaction.*
import myapp.pages.LoginPage
#Integration
#Rollback
class LoginSpec extends GebSpec {
def setup() {
}
def cleanup() {
}
void "user successfully logs in, is redirected to homepage"() {
given:
to LoginPage
when:
LoginPage.loginSubmit("email#something.com", "secret")
then:
title.contains("Dashboard")
}
}
When i run this test, I get the following error:
groovy.lang.MissingMethodException: No signature of method: static myapp.pages.LoginPage.loginSubmit() is applicable for argument types: (java.lang.String, java.lang.String) values: [email#something.com.com, secret]
I basically get the same error when I hardcode the username and password into the login page loginsubmit function. The selectors are fine, when I use the same selectors directly in the LoginSpec test to set the username and password, the test passes. The issue only occurs when I try to use the page object.
Instead of this:
when:
LoginPage.loginSubmit("email#something.com", "secret")
Use this:
when:
loginSubmit("email#something.com", "secret")
The issue isn't really a Geb one. The JVM doesn't allow you to invoke an instance method on a class reference as the context necessary to carry out that invocation wouldn't exist. loginSubmit is an instance method, not a static method.
I hope that helps.
Geb remembers the current page and automatically dispatches method calls to the page, so you do not need to include the page class name: loginSubmit("email#something.com", "secret") in the test will call the method on the page.
My current problem right now is the custom object marshaller that I created since grails 2.4.5 to grails 3.3.0 is not working on grails 4.0.0. Grails 4 respond the domain model by default not the custom made I create.
Below are the codes I have. Please review and if you find theres something wrong please do let me know guys, I will be glad if you can help me with this.
ResponseSender.groovy
package com.problem.solve.common
import org.springframework.http.HttpStatus
trait ResponseSender {
void sendResponse() {
render status: HttpStatus.NO_CONTENT
}
void sendResponse(def responseData) {
respond (responseData)
}
void sendResponse(HttpStatus status, def responseData) {
response.status = status.value()
respond (responseData)
}
}
This ResponseSender.groovy trait is implemented on the controller.
MarshallerInitializer.groovy
package com.problem.solve.marshaller
class MarshallerInitializer {
CustomObjectMarshallers customObjectMarshallers
void initialize() {
customObjectMarshallers.register()
}
}
this MarshallerInitializer.groovy will be called when bootstrap initialize.
package com.problem.solve.marshaller
class CustomObjectMarshallers {
List marshallers = []
void register() {
marshallers.each {
it.register()
}
}
}
This CustomObjectMarshallers.groovy will going to register all marshallers.
UserMarshaller.groovy
package com.problem.solve.marshaller.marshalls
import com.problem.solve.security.User
import grails.converters.JSON
class UserMarshaller {
void register() {
JSON.registerObjectMarshaller(User) { User user ->
return [
id: user.id,
fullName: user.fullName,
username: user.username,
emailAddress: user.emailAddress,
roles: user.authorities.authority,
dateCreated: user.dateCreated,
lastUpdated: user.lastUpdated,
_entityType: 'User'
]
}
}
This UserMarshaller.groovy is a sample domain model that I want to convert from domain model to json response.
resources.groovy
import com.problem.solve.marshaller.CustomObjectMarshallers
import com.problem.solve.marshaller.MarshallerInitializer
import com.problem.solve.marshaller.marshalls.*
// Place your Spring DSL code here
beans = {
customObjectMarshallers(CustomObjectMarshallers) {
marshallers = [
new UserMarshaller()
]
}
marshallerInitializer(MarshallerInitializer) {
customObjectMarshallers = ref('customObjectMarshallers')
}
}
The problem with this setup is not working on grails 4, but this setup are working with grails 2.4.5 and grails 3.3.0.
I really need your help guys.
Thank you so much :)
I solve this marshaller problem by creating DomainModelResponseDto as respond domain model.
Example:
class UserResponseDto {
String id
String username
String email
UserResponseDto(User user) {
id = user.id
username = user.username
email = user.email
}
}
I have another solution to this problem. The marshaller is working the problem is the views. I have to delete the views that are generated when creating a domain class and all is working perfectly with the current marshaller and setup.
I am working on a Grails 3.2.2 Plugin in which I am trying to override the RegisterController from spring-security-ui as well as the register.gsp. I have used the s2ui-override command which has created me the required controller and at the moment I only added a println to see it working, but it seems to be still going to the original one. So far I have:
RegisterController
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UserDetails
import grails.plugin.springsecurity.ui.RegisterCommand
class RegisterController extends grails.plugin.springsecurity.ui.RegisterController {
static defaultAction = 'register'
UserDetailsService userDetailsService
#Override
def register(RegisterCommand registerCommand) {
println "IN MY CONTROLLER!!"
def registerReturn = super.register(registerCommand)
if (registerReturn?.emailSent) {
UserDetails createdUser = userDetailsService.loadUserByUsername(registerCommand.username)
println "New user ${createdUser}"
}
registerReturn
}
def otherAction() {
println "IN MY CONTROLLER!!"
println "userDetailsService = ${userDetailsService}"
"Hellloooo"
}
}
register.gsp same as the original, only added
<p style="color:red;"> HELLLOOOOOOO </p>
immediately after the body tag
UrlMappings
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
"404"(view:'/notFound')
}
}
Result when running the app:
I can see my sample red text at the top, but cannot see the output in the console, although I am getting a warning when running the app. Console output when going to the "register" URL:
Running application...
WARNING: The [register] action accepts a parameter of type [grails.plugin.springsecurity.ui.RegisterCommand] which does not implement grails.validation.Validateable. Data binding will still be applied to this command object but the instance will not be validateable.
#Override
^
Configuring Spring Security Core ...
... finished configuring Spring Security Core
Configuring Spring Security UI ...
... finished configuring Spring Security UI
Configuring Spring Security REST 2.0.0.M2...
... finished configuring Spring Security REST
Grails application running at http://localhost:8060 in environment: development
Though when going to "register/otherAction" I am getting:
IN MY CONTROLLER
userDetailsService = grails.plugin.springsecurity.userdetails.GormUserDetailsService#10ed50b0
Interestingly, if I publish the plugin locally (my .m2 dir) and import it into an app, I am getting my printlns in the console, BUT my gsp is now not being used (I cannot see my red Hello text). Console output:
IN MY CONTROLLER!!
// unimportant mailing error
New user grails.plugin.springsecurity.userdetails.GrailsUser#364492: Username: test; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: false; Granted Authorities: ROLE_NO_ROLES
Anyone can help on where am I going wrong? I am happy to provide any other resources which may help.
So I have a fix (maybe a workaround):
RegisterController
class RegisterController extends grails.plugin.springsecurity.ui.RegisterController {
static namespace = 'my-great-plugin'
UserDetailsService userDetailsService
#Override
def register(RegisterCommand registerCommand) {
println "IN MY CONTROLLER!!"
def registerReturn = super.register(registerCommand)
println "Got return from super call ${registerReturn}"
if (registerReturn?.emailSent) {
UserDetails createdUser = userDetailsService.loadUserByUsername(registerCommand.username)
println "New user ${createdUser}"
}
registerReturn
}
def otherAction() {
println "IN MY CONTROLLER"
println "userDetailsService = ${userDetailsService}"
"Hellloooo"
}
MyPluginUrlMappings.groovy
static mappings = {
"/register/$action?/$id?(.$format)?" {
controller = 'register'
namespace = 'my-great-plugin'
}
}
MyPluginGrailsPlugn.groovy
def loadAfter = ['springSecurityCore', 'springSecurityUi', 'springSecurityRest']
And it works now!
I am still open to suggestions on how to handle this better
I am new to Groovy & Grails. I am working on one of the sample one-to-many relationship in Grails.
The below is the code.
class User {
//properties
String login
String password
String role
//constraints and order of display of fields in UI
static constraints = {
login(blank: false, nullable: false, unique: true)
password(blank: false, nullable: false, password: true)
role(inList:["Admin", "Member"])
}
static hasMany = [posts : Post]
}
class Post {
String content
Date dateCreated
static constraints = {
content(blank: true)
}
static belongsTo = [user : User]
}
My Test class in Groovy
#TestFor(User)
class UserTests {
void testUserToPost() {
def user = new User(login: "joe", password: "joe", role:"Admin")
user.addToPosts(new Post(content: "First"));
user.addToPosts(new Post(content: "Second"));
user.addToPosts(new Post(content: "Third"));
user.save(flush: true)
assertEquals 3, User.get(user.id).posts.size()
}
}
While running the test class, getting following exception:
groovy.lang.MissingMethodException: No signature of method: com.library.forum.User.addToPosts() is applicable for argument types: (com.library.forum.Post) values: [com.library.forum.Post : (unsaved)]
Possible solutions: getPosts() at com.library.forum.UserTests.testUserToPost(UserTests.groovy:17)
Can anyone tell me where is the problem in code.
Since Grails 2.1.4, there's a change in mock behavior because of performance issue. So you need to mock all associated entities of the mocked entity.
See GRAILS-9637 - Due to a performance issue, #Mock no longer mocks
associated entities of the mocked entity. These have to be manually
specified. For example the following test will fail in 2.1.4 and
above:
#Mock(Author)
void testAddToBooks() {
def a = new Author()
a.addToBooks(new Book())
}
To correct the above test you need to mock both Author and Book:
#Mock([Author, Book])
void testAddToBooks() {
def a = new Author()
a.addToBooks(new Book())
}
You can check this reference.
You need to mock all related domain classes. Change :
#TestFor(User)
class UserTests {
to
#TestFor(User)
#Mock(Post)
class UserTests {
If you need, the mock annotation support a list of classes, for example: #Mock([Domain1, Domain2, Domain3])