How to use a custom codec in a Grails unit test - grails

I am working through a Grails tutorial from InfoQ called Getting Started With Grails, Second Edition, and I am trying to add a custom codec to a unit test. My environment is Grails 1.3.7 and Groovy 1.7.8.
So the codec is SHACodec.groovy, and it lives in grails-app/utils. The contents are:
import java.security.MessageDigest
class SHACodec{
static encode = {target->
MessageDigest md = MessageDigest.getInstance('SHA')
md.update(target.getBytes('UTF-8'))
return new String(md.digest()).encodeAsBase64()
}
}
The codec works just fine when I log into the app. It's being used for a password field in my UserController.authenticate()
def authenticate = {
def user =
User.findByLoginAndPassword(params.login, params.password.encodeAsSHA())
if(user){
session.user = user
flash.message = "Hello ${user.login}!"
redirect(controller:"race", action:"list")
}else{
flash.message = "Sorry, ${params.login}. Please try again."
redirect(action:"login")
}
}
When I add this to a unit test, the following error displays:
No such property: SHACodec for class:
racetrack.UserControllerTests
groovy.lang.MissingPropertyException:
No such property: SHACodec for class:
racetrack.UserControllerTests at
racetrack.UserControllerTests.testAuthenticate(UserControllerTests.groovy:39)
The test is:
package racetrack
import org.codehaus.groovy.grails.plugins.codecs.*
import grails.test.*
class UserControllerTests extends ControllerUnitTestCase {
protected void setUp() {
super.setUp()
String.metaClass.encodeAsSHA = {->
SHACodec.encode(delegate)
}
}
protected void tearDown() {
super.tearDown()
}
void testAuthenticate(){
def jdoe = new User(login:"jdoe", password:"password".encodeAsSHA())
mockDomain(User, [jdoe])
controller.params.login = "jdoe"
controller.params.password = "password"
controller.authenticate()
assertNotNull controller.session.user
assertEquals "jdoe", controller.session.user.login
controller.params.password = "foo"
controller.authenticate()
assertTrue controller.flash.message.startsWith(
"Sorry, jdoe")
}
I found two different recommendations so far.
First, call the codec directly from the test:
SHACodec codec = new SHACodec()
codec.encode("password")
Second, use an integration test instead of a unit test.
The results were the same for each option. What is the small thing I am missing?
Thanks in advance for your time!

you are not importing your codec--you are importing the default grails codecs. You just need to make the import for the codec in the test match the exact package path of where the codec lives. If you are using an IDE, let it try to import the codec for you...

Use the loadCodec method of the GrailsUnitTestCase. It's very simple. See the example bellow:
import grails.test.*
import org.codehaus.groovy.grails.plugins.codecs.HTMLCodec
class SomeTests extends GrailsUnitTestCase {
protected void setUp() {
super.setUp()
loadCodec(HTMLCodec)
}
protected void tearDown() {
super.tearDown()
}
void testSomething() {
assertEquals "<p>Hello World</p>", "<p>Hello World</p>".encodeAsHTML()
}
}

The answer from #hvgotcodes is correct, but you also need to add the grails-app/utils folder to your Java Build Path before the import works (at least that's what I needed to do with SpringSource Eclipse IDE).

Related

Jenkins scripted Pipeline: How to apply #NonCPS annotation in this specific case

I am working on a scripted Jenkins-Pipeline that needs to write a String with a certain encoding to a file as in the following example:
class Logger implements Closeable {
private final PrintWriter writer
[...]
Logger() {
FileWriter fw = new FileWriter(file, true)
BufferedWriter bw = new BufferedWriter(fw)
this.writer = new PrintWriter(bw)
}
def log(String msg) {
try {
writer.println(msg)
[...]
} catch (e) {
[...]
}
}
}
The above code doesn't work since PrintWriter ist not serializable so I know I got to prevent some of the code from being CPS-transformed. I don't have an idea on how to do so, though, since as far as I know the #NonCPS annotation can only be applied to methods.
I know that one solution would be to move all output-related code to log(msg) and annotate the method but this way I would have to create a new writer every time the method gets called.
Does someone have an idea on how I could fix my code instead?
Thanks in advance!
Here is a way to make this work using a log function that is defined in a shared library in vars\log.groovy:
import java.io.FileWriter
import java.io.BufferedWriter
import java.io.PrintWriter
// The annotated variable will become a private field of the script class.
#groovy.transform.Field
PrintWriter writer = null
void call( String msg ) {
if( ! writer ) {
def fw = new FileWriter(file, true)
def bw = new BufferedWriter(fw)
writer = new PrintWriter(bw)
}
try {
writer.println(msg)
[...]
} catch (e) {
[...]
}
}
After all, scripts in the vars folder are instanciated as singleton classes, which is perfectly suited for a logger. This works even without #NonCPS annotation.
Usage in pipeline is as simple as:
log 'some message'

How to wrap all Grails service methods with a Groovy closure?

Grails 2.4.x here.
I have a requirement that all the methods of all my Grails services, generated by grails create-service <xyz>, be "wrapped"/intercepted with the following logic:
try {
executeTheMethod()
} catch(MyAppException maExc) {
log.error(ExceptionUtils.getStackTrace(maExc))
myAppExceptionHandler.handleOrRethrow(maExc)
}
Where:
log.error(...) is the SLF4J-provided logger you get when you annotate your class with the #Slf4j annotation; and
ExceptionUtils is the one from org.apache.commons:commons-lang3:3.4; and
myAppExceptionHandler is of type com.example.myapp.MyAppExceptionHandler; and
This behavior exists (or has the option to exist in the event that it needs to be explicitly called somehow) for each method defined in a Grails service
So obviously this wrapper code needs to include import statements for those classes as well.
So for example if I have a WidgetService that looks like this:
class WidgetService {
WidgetDataService widgetDataService = new WidgetDataService()
Widget getWidgetById(Long widgetId) {
List<Widget> widgets = widgetDataService.getAllWidgets()
widgets.each {
if(it.id.equals(widgetId)) {
return it
}
}
return null
}
}
Then after this Groovy/Grails/closure magic occurs I need the code to behave as if I had written it like:
import groovy.util.logging.Slf4j
import org.apache.commons.lang3.exception.ExceptionUtils
import com.example.myapp.MyAppExceptionHandler
#Slf4j
class WidgetService {
WidgetDataService widgetDataService = new WidgetDataService()
MyAppExceptionHandler myAppExceptionHandler = new MyAppExceptionHandler()
Widget getWidgetById(Long widgetId) {
try {
List<Widget> widgets = widgetDataService.getAllWidgets()
widgets.each {
if(it.id.equals(widgetId)) {
return it
}
}
return null
} catch(MyAppException maExc) {
log.error(ExceptionUtils.getStackTrace(maExc))
myAppExceptionHandler.handleOrRethrow(maExc)
}
}
}
Any ideas as to how I might be able to achieve this? I'm worried that a pure Groovy closure might interfere somehow with whatever Grails is doing to its services under the hood at runtime (since they are all classes that don't explicitly extend a parent class).
Here is what I was trying to pin point in my comment:
package com.example
import groovy.util.logging.Log4j
#Log4j
trait SomeTrait {
def withErrorHandler(Closure clos) {
try {
clos()
} catch(Exception e) {
log.error e.message
throw new ApplicationSpecificException(
"Application Specific Message: ${e.message}"
)
}
}
}
Service class:
package com.example
class SampleService implements SomeTrait {
def throwingException() {
withErrorHandler {
throw new Exception("I am an exception")
}
}
def notThrowingException() {
withErrorHandler {
println "foo bar"
}
}
}
Test:
package com.example
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(SampleService)
class SampleServiceSpec extends Specification {
void "test something"() {
when:
service.throwingException()
then:
ApplicationSpecificException e = thrown(ApplicationSpecificException)
e.message == "Application Specific Message: I am an exception"
}
void "test something again"() {
when:
service.notThrowingException()
then:
notThrown(Exception)
}
}
Here is the sample app.
Grails 3.0.9 but it should not matter. this is applicable for Grails 2.4.*
You can intercept the calls to your Service class methods either using MetaInjection or Spring AOP. So you don't have to write closure in each Service class. You can look into this blog that explains both the approaches with examples.

