Grails: Testing constraint error messages using spock - unit-testing

I am currently having issues validating my constraint messages in spock using grails 2.4.2. When testing i keep getting the default error message rather than my custom message. However when i print to a view using grails error tag i get the correct error message. I have even deleted the default from message.propperties.
Here is what my spec looks like:
import com.fishLogger.user.User
import com.fishLogger.DomainErrorHandling
import grails.test.mixin.TestFor
import org.springframework.context.MessageSource
import spock.lang.Specification
/**
* See the API for {#link grails.test.mixin.domain.DomainClassUnitTestMixin} for usage instructions
*/
#TestFor(User)
class UserSpec extends Specification {
MessageSource messageSource
def setup() {
DomainErrorHandling.enable(new User())
}
def cleanup() {
}
void "test Invalid User Name Errors"(){
when:
User user = new User(userName:userName, password:password, email:email)
//user.errors.allErrors.get(0).code
user.validate()
then:
user.errorMessage()==expectedMessage
where:
userName | password | email | expectedMessage
"" |"vailidPassword" | "thisworks#email.com" | "User name must be entered"
//"asd" |"vailidPassword" | "thisworks#email.com" | "User name must be atleast 6 chacters long"
//" " |"vailidPassword" | "thisworks#email.com" | "User name must be entered"
//"aaaaaaaaaaaaaaaaaaaaa" |"vailidPassword" | "thisworks#email.com" | "User name can not be greater than 20 characters"
}
Where enable is a call loading a helping method to the domain to grab the error message
class DomainErrorHandling {
public static void enable(Object obj){
obj.class.metaClass.errorMessage = {
MessageSource messageSource = Holders.applicationContext.getBean("messageSource")
def errorString = delegate?.errors?.allErrors?.collect{error ->
messageSource.getMessage(error,Locale.default)
}?.join(' \n')
return errorString
}
}
}
Any help or guidance would be great. I know that testing the error messages should be more of a UI test but i would really prefer to test them with my unit tests as well.

As for why it still shows the old message when you delete it from message.properties, check out the getDefaultMessage() in MessageSourceResolvable. I'm guessing that your custom message isn't visible to the test, either because its key is misspelled or some Grails test bootstrap problem.
I would try printing out the getCodes() of the error in your errorMessage() method, and see if any of the codes listed is the key to one of your custom ones in message.properties.

Assuming a Book domain class with a non nullable name property you can do the following:
#Mock(Book)
class BookSpec extends Specification {
StaticMessageSource messageSource
def setup () {
messageSource = (StaticMessageSource)grailsApplication.mainContext.getBean('messageSource')
}
def "check name validation error message" () {
given:
messageSource.addMessage ("nullable.name", Locale.default, "Null!")
Book book = new Book (name:null)
when:
book.validate ()
then:
messageSource.getMessage (book.errors.getFieldError ('name'), Locale.default) == "Null!"
}
}

Related

Set-up/clean-up only once per feature method with 'where:' block

Spock has setupSpec on the Spec class level. I would want to have something similar for a single test case level.
This might not be available in Spock, Does someone has a workaround for this.
void "test something"() {
setup:
User user = createUser()
when:
user.setAdress(new Address(zipCode: inputZipCode, city: inputCity))
then:
user.address.city == inputCity
user.address.zipCode == inputZipCode
cleanup:
deleteUser(user)
where:
inputCity | inputZipCode
"a" |"1"
"b" |"2"
}
Creating and deleting user is unnecessarily done after every iteration.
Could it be possible to have something la- setupSpec for a single test instead of class-level?
It is possible to manipulate the test cases to use class-setupSpec/CleanupSpec or even create a new test (with #Stepwise) to achieve this but I am looking for something good solution not a hack.
I think this is very ugly because it involves manual bookkeeping. I do not recommend you to do it like this, but anyway:
package de.scrum_master.stackoverflow.q57721328
import spock.lang.See
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll
class OneTimeSetupCleanupParametrisedTest extends Specification {
#Shared User user
#Shared int iteration
User createUser() {
// Set up test fixture only if iteration == 0 (or optionally, if fixture is uninitialised)
user ?: new User()
}
void deleteUser(User userInstance) {
// Clean up counter (and test fixture, if any) only if iteration == total number of iterations
if (++iteration == specificationContext.currentIteration.estimatedNumIterations) {
userInstance.delete()
user = null
iteration = 0
}
}
// #Unroll
void "test something"() {
setup:
// Call initialiser helper for each iteration, relying on the fact that it will only
// create a text fixture if none exists yet
user = createUser()
when:
user.setAdress(new Address(zipCode: inputZipCode, city: inputCity))
then:
user.address.city == inputCity
user.address.zipCode == inputZipCode
cleanup:
// Call clean-up helper for each iteration, relying on the fact that it will only
// clean up the fixture during the last iteration
deleteUser(user)
where:
inputCity | inputZipCode
"a" | "1"
"b" | "2"
}
static class User {
Address address
User() {
println "creating user"
}
void setAdress(Address address) {
this.address = address
}
void delete() {
println "deleting user"
address = null
}
}
static class Address {
String zipCode, city
}
}
Console log:
creating user
deleting user
Update: The Spock manual says about this topic:
Sharing of Objects between Iterations
In order to share an object between iterations, it has to be kept in a #Shared or static field.
NOTE: Only #Shared and static variables can be accessed from within a where: block.
Note that such objects will also be shared with other methods. There is currently no good way to share an object just between iterations of the same method. If you consider this a problem, consider putting each method into a separate spec, all of which can be kept in the same file. This achieves better isolation at the cost of some boilerplate code.

How do I unit test a taglib that calls g.formatDate?

I have a tag library that calls formatDate:
out << g.formatDate(attrs)
In my unit test I have the following:
def output = applyTemplate('<tz:formatDate date="${date}"/>', [date: date])
When I run the test I get the following error:
org.grails.taglib.GrailsTagException: [Byte array resource [test_1520620408798]:1] Error executing tag <tz:formatDate>: Cannot invoke method getTimeZone() on null object
at org.grails.gsp.GroovyPage.throwRootCause(GroovyPage.java:472)
at org.grails.gsp.GroovyPage.invokeTag(GroovyPage.java:415)
at test_1520620408798.run(test_1520620408798:15)
at org.grails.gsp.GroovyPageWritable.doWriteTo(GroovyPageWritable.java:162)
at org.grails.gsp.GroovyPageWritable.writeTo(GroovyPageWritable.java:82)
at grails.testing.web.GrailsWebUnitTest$Trait$Helper.renderTemplateToStringWriter(GrailsWebUnitTest.groovy:242)
at grails.testing.web.GrailsWebUnitTest$Trait$Helper.applyTemplate(GrailsWebUnitTest.groovy:227)
at grails.testing.web.taglib.TagLibUnitTest$Trait$Helper.applyTemplate(TagLibUnitTest.groovy:49)
at grails.testing.web.GrailsWebUnitTest$Trait$Helper.applyTemplate(GrailsWebUnitTest.groovy:212)
at grails.testing.web.taglib.TagLibUnitTest$Trait$Helper.applyTemplate(TagLibUnitTest.groovy:44)
at com.captivatelabs.grails.timezone.detection.FormatTagLibSpec.test offset client to server time - formatDate(FormatTagLibSpec.groovy:22)
Caused by: java.lang.NullPointerException: Cannot invoke method getTimeZone() on null object
at org.grails.plugins.web.taglib.FormatTagLib$_closure2.doCall(FormatTagLib.groovy:170)
at groovy.lang.Closure.call(Closure.java:414)
at org.grails.taglib.TagOutput.captureTagOutput(TagOutput.java:64)
at org.grails.taglib.TagLibraryMetaUtils.methodMissingForTagLib(TagLibraryMetaUtils.groovy:139)
at org.grails.taglib.NamespacedTagDispatcher.methodMissing(NamespacedTagDispatcher.groovy:59)
at com.captivatelabs.grails.timezone.detection.FormatTagLib$_closure1.doCall(FormatTagLib.groovy:14)
at groovy.lang.Closure.call(Closure.java:414)
at org.grails.gsp.GroovyPage.invokeTagLibClosure(GroovyPage.java:439)
at org.grails.gsp.GroovyPage.invokeTag(GroovyPage.java:364)
... 9 more
Does anyone have any thoughts on this?
There are a number of ways you could orchestrate this. One is demonstrated in the project at https://github.com/jeffbrown/pietertest.
https://github.com/jeffbrown/pietertest/blob/master/grails-app/taglib/pieter/DemoTagLib.groovy
package pieter
class DemoTagLib {
static defaultEncodeAs = [taglib:'html']
static namespace = 'tz'
def formatDate = { attrs ->
out << g.formatDate(date: attrs.date, format: 'yyyy-MM-dd')
}
}
https://github.com/jeffbrown/pietertest/blob/master/src/test/groovy/pieter/DemoTagLibSpec.groovy
package pieter
import grails.testing.web.taglib.TagLibUnitTest
import org.grails.plugins.web.DefaultGrailsTagDateHelper
import spock.lang.Specification
class DemoTagLibSpec extends Specification implements TagLibUnitTest<DemoTagLib> {
Closure doWithSpring() {{ ->
grailsTagDateHelper DefaultGrailsTagDateHelper
}}
void "test date format"() {
given:
def date
Calendar cal = Calendar.instance
cal.with {
clear()
set MONTH, JULY
set YEAR, 1776
set DATE, 4
date = time
}
when:
def output = applyTemplate('<tz:formatDate date="${date}"/>', [date: date])
then:
output == '1776-07-04'
}
}
I hope that helps.

Grails String.encodeAsBase64() Fails in Spock Tests

This code works fine when I run it in Grails.
String getLoginToken() {
generatePassword()
passwordExpired = false
[email, password].join(',').encodeAsBase64()
}
However, this Spock test fails
def "test getLoginToken"() {
setup:
String email = "bacon#eggs.edu"
Person person = new Person(email: email)
when:
String token = person.getLoginToken()
then:
token.decodeBase64() == "$email,$person.password"
}
with the following exception
| Failure: test getLoginToken(com.campuscardtools.myphotoid.PersonSpec)
| groovy.lang.MissingMethodException: No signature of method: java.lang.String.encodeAsBase64() is applicable for argument types: () values: []
Possible solutions: decodeBase64()
at com.campuscardtools.myphotoid.Person$$EPFScS6i.getLoginToken(Person.groovy:140)
at com.campuscardtools.myphotoid.PersonSpec.test getLoginToken(PersonSpec.groovy:68)
My understanding is that Groovy provides the encodeAsBase64() on the String class (see: http://mrhaki.blogspot.com/2009/11/groovy-goodness-base64-encoding.html), so why doesn't this work in the unit test?
Rather than
"Blah".encodeAsBase64()
You need
"Blah".encodeBase64()
Without the 'As'
You could also include a mockCodec for the method you're using.
and: "add the Base64 codec"
mockCodec(org.codehaus.groovy.grails.plugins.codecs.Base64Codec)
This works, but I fells like a bad hack. Surely there must be cleaner solution:
def cleanup () {
String.metaClass = null
}
def "test getLoginToken"() {
setup:
String email = "bacon#eggs.edu"
Person person = new Person(email: email)
String encoded = null
and:
String.metaClass.encodeAsBase64 {
encoded = delegate
return delegate
}
when:
String token = person.getLoginToken()
then:
token == "$email,$person.password"
encoded == "$email,$person.password"
}

How can I override the test method name that appears on the TestNG report?

How can I override the test name that appears on the TestNG report? I want to override the name that appears in the middle column (currently shows as the method name). Is this even possible?
I tried to do it like this, but it didn't work.
public class EchApiTest1 extends TestBase {
...
#BeforeTest
public void setUp() {
restClient = new RestClientPost();
this.setTestName( "ech: XXXXXX" );
}
And, the base class:
import org.testng.ITest;
public class TestBase implements ITest {
String testName = "";
#Override
public String getTestName() {
return this.testName;
}
public void setTestName( String name ) {
this.testName = name;
}
}
NOTE: The above code does work when I am viewing the report detail in the Jenkins TestNG plugin report, which shows the overridden test name as a string called "Instance Name:" at the beginning of the Reporter log output. Why, in this case, WHY does a "setTestName()" method alter a string labeled "Instance Name" in the report?
One answer I found had a suggestion like this but I don't know how to pass an ITestResult arg to a AfterMethod method:
#AfterMethod
public void setResultTestName( ITestResult result ) {
try {
BaseTestMethod bm = (BaseTestMethod)result.getMethod();
Field f = bm.getClass().getSuperclass().getDeclaredField("m_methodName");
f.setAccessible(true);
f.set( bm, bm.getMethodName() + "." + your_customized_name );
} catch ( Exception ex ) {
Reporter.log( "ex" + ex.getMessage() );
}
Thoughts?
Please find following code for set custom name of testcase in TestNG reports.
Following features are available in this code.
Dynamic execution on same test-case in multiple time
Set custom test-case name for reports
Set parallel execution of multiple test-cases execution
import java.lang.reflect.Field;
import org.testng.ITest;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Factory;
import org.testng.annotations.Test;
import org.testng.internal.BaseTestMethod;
import com.test.data.ServiceProcessData;
public class ServiceTest implements ITest {
protected ServiceProcessData serviceProcessData;
protected String testCaseName = "";
#Test
public void executeServiceTest() {
System.out.println(this.serviceProcessData.toString());
}
#Factory(dataProvider = "processDataList")
public RiskServiceTest(ServiceProcessData serviceProcessData) {
this.serviceProcessData = serviceProcessData;
}
#DataProvider(name = "processDataList", parallel = true)
public static Object[] getProcessDataList() {
Object[] serviceProcessDataList = new Object[0];
//Set data in serviceProcessDataList
return serviceProcessDataList;
}
#Override
public String getTestName() {
this.testCaseName = "User custom testcase name";
// this.testCaseName = this.serviceProcessData.getTestCaseCustomName();
return this.testCaseName;
}
#AfterMethod(alwaysRun = true)
public void setResultTestName(ITestResult result) {
try {
BaseTestMethod baseTestMethod = (BaseTestMethod) result.getMethod();
Field f = baseTestMethod.getClass().getSuperclass().getDeclaredField("m_methodName");
f.setAccessible(true);
f.set(baseTestMethod, this.testCaseName);
} catch (Exception e) {
ErrorMessageHelper.getInstance().setErrorMessage(e);
Reporter.log("Exception : " + e.getMessage());
}
}}
Thanks
I found a "workaround" but I am hoping for a better answer. I want to be able to show this "test name" OR "instance name" value on the HTML report (not just within the Reporter.log output) and I am starting to think its not possible :
#Test(dataProvider = "restdata2")
public void testGetNameFromResponse( TestArguments testArgs ) {
this.setTestName( "ech: " + testArgs.getTestName() );
Reporter.log( getTestName() ); // this magic shows test name on report
....
With this workaround, the user can now identify which test it was by looking at the Reporter.log output but I still wish the name was more prominant.
I suspect the answer lies in writing a TestListenerAdapter that somehow overrides the ITestResult.getTestNameMethod() method? That is the holy grail I am looking for.
The ‘result’ object will automatically pass in the method setResultTestName( ITestResult result )
Make sure you put alwaysRun=true like the following when you have groups defined in your test class otherwise “AfterMethod” will not be excuted.
#AfterMethod (alwaysRun=true)

How to mock Shiro accessControl() method in grails unit test case

I am using shiro security in my grail application.
Grails version : 2.2.1
shiro : 1.2.0
I have a problem in writing grails unit test case for the controller with filter enabled. When the test case run without filters then it is working fine, if it runs withFilters then it is failing for accessControl() method not found in the controller. I dont know how to make Shiro's api to be visible while running the test case.I referred shiro unit test case link http://shiro.apache.org/testing.html but I couldn't get any info regarding accessControl().I have given sample code how my classes and test case looks like
MyController.groovy
def create() {
// getting request parameters and validation
String emailId = params.emailId
def ret = myService.createUser(emailId)
return ret
}
MyControllerFilters.groovy
def filters = {
loginCheck(controller: 'user') {
before = {
//do some business checks
// Access control by convention.
accessControl() // This is a dynamic method injected by ShiroGrailsPlugin to FilterConfig, but this is not visible during the test case.
}
}
MyControllerTests.groovy
#TestFor(MyController)
#Mock(MyControllerFilters)
class MyControllerTests {
#Before
void setup() {
// initializing some variables
}
void testCreateUserWithFilter() {
request.accessAllowed = true
withFilters(action:"create") {
controller.create()
}
assert response.message == "success"
}
}
Try to use:
ControllerOrService.metaClass.method = {
return "" //what you need to be returned
}
Add parameters to closure if method take parameters:
ControllerOrService.metaClass.method = { def a, def b ->
return a + b
}
Don't forget to use full name of method when you mock them in that way.