register
other register

Thursday, November 25, 2010

Mocking Command Object in Grails

Class PersonCommand {
  String firstname
  String lastname

  static constraints = {
    firstname(blank: false)
    lastname(blank: false)
  }
}

Class PersonController {
  def save {PersonCommand cmd ->
    if (cmd.hasErrors()) {
      ...
    }
    else {
      render (view: 'confirm', model: [person: cmd])
    }
  }
}

Create an instance of that command object and pass it to the action. Below also shows the way to pass on a map of parameters to construct the command object. This needs the help of the metaClass.

import grails.plugin.spock.ControllerSpec

Class PersonControllerSpec extends ControllerSpec {
  def 'save action' () {
    given "":
    mockCommandObject(PersonCommand)
    Map mp = [firstname: 'joe', lastname: 'blog']
    controller.metaClass.getParams { -> mp}

    when : "person form parameters are submitted"
    // This works
    // def cmd = new PersonCommand(firstname: 'joe', lastname: 'blog')

    // This works better as it allows to pass on a map of parameters
    def cmd = new PersonCommand(mp)

    // Call the action
    controller.save(cmd)

    then : "the command object should be valid"
    assert cmd.valid()

    and : "the right model and view can be retrieved"
    renderArgs.view = 'confirm'
    renderArgs.model.person.firstname == 'joe'
  }
}

Note that the mockCommandObject only works when doing controller unit test, not domain unit test.

Friday, November 19, 2010

Monday, November 15, 2010

Grails Controller Unit Test with setting params as a map

When running controller unit test. Setting the params individually like below works fine

controller.params.firstname = 'joe'
controller.params.lastname = 'blog'

However, if setting the params using a map like below:

controller.params = [firstname: 'joe', lastname: 'blog']

It would throw exceptions:

groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: params for class:

As suggested by Richard Bradley and Amit Jain in Grails mailing list, there are two solutions:

With groovy meta programming:

controller.metaClass.getParams { ->
 [firstname: 'Joe', lastName: 'blog']
}

Or use the putAll on the params:

Map myParams = [firstname: 'joe', lastname: 'blog']
controller.params.putAll(myParams)

Thursday, November 11, 2010

Integration Testing Mail function with Greenmail and Spock in Grails

Just managed to do spock integration test on my mail function running on grails.

I am using grails: 1.3.5, mail:  0.9,  spock: 0.4-groovy-1.7, greenmail: 1.2.1

RegistrationService will save the person's registration record to the database and then send a confirmation email to the person.
class RegistrationService {
def registrate (def person) {
person.save(faileOnError: true)
// when using mail plugin, mailService is automatically injected so we don't have to declare it.
// also valid: mailService.sendMail
sendMail {
multipart true
subject "Hello World"
to person.emailAddress
from "sender@test.com"
body (view: '/email/confirm', model: [person: person])
}
return person
}

To make greenmail integration work, make sure the following settings in the Config.groovy test environment. Please make sure there is no real mail server setting there, and also no real mail server settings in the conf/spring/resources.xml, otherwise a real email will be sent. Bear in mind, by using greenmail plugin for integration test, there is no real mail sent.

Previously I was doing refactoring on someone else's code and got stuck on receiving real email and fail the integration test. Thanks to Mike Hugo on nabble to pinpoint this. Here comes the settings in Config.groovy test environment:

test {
  grails.mail.port = com.icegreen.greenmail.util.ServerSetupTest.SMTP.port
  grails.mail.host = "localhost"
}
The spock integration test is as below:

import grails.plugin.spock.*;
import com.icegreen.greenmail.util.*

class RegistrationServiceIntegrationSpec extends IntegrationSpec{
def greenMail

def "create registration and send email"() {
given:
def service = new RegistrationService()

when: "a user makes a valid registration" 
def person = new Person(title: 'Mr', lastName: 'joe',
firstName: 'blog', 'dateOfBirth': new Date(),
emailAddress: 'receipt@test.com'
)
assert person.validate(), true
person == service.registrate(person)

then: "the registration should be successful and the confirmation email should be sent successfully"
assert 1 == greenMail.getReceivedMessages().length

def message = greenMail.getReceivedMessages()[0]
assert "sender@test.com" == GreenMailUtil.getAddressList(message.from)
assert "Hello World" == message.subject
}
}

Thursday, October 28, 2010

When Run (Ctrl+F11) gets stuck in Eclipse.

In Eclipse (or its derivative Spring STS 2.5.0) generically, Ctrl+F11 is Run and it is context aware. As if the cursor is in a Java class with a main method, it will run the run class; if the cursor is in a test class, it will run the test class; if the cursor is at the method line in a test class, it will run the test for this single method.

However, when a new Grails plugin is installed, or if you run "grails test-app -coverage" you will noticed from the console that: $app/web-app/WEB-INF/classes directory is deleted. And when you Ctrl+F11 to run again, it will have the following exceptions:


Class not found uk.ac.lse.LockerRegisterServiceUnitTests
java.lang.ClassNotFoundException: uk.ac.lse.LockerRegisterServiceUnitTests
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.loadClass(RemoteTestRunner.java:693)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.loadClasses(RemoteTestRunner.java:429)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:452)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Because the Default output folder is $app/web-app/WEB-INF/classes, that's why the test or run(main method for java) can't be run. 

