Stale GORM Session Causes Spock Unit Test to Fail - grails

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

Related

spock testing of endpoint and repository

Working on my Spring 2.67 and Spock 2.1-groovy-3.0 testing. I have the basic testing working but now trying some integration testing without success. I have a controller with:
private ApiService apiService;
#Autowired
public ApiController(ApiService apiService) {
this.apiService = apiService;
}
#GetMapping("api/{scannedId}")
#ResponseBody
public ResponseEntity getScannedId(#PathVariable String scannedId) {
try {
logger.info("ApiKey Controller received GET /api/" + scannedId);
ApiKey found = apiService.retrieveValidApiKey(scannedId);
...
}
...
The apiService has :
private ApiRepository apiRepository;
#Autowired
public ApiService(ApiRepository apiRepository) {
this.apiRepository = apiRepository;
}
public ApiKey retrieveValidApiKey(String uuid) {
ApiKey anApi = apiRepository.getApiKeyByApiKey(uuid);
if (anApi == null) {
logger.info("ApiService.retrieveValidApiKey({}) failed to find a matching ApiKey", uuid);
return null;
}
I have a Spock test that seeds the database with two values and then successfully calls the /api endpoint. I have code in the test that confirms the two values were inserted, but when the actual ApiService class is called, they are not found:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureTestDatabase
class ApiControllerTest extends Specification {
#Shared Logger logger = LoggerFactory.getLogger(this.getClass())
#Autowired
ApiController apiController
#Autowired
ApiRepository apiRepository
#Transactional
def "GET by scannedId using apikey #apiKey should be #resultCode"() {
given:
def foundList = apiRepository.findAll()
logger.info("{} apiKeys in repository", foundList.size())
for (int i = 0; i < foundList.size(); i++) {
logger.info("Found ApiKey #{} apiKey: {} & uuid: {}", i, foundList.get(i).apiKey, foundList.get(i).uuid)
}
when:
def foundListCount = apiRepository.getApiKeyByApiKey(apiKey)
logger.info("FoundList: {}", foundListCount)
ResponseEntity<ApiKey> result = restTemplate.getForEntity( "/api/{scannedId}", ApiKey.class, apiKeyValue1)
logger.info("TestRestTemplate returned apikey: {}", result)
then:
assert result.getStatusCode() == resultCode
where:
apiKey || resultCode
"testApiKey3" || HttpStatus.NOT_FOUND
apiKeyValue1 || HttpStatus.OK
apiKeyValue2 || HttpStatus.OK
}
def setup() {
def apiKey1 = new ApiKey(apiKey: apiKeyValue1, uuid: uuid1, beginDate: beginDate1, endDate: endDate1)
def apiKey2 = new ApiKey(apiKey: apiKeyValue2, uuid: uuid2, beginDate: beginDate2, endDate: endDate2)
apiRepository.saveAndFlush(apiKey1)
apiRepository.saveAndFlush(apiKey2)
}
When I run the test, the logger in the test method spits out all the persisted values. But the test fails because the ApiService.getScannedId fails because it does not see the values persisted in test setup.
I cannot use the #DataJpaTest because the ApplicationContext isn't loaded then, so the endpoints fail.
I am not sure why Spock sees the values persisted via Repository, but the ApiService doesn't. Is it a context issue? I really would like to test without mocks here if at all possible.
The problem is that your test is annotated with #Transactional that means that only things that run in that method can see the data. The rest request you are sending out, will be handled by another thread that doesn't have access to the transaction and thus will not see the data.
You'll have to remove the annotation if you want it to work, but then you'll also have to clean the inserted data manually at the end of the test/cleanup(), since you can't rely on the transaction rollback.

Grails functional test Geb page object module not working No signature of method

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.

Grails 4: Custom Domain Model Marshaller Not Working On Grails 4

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.

Grails test-app Error creating bean with name 'grailsDatastore'

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.

Grails: testing a redirect with an integration test

I'm using Grails 1.3.7. I'm trying to test a redirect in my integration test. Here is my controller and method in question ...
class HomeController {
def design = {
....
if (params.page) {
redirect(uri: "/#/design/${params.page}")
}
else {
redirect(uri: "/#/design")
}
break;
}
}
However in my integration test, the call to "controller.response.redirectedUrl" is failing (always returns null) even though I know the redirect call is being made (verified through logging). What is wrong with the integration test below?
class HomeControllerTests extends grails.test.ControllerUnitTestCase {
....
void testHomePageDesign() {
def controller = new HomeController()
// Call action without any parameters
controller.design()
assert controller.response.redirectedUrl != null
assertTrue( responseStr != "" )
}
Thanks, - Dave
Changing your HomeControllerTests to extend GrailsUnitTestCase should fix the problem.
class HomeControllerTests extends grails.test.GrailsUnitTestCase {
....
}
The various ways of generating a test class all seem to vary the class that is extended.
create-integration-test => GroovyTestCase
create-unit-test => GrailsUnitTestCase
create-controller => ControllerUnitTestCase
However, according to the Test section of the Grails User Guide, GrailsUnitTestCase is the core part of the testing frame and, at least in 1.3.7, that is the best class to base test classes on.

Resources