validate() Mock Constraint Testing in Grails - unit-testing

I have a unit test that I am trying to get the validation to succeed on.
Basically, when I call badMockedSecureFile.validate(), it is not doing what I expect, and that is failing validation for the two fields, encryptedFileName and encryptedFileData.
When I break in the debugger, I simply get a null value for badMockedSecureFile.errors in the subsequent assert. Here are my two files:
Any input would be greatly appreciated. I couldn't find an exact similar question. I'm using grails 2.2.4 with Oracle JDK 1.7.0_25 if that matters any.
EDIT: I just wanted to note that I removed the mockForConstraintTests call and it seems to be working now. I get this feeling I didn't RTFM somewhere and this behaviour changed in unit testing, or is something else going on?
SecureFile.groovy
class SecureFile implements Serializable {
/**
* An unencrypted version of the file name. This file name is unencrypted
* when the appropriate password and key combo is used and it is never
* persisted to the database for security (see transients below).
*/
String fileName
/**
* Unencrypted version of the file data. Never persisted to the
* database for security (see transients below).
*/
byte[] fileData
String encryptedFileName
byte[] encryptedFileData
Date dateAdded
Date dateUpdated
Date dateDeleted
static constraints = {
encryptedFileName(nullable: false, blank: false)
encryptedFileData(nullable: false)
}
static transients = ["fileName", "fileData"]
static belongsTo = [user: User]
}
SecureFileTests.groovy
import static org.junit.Assert.*
import grails.test.mixin.*
import grails.test.mixin.support.*
import org.junit.*
/**
* See the API for {#link grails.test.mixin.support.GrailsUnitTestMixin} for usage instructions
*/
#TestFor(SecureFile)
class SecureFileTests {
static final String SAMPLE_PDF_FILE = "fileEncryptionTestSample.pdf"
void testConstraints() {
def samplePdfFile = new FileInputStream(SAMPLE_PDF_FILE)
// Not really encrypted for this mock.
def mockedSecureFile = new SecureFile(
encryptedFileName: "--Not-Really-Encrypted--",
encryptedFileData: samplePdfFile.getBytes()
)
mockForConstraintsTests(SecureFile, [mockedSecureFile])
// Validation should fail if both properties are null.
def badMockedSecureFile = new SecureFile()
assert !badMockedSecureFile.validate()
assert "nullable" == badMockedSecureFile.errors["encryptedFileName"].code
assert "nullable" == badMockedSecureFile.errors["encryptedFileData"].code
}
}

Remove code from badMockedSecureFile.errors["encryptedFileName"].code .
You will get as you expected.
mockForConstraintsTests(SecureFile)
// Validation should fail if both properties are null.
def badMockedSecureFile = new SecureFile()
assert !badMockedSecureFile.validate()
assert "nullable" == badMockedSecureFile.errors["encryptedFileName"]
assert "nullable" == badMockedSecureFile.errors["encryptedFileData"]

Related

Grails Spock Mocking an Object

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

Unit testing Grails domains that (over?) extend a base class that uses services