How to get the spring bean instance of a service, added via dependency injection in webflow

I would like to mock a service method in an integration test for one test, however I don't know how to get a reference to the service as it's added to the controller via dependency injection. To further complicate things the service is in a webflow, but I know it's not stored in the flow as the service is not serialized.
Ideal mocking scenario:
Get reference to the service
Mock the method via the metaClass
Main test
Set the metaClass to null so it's replaced with the original
Methods like mockFor so far don't seem to effect the service.
Example of the setup:
Controller:
package is.webflow.bad
import is.webflow.bad.service.FakeService
class FakeController
{
def index = {
redirect(action: 'fake')
}
def fakeFlow = {
start {
action {
flow.result = fakeService.fakeCall()
test()
}
on('test').to('study')
}
study {
on('done').to('done')
}
done {
System.out.println('done')
}
}
}
Service:
package is.webflow.bad.service
class FakeService
{
def fakeCall()
{
return 'failure'
}
}
Test:
package is.webflow.bad
import static org.junit.Assert.*
import grails.test.WebFlowTestCase
import is.webflow.bad.service.FakeService
import org.junit.*
class FakeControllerFlowIntegrationTests extends WebFlowTestCase
{
def controller = new FakeController()
def getFlow() { controller.fakeFlow }
String getFlowId() { "fake" }
#Before
void setUp() {
// Setup logic here
super.setUp()
}
#Test
void testBasic()
{
startFlow()
assertCurrentStateEquals 'study'
assertEquals 'failure', getFlowScope().result
}
#Test
void testServiceMetaClassChange()
{
// want to modify the metaClass here to return success
startFlow()
assertCurrentStateEquals 'study'
assertEquals 'success', getFlowScope().result
}
}
You can inject the service into your Integration test using "#AutoWired" or using application context you get reference. Am i missing something?
#Autowired
private YourService yourservice;
or
#Autowired
private ApplicationContext appContext;
YourService yourService = (YourService)appContext.getBean("yourService");
Here you go:
void "test something"() {
given: "Mocked service"
someController.someInjectedService = [someMethod: { args ->
// Mocked code
return "some data"
] as SomeService
when: "Controller code is tested"
// test condition
then: "mocked service method will be called"
// assert
}

Integration test - how to test a method with no inputs and a render

How does one integration-test (Grails) a controller with no inputs and a render view? Here is my controller code and test; the test looks correct but throws a "java.lang.Exception: No tests found matching grails test target pattern filter" error. many thanks for your help. -ryan
Controller code section:
class ReportSiteErrorsController {
static allowedMethods = [reportError: 'GET', saveReportError: 'POST']
def mailService
#Transactional(readOnly = true)
def reportError() {
render(view:'reportError', model:[])
}
}
Integration Test:
#TestFor(ReportSiteErrorsController)
class ReportSiteErrorsControllerTests extends DbunitGroovyTestCase {
#Test
void "test report error"() {
controller.reportError()
assert view == "reportError"
}
}
Leave the extension off in your command line. You are supposed to be specifying the name of the test, not the name of the file in which it is defined.
Use something like...
grails test-app integration: NameOfTest
Not something like...
grails test-app integration: NameOfTest.groovy
I hope that helps.
in Grails 2.4 the default syntax to test render "hello" would be:
import grails.test.mixin.TestFor
import spock.lang.Specification
/**
* See the API for {#link grails.test.mixin.web.ControllerUnitTestMixin} for usage instructions
*/
#TestFor(SimpleController)
class SimpleControllerSpec extends Specification {
def setup() {
}
def cleanup() {
}
void "test something"() {
controller.index()
expect:
response.text == "hello"
}
}
The assert won't do for me. See also: the Grails Documentation

Testing Service in Grails produces 'org.junit.ComparisonFailure: expected:<An[a]nymous> but was:<An[o]nymous>' error

I'm working my way through 'Grails in Action' and I'm running into an issue when trying to write an Integration test for one of my services.
I realize that I'm using Grails 2.0.3 whereas the book was written with Grails 1.x.x in mind.
Here is my Service:
package qotd
class QuoteService {
boolean transactional = true
def getRandomQuote(){
def allQuotes = Quote.list()
def randomQuote
if(allQuotes.size() > 0){
def randomIndex = new Random().nextInt(allQuotes.size())
randomQuote = allQuotes[randomIndex]
}
else{
randomQuote = getStaticQuote()
}
return randomQuote
}
def getStaticQuote(){
return new Quote(author: "Anonymous",
content: "Real Programmers Don't eat quiche")
}
}
And below is my Integration Test, located in '/test/integration/qotd/'
package qotd
import static org.junit.Assert.*
import org.junit.*
class QuoteServiceIntegrationTests extends GroovyTestCase {
def quoteService
#Before
void setUp() {
}
#After
void tearDown() {
}
#Test
void testStaticQuote() {
def staticQuote = quoteService.getStaticQuote()
assertNotNull quoteService
assertEquals "Ananymous",staticQuote.author
assertEquals "Real Programmers Don't Eat Quiche",staticQuote.content
}
}
Just in case it may be relevant, here is the Quote class that I'm testing the contents of above:
package qotd
class Quote {
String content
String author
Date created = new Date()
static constraints = {
author(blank:false)
content(maxSize:1000,blank:false)
}
}
When I run my test, using 'test-app -integration' I get the following:
Running 1 integration test... 1 of 1
Failure: testStaticQuote(qotd.QuoteServiceIntegrationTests)
org.junit.ComparisonFailure: expected: An[a]nymous but was:An[o]nymous
at org.junit.Assert.assertEquals(Assert.java:125)
at org.junit.Assert.assertEquals(Assert.java:147)
at qotd.QuoteServiceIntegrationTests.testStaticQuote(QuoteServiceIntegrationTests.groovy:24)
Any insight would be appreciated. Thank you all!
you spelled "Anonymous" incorrectly on this line
assertEquals "Ananymous",staticQuote.author

Resources