To get your favourite Ctrl+F11 come back work for you, do the following:

1. Delete the project without deleting the content from Eclipse.
2. Import your project into Eclipse. 

/***** The following approach DOES NOT work ********/

Or better (29/10/2010)

1. Right click your project.
2. Select Grails Tools -> Refresh Dependencies

Then you will find the classes are compiled in to $app/target/classes which is the place your test or main java classes will run from.





Monday, October 25, 2010

Lockers App is Launched.

Very excited, my Online Locker Booking System will be launched today. It was built using Grails in 3 months.

550+ lockers will be booked within 1 hour, then the waiting list is built up until it reaches its max limit.

Monday, August 02, 2010

Grails Exception Handling with HTTP Status Code, URLMapping, and Email

The post describes how to:

1. Configure the UrlMapping.groovy to allow custom action on the specific http status code.
2. Display a customised "https status code handling page" to the user.
3. Send customised "https status code handling page" as an email to the admin staff if the admin email is configured in the Config.groovy file.

1. Configure the UrlMapping.groovy

class UrlMappings {
  static mappings {
    "403" (controller: "error", action: "forbidden")
    "404" (controller: "error", action: "notFound")
    "500" (controller: "error", action: "internalError")
  }
}


So any page status with 500 will be handled by the internalError action in the error controller.

Individual exceptions can be also configured in the response code to give more fine grained level control

"500" (controller: "error", action: "nullPointer", exception: NullPointerException)
  "500" (controller: "error", action: "illegalArgument", exception: IllegalArgumentException)

This allows any NullPointerException with 500 http status code to be handled by nullPointer action of error controller.


2. Display a customised "https status code handling page" to the user.

There are two properties available to the exception handling page. These properties are: exception and request. So you can copy the default error.gsp in the grails-app/view folder to a new file which is specified in the UrlMapping.groovy (i.e. error/internalError.gsp) and modified it to your needs.

...
Error ${request.'javax.servlet.error.status_code'}: ${request.'javax.servlet.error.message'.encodeAsHTML()}

Servlet: ${request.'javax.servlet.error.servlet_name'}

URI: ${request.'javax.servlet.error.request_uri'}

...

  

Stack Trace

${it.encodeAsHTML()}
...

The error page is as below:



3. Send customised "https status code handling page" as an email to the admin staff

The grails mail plugin is required to fulfil this requirement. Please refer to the Grail Mail plugin documentation

The following sending email code needs to be placed in the relevant action specified in the UrlMapping.groovy file.

import org.codehaus.groovy.grails.commons.ConfigurationHolder

class ErrorController {

  def mailService // mailService is provided by the grails mail plugin
 
  def index = { }
    
  def internalError = {
    // admin email is specified in the Config.groovy file
    // An email will be sent to the admin person whenever an internalError occurred. 
    def adminEmail = ConfigurationHolder.config.admin.email 
    try {
      mailService.sendMail {
 multipart true
 to adminEmail
 subject "500 Error"
 body (view: "/email/error")
      }
    }
    catch (Exception e) {
      log.info e
    }
  }
    
  def notFound = {}
}

The exception and request properties in the exception handling page are also available in the email template page. Thus the default error.gsp can be also copied to email template page (i.e. grails-app/email/error.gsp) and modified to your needs.

The error email is as below:

Tuesday, June 29, 2010

mockLogging in Grails Test

"By calling mockLogging(Class) in your unit test, any subsequently create instance of the given class will magically gain a working log property that echoes all log messages to the console. By default, debug and trace messages aren't included, to avoid excess output, but you can enable the debug messages by passing true as an optional second argument to mockLogging()." -- (Grails in Action)