So, I have a base class that I want to extend most (but not all) of my domain classes from. The goal is that I can add the six audit information columns I need to any domain class with a simple extends. For both creation and updates, I want to log the date, user, and program (based on request URI). It's using a Grails service (called CasService) to find the currently logged on user. The CasService then uses Spring Security and a database call to get the relevant user information for that field.
The trouble is, if I do this, then I'm going to have to Mock the CasService and request object in any unit test that tests a domain that uses these classes. That will also impact unit tests for services and controllers that use these domains. That's going to make unit testing a bit of a pain, and increase boiler plate code, which is what I was trying to avoid.
I'm fishing for better design options, and I'm open to suggestion. My current default is to simply add the same boiler plate to all my domain classes and be done with it. See below for source code and what I've tried so far.
Source
Common Audit Domain Class
package com.mine.common
import grails.util.Holders
import org.springframework.web.context.request.RequestContextHolder
class AuditDomain implements GroovyInterceptable {
def casService = Holders.grailsApplication.mainContext.getBean('casService')
def request = RequestContextHolder?.getRequestAttributes()?.getRequest()
String creator
String creatorProgram
String lastUpdater
String lastUpdateProgram
Date dateCreated
Date lastUpdated
def beforeValidate() {
beforeInsert()
beforeUpdate()
}
def beforeInsert() {
if (this.creator == null) {
this.creator = casService?.getUser() ?: 'unknown'
}
if (this.creatorProgram == null) {
this.creatorProgram = request?.requestURI ?: 'unknown'
}
}
def beforeUpdate() {
this.lastUpdater = casService?.getUser() ?: 'unknown'
this.lastUpdateProgram = request?.requestURI ?: 'unknown'
}
static constraints = {
creator nullable:true, blank: true
lastUpdater nullable:true, blank: true
creatorProgram nullable:true, blank: true
lastUpdateProgram nullable:true, blank: true
}
}
CasService
package com.mine.common
import groovy.sql.Sql
class CasService {
def springSecurityService, sqlService, personService
def getUser() {
if (isLoggedIn()) {
def loginId = springSecurityService.authentication.name.toLowerCase()
def query = "select USER_UNIQUE_ID from some_table where USER_LOGIN = ?"
def parameters = [loginId]
return sqlService.call(query, parameters)
} else {
return null
}
}
def private isLoggedIn() {
if (springSecurityService.isLoggedIn()) {
return true
} else {
log.info "User is not logged in"
return false
}
}
//...
}
What I've Tried
Creating a Test Utilities Class to do the setup logic
I've tried building a class like this:
class AuditTestUtils {
def setup() {
println "Tell AuditDomain to sit down and shut up"
AuditDomain.metaClass.casService = null
AuditDomain.metaClass.request = null
AuditDomain.metaClass.beforeInsert = {}
AuditDomain.metaClass.beforeUpdate = {}
}
def manipulateClass(classToTest) {
classToTest.metaClass.beforeInsert = {println "Yo mama"}
classToTest.metaClass.beforeUpdate = {println "Yo mamak"}
}
}
And then calling it in my Unit Test's setup() and setupSpec() blocks:
def setupSpec() {
def au = new AuditTestUtils()
au.setup()
}
OR
def setupSpec() {
def au = new AuditTestUtils()
au.manipulateClass(TheDomainIAmTesting)
}
No dice. That errors out with a NullPointerException on the CasService as soon as I try to save the domain class that extends the AuditDomain.
java.lang.NullPointerException: Cannot invoke method isLoggedIn() on null object
at com.mine.common.CasService.isLoggedIn(CasService.groovy:127)
at com.mine.common.CasService.getPidm(CasService.groovy:9)
at com.mine.common.AuditDomain.beforeInsert(AuditDomain.groovy:26)
//...
at org.grails.datastore.gorm.GormInstanceApi.save(GormInstanceApi.groovy:161)
at com.mine.common.SomeDomainSpec.test creation(SomeDomainSpec:30)
I'm open to alternate ways of approaching the issue of DRYing the Audit Information out my Domains. I'd settled on inheritance, but there are other ways. Traits aren't available in my environment (Grails 2.3.6), but maybe that's just a reason to get cracking on updating to the latest version.
I'm also open to suggestions about how to test these domains differently. Maybe I should have to contend with the audit columns in the unit tests of every domain class that has them, though I'd rather not. I'm okay with dealing with that in integration tests, but I can unit test the AuditDomain class easily enough on its own. I'd prefer that unit tests on my domains tested the specific things those domains bring to the table, not the common stuff that they all have.
So, ultimately, I've gone with Groovy delegation to meet this need.
The validation logic all lives in
class AuditDomainValidator extends AuditDomainProperties {
def domainClassInstance
public AuditDomainValidator(dci) {
domainClassInstance = dci
}
def beforeValidate() {
def user = defaultUser()
def program = defaultProgram()
if (this.creator == null) {
this.creator = user
}
if (this.creatorProgram == null) {
this.creatorProgram = program
}
this.lastUpdater = user
this.lastUpdateProgram = program
}
private def defaultProgram() {
domainClassInstance.getClass().getCanonicalName()
}
private def defaultUser() {
domainClassInstance.casService?.getUser() ?: 'unknown'
}
}
I created this abstract class to hold the properties while trying various solutions. It could probably be folded into the validator class with no problems, but I'm just lazy enough to leave it in there since it's working.
abstract class AuditDomainProperties {
String creator
String creatorProgram
String lastUpdater
String lastUpdateProgram
Date dateCreated
Date lastUpdated
}
And finally, here's how to implement the validator class in a Grails domain class.
import my.company.CasService
import my.company.AuditDomainValidator
class MyClass {
def casService
#Delegate AuditDomainValidator adv = new AuditDomainValidator(this)
static transients = ['adv']
//...domain class code
static mapping = {
//..domain column mapping
creator column: 'CREATOR'
lastUpdater column: 'LAST_UPADTER'
creatorProgram column: 'CREATOR_PGM'
lastUpdateProgram column: 'LAST_UPDATE_PGM'
dateCreated column: 'DATE_CREATED'
lastUpdated column: 'LAST_UPDATED'
}
}
This approach doesn't work perfectly for Unit and Integration tests, it seems. Trying to access the dateCreated column in either fails with an error that there is no such property for the domain class in question. Thats odd, since running the application works fine. With the Unit tests, I would think it was a mocking issue, but I wouldn't expect that to be a problem in the integration tests.

Grails build-test-data Plugin - Can't Find TestDataConfig.groovy

I'm trying to utilize the build-test-data plugin in my Grails (v2.4.3) app to assist with test data creation for unit testing, but while running my unit tests the plugin cannot find TestDataConfig.groovy to load my specified values (for unique constraint tests, etc).
I've installed the plugin via including it in BuildConfig.groovy:
plugins {
...
test ":build-test-data:2.2.0"
...
}
I've ran the following command to create the TestDataConfig.groovy template, which places the file at \grails-app\conf\:
grails install-build-test-data-config-template
I've followed the general instructions on the plugin wiki to come up with a properly formatted file:
testDataConfig {
sampleData {
'<path>.User' {
def a = 1
username = { -> "username${a++}" }
}
}
}
(Where path is the fully-qualified class name.)
In my tests, I am using the following general format:
import grails.buildtestdata.TestDataConfigurationHolder
import grails.buildtestdata.mixin.Build
import grails.test.mixin.TestFor
import spock.lang.Specification
import spock.lang.Unroll
#TestFor(User)
#Build(User)
class UserSpec extends Specification {
def setup() {
mockForConstraintsTests(User)
TestDataConfigurationHolder.reset()
user = User.buildWithoutSave()
}
#Unroll
void "test #field must be unique"() {
given: 'a User exists'
user.save(flush: true)
when: 'another User is created with a non-unique field value'
def nonUniqueUser = User.buildWithoutSave()
nonUniqueUser."$field" = user."$field"
then: 'validation fails and the field has a unique constraint error'
!nonUniqueUser.validate()
nonUniqueUser.errors.errorCount == 1
nonUniqueUser.errors.getFieldError("$field").code == 'unique'
where:
field << ['username', '<some other field>']
}
}
But, when the test is run (using IntelliJ IDEA) TestDataConfig.groovy cannot be found via the following method in the plugin:
static Class getDefaultTestDataConfigClass() {
GroovyClassLoader classLoader = new GroovyClassLoader(TestDataConfigurationHolder.classLoader)
String testDataConfig = Holders.config?.grails?.buildtestdata?.testDataConfig ?: 'TestDataConfig'
try {
return classLoader.loadClass(testDataConfig)
} catch (ClassNotFoundException ignored) {
log.warn "${testDataConfig}.groovy not found, build-test-data plugin proceeding without config file"
return null
}
}
So the test continues on without a config file and I do not get uniquely generated data sets.
I've even tried explicitly including the file in Config.groovy:
grails.buildtestdata.testDataConfig = "TestDataConfig"
But, the same method in the plugin shows that Holders.config? is null.
I've looked at a few solutions to a similar problem here on StackOverflow with nothing working in my case; I cannot figure out how to get my app to detect the presence of the TestDataConfig.groovy file.
Any ideas? Thanks so much!