class JetService {
  def doSomthing() {
    def a = "hello world"
    log.debug a
  }
}

class JetServiceUnitTests extends grails.test.GrailsUnitTestCase {
  void  testDoSomething() {
    mockLogging(JetService, true) // the second parameter "true" will output debug messages in the service method
    ...
  }
}

When running the unit test:

grails test-app unit:unit JetServiceUnit

In the report, you can see the debug message is shown in the System.output

Wednesday, June 23, 2010

shouldFail in Groovy Testing

If the program hasn't got code to catch the runtime unchecked exceptions, then shouldFail can be used during groovy testing. http://groovy.codehaus.org/Unit+Testing

shouldFail() allows you to check that a particular lock of code throws an exception.

shouldFail (NullPointerException) {
    testService.methodA('xxx')
}

However runtime unchecked exceptions can sometimes be considered as bugs in the programme and should be dealt with.

Monday, June 07, 2010

Grails Controller Unit Test

import grails.converters.JSON

class PersonController {
    def personService

    def jsonPeople = {
        def people = personService.getPerson('usrname', 'passwd')
        render people as JSON
    }
}



class PersonService {

    static transactional = true

    def getPerson(String usrname, String password) {
        // some business logic
     return [firstname: 'joe', lastname: 'blog']
    }
}



import grails.test.*
import grails.converters.JSON

class PersonControllerTests extends ControllerUnitTestCase {
protected void setUp() {
super.setUp()
}

protected void tearDown() {
super.tearDown()
}

void testJsonResponse() {
controller.params.username= 'usrname'
controller.params.password = 'passwd'
              
                // mock the colloaborator
def personControl = mockFor(PersonService)
personControl.demand.getPerson(1..1) { String x, String y ->
                        // the number of parameters and return type have to match the method
return [firstname: 'joe', lastname: 'blog']
}
controller.personService = personControl.createMock()
controller.jsonPeople()

//parse the JSON
def controllerResponse = controller.response.contentAsString
def jsonResult = JSON.parse(controllerResponse)

//navigate the JSON as an object
assertEquals 'joe', jsonResult.username
assertEquals 'blog', jsonResult.password

}
}

Friday, May 21, 2010

JMeter HTTP Proxy Server Settings

The procedures below shows how to set up a HTTP proxy server using JMeter together with Firefox.

At WorkBench, add a HTTP Proxy Server.



In the newly added HTTP Proxy Server control panel.

  • Add "text/html" in the Content-type filter include field, so that all the html format page request will be filtered in.
  • In the URL Patterns to Exclude, add .*\.jpg  .*\.js  .*\.png  .*\.gif  .*\.css

The url request containing these extension will not shown on the JMeter left panel, as we are not interested in requesting these files for our testing purpose.

  • Change "port" to 8090, if your application is running under 8080
  • Click Start to start the proxy server.



Launch Firefox, in the Tools -> Options -> Network -> Settings, set the Manual proxy configuration to:
HTTP Proxy: localhost
Port: 8090 (this should be the same as the setting in the JMeter HTTP Proxy Server setting)


Now type any web address in Firefox, you will see only those url (e.g. /myApp/myController/index) request interested to us will come through in the left panel of the JMeter.

If there are other url  request which are not interested to us, then simply add their url patterns to the URL Patterns to Exclude section in the HTTP Proxy Server settings in the JMeter.

p.s. remember to put port 80 in the port field in HttpRequest Client

There are video resources which give introductions on JMeter:

http://vimeo.com/10164982
http://vimeo.com/3453772

Thursday, April 29, 2010

Convert GroovyRowResult into Domain Class

There are chances that you might want to convert a GroovyRowResult into a domain class. For example, in the clustered environment, certain class needs to "implments Serializable" but if there is GroovyRowResult is in the class, then it will throw  java.io.NotSerializableException as GroovyRowResult can not be serialized.

The workaround is simply create a dummy domain class with properties match the result set fields. It is case sensitive.

If it is oracle database, then it is case sensitive.

GroovyRowResult row = connection.firstRow("select user.firstname as \"firstname\"......")
def person = new Peson (row)

Monday, April 19, 2010

Number format

When comes to custom number formatting, you might have a user case like room number 001, 075. How to format this:


NumberFormat formatter = new DecimalFormat("000")
def s
s = formatter.format(1) // the result will be 001
s = formatter.format(75) // the result will be 075

Monday, March 15, 2010