Grails unit tests not recognizing .save() on a domain class

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

How to mock a request when unit testing a service in grails

I am trying to unit test a service that has a method requiring a request object.
import org.springframework.web.context.request.RequestContextHolder as RCH
class AddressService {
def update (account, params) {
try {
def request = RCH.requestAttributes.request
// retrieve some info from the request object such as the IP ...
// Implement update logic
} catch (all) {
/* do something with the exception */
}
}
}
How do you mock the request object ?
And by the way, I am using Spock to unit test my classes.
Thank you
This code seems to work for a basic unit test (modified from Robert Fletcher's post here):
void createRequestContextHolder() {
MockHttpServletRequest request = new MockHttpServletRequest()
request.characterEncoding = 'UTF-8'
GrailsWebRequest webRequest = new GrailsWebRequest(request, new MockHttpServletResponse(), ServletContextHolder.servletContext)
request.setAttribute(GrailsApplicationAttributes.WEB_REQUEST, webRequest)
RequestContextHolder.setRequestAttributes(webRequest)
}
It can be added as a function to your standard Grails unit test since the function name does not start with "test"... or you can work the code in some other way.
One simple way to mock these, is to modify the meta class for RequestContextHolder to return a mock when getRequestAttributes() is called.
I wrote up a simple spec for doing this, and was quite surprised when it didn't work! So this turned out to be a quite interesting problem. After some investigation, I found that in this particular case, there are a couple of pitfalls to be aware of.
When you retrieve the request object, RCH.requestAttributes.request, you are doing so via an interface RequestAttributes that does not implement the getRequest() method. This is perfectly fine in groovy if the returned object actually has this property, but won't work when mocking the RequestAttributes interface in spock. So you'll need to mock an interface or a class that actually has this method.
My first attempt at solving 1., was to change the mock type to ServletRequestAttributes, which does have a getRequest() method. However, this method is final. When stubbing a mock with values for a final method, the stubbed values are simply ignored. In this case, null was returned.
Both these problems was easily overcome by creating a custom interface for this test, called MockRequestAttributes, and use this interface for the Mock in the spec.
This resulted in the following code:
import org.springframework.web.context.request.RequestContextHolder
// modified for testing
class AddressService {
def localAddress
def contentType
def update() {
def request = RequestContextHolder.requestAttributes.request
localAddress = request.localAddr
contentType = request.contentType
}
}
import org.springframework.web.context.request.RequestAttributes
import javax.servlet.http.HttpServletRequest
interface MockRequestAttributes extends RequestAttributes {
HttpServletRequest getRequest()
}
import org.springframework.web.context.request.RequestContextHolder
import spock.lang.Specification
import javax.servlet.http.HttpServletRequest
class MockRequestSpec extends Specification {
def "let's mock a request"() {
setup:
def requestAttributesMock = Mock(MockRequestAttributes)
def requestMock = Mock(HttpServletRequest)
RequestContextHolder.metaClass.'static'.getRequestAttributes = {->
requestAttributesMock
}
when:
def service = new AddressService()
def result = service.update()
then:
1 * requestAttributesMock.getRequest() >> requestMock
1 * requestMock.localAddr >> '127.0.0.1'
1 * requestMock.contentType >> 'text/plain'
service.localAddress == '127.0.0.1'
service.contentType == 'text/plain'
cleanup:
RequestContextHolder.metaClass = null
}
}