alexgorbatchev

Friday, December 13, 2013

Jasmine Testing Framework - syntax comparison to Junit and Spock


Example Jasmine Test Suite

describe('This is my test suite', function () {
  var testObject;  

  beforeEach(function(){
    testObject = new TestObject();
  });

  it('This is my test', function(){
   expect(testObject.doSomething()).toEqual('Some Value');
  });

});
JasmineJUnitSpock
describeTestCaseSpecification
Describes a feature of an application and can be nested inside on another.

beforeEach@Before / setUp()setup()
Piece of code run before each test used to initialize testing objects.

it@Test<test function>()
Runs one test of the larger test suite.  Contains a test name and a function.

expect & (toEqual(), toBeTruthy(), etc)assert*()Any conditions in an expect: block
Conditions to check during the test.

afterEach@After / tearDown()cleanup()
Piece of code run after every test to tear down or destroy any testing objects.

Friday, November 22, 2013

Grails Cobertura Plugin Excludes

After setting up the Cobertura Plugin in my Jenkins-Grails build, I was a little surprised to see my build failing because of low test coverage, since I have unit tests to cover nearly every line of the domain, controller, and service classes.  What I failed to consider is the Cobertura plugin believes anything with a .groovy extension is a source file and should have tests.  This includes database migration scripts (i.e. changelog.groovy), non-standard config files, and other miscellaneous application files with a .groovy extension.  Therefore, to have the Jenkins-Cobertura plugin report and more importantly use accurate metrics in evaluating the quality of the build, you must exclude those files.  The coverage exclusions are based on package, so the easiest way to exclude them is to put all of your 'real' code into packages (which should be done anyway) and exclude all default package 'classes' with the following config block in BuildConfig.groovy

coverage {
    //('*') The Single asterik excludes top-level groovy classes/scripts, such as BootStrap, changelog (Database Migration), ApplicationResources, etc
    exclusions = ['*']

    // Creates xml output that can be parsed in the Jenkins-Cobertura plugin
    xml = true
    
    //Keeps the coverage results from a previous set of unit tests (see: https://github.com/beckje01/grails-code-coverage)
    appendCoverageResults = true
}


On a related note, the Spring-Security-Core plugin generates source code instead of providing it via plugin, but does not create unit test code, so there will be little to no coverage out of the box.  Some may argue that the generated code is tested before being packaged into the plugin.  If you are one, then you could substitute the previous exclusions block with the one below:

    //('**/security/*') Excludes packages from the 'security' package, which
    // is where I put my generated Spring-Security-Core classes
    exclusions = ['*','**/security/*']

I would not recommend this however, because if you feel the need to have security on your application, you should test it.

Friday, August 30, 2013

Using MySQL LOAD DATA INFILE on Windows

Many times I need to load data into the MySQL database backing my Grails application before my initial release.  And frequently those databases include tables with last_updated and date_created timestamp fields.  Instead of entering the values into a csv file, I found it is very easy to set them programmatically while loading other data from a file like the following:

LOAD DATA INFILE 'D:\\sql_data\\available_samples.csv' 
INTO TABLE sample
FIELDS TERMINATED BY ',' 
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\r\n'
  (id, version, barcode, used)
 SET last_updated = NOW(), date_created = NOW();

References

Wednesday, August 28, 2013

My Grails Workflow: New Project Setup

Configuration changes

Build Config Changes
  • add organization's artifact repository to buildConfig.groovy
    mavenRepo https://my.url.org/nexus
    mavenRepo https://my.url.org/artifactory
    
    
  • Update all the plugins in buildConfig.groovy. Some of these plugins mature faster than the Grails releases and need to be updated when starting a new project.
    • Add (uncomment) cached resources - creates a hash of resources and sets expire headers so that your resources will not be downloaded over and over, helping to speed up your application
    • Add (uncomment) zipped resources - gzips the static resources in your application
    • Add build-test-data plugin - provides data that meets your domain constraints so you can concentrate on data under test
    • Add fixtures plugin - allows you to define dummy data for tests and development in the form of spring beans
    • Add spock plugin, if necessary - BDD Testing framework that makes tests much more expressive
        dependencies {
            test "org.spockframework:spock-grails-support:0.7-groovy-2.0" //Required for Grails v2.2+
        }
    
        plugins {
            runtime ":hibernate:$grailsVersion"
            runtime ":jquery:1.8.3"
            runtime ":resources:1.1.6"
            runtime ":zipped-resources:1.0"
            runtime ":cached-resources:1.0"
            runtime ":database-migration:1.3.2"
    
            build ":tomcat:$grailsVersion"
           
            compile ":cache-headers:1.1.5"
            compile ":cache:1.0.1"
            compile ":build-test-data:2.0.5"
    
            test (":spock:0.7") {
                    exclude "spock-grails-support"
            }
            test ':fixtures:1.2'
        }
Datasource Changes
  • Remove production block from datasource.groovy  (leaving the development and testing blocks).  I would remove all content, however, it appears that the database migration plugin has problems using alternative datasource configuration files when performing diffs/updates (http://jira.grails.org/browse/GPDATABASEMIGRATION-63)
  • Add <my app>-config.groovy to root directory for externalized configuration
  • Add /*-config.groovy to .gitignore to exclude it from the code repo.
Work Directory Change
This one is from Programming Grails by Burt Beckwith. Make target your work directory. This moves generated files, compiled classes, and installed plugins under the target directory and allows you to delete all of it by removing the target directory. This can help when Grails gets out of sync with your source code.
Change
  grails.project.class.dir = "target/classes"
  grails.project.test.class.dir = "target/test-classes"
  grails.project.test.reports.dir = "target/test-reports"
To
  grails.project.work.dir = 'target'


Setup/Preparation for Future Work

Front-End Development Preparation
  • Create and configure .editorconfig (This is not playing well with Intellij)
  • Disable caching, zipping, etc in development mode. This will allow you to make edits to the css and javascript while debugging/testing
      development {
        grails.resources.debug = true
      }
    
Database Migrations
Get the resources and artifacts prepared for the future. I like to setup the changelog prior to creating any domain classes.  This makes changelog.groovy  a simple list of includes.

dbm-generate-changelog changelog.groovy

Wednesday, June 26, 2013

Grails Unmarshal JSON into Domain Objects

Quick primer:

For more information regarding marshaling, consult your local wikipedia page on marshaling, but for our purposes, this is all you need to know.
  • Marshalling: Object transformed into simple format (JSON for this example)
  • Unmarshalling: simple format to Object

Prerequisties

In order to run this example, you need
  • Grails (I'm using 2.2.1)
  • Postman - for posting JSON to the Grails app

Example

1. Setup a new project, for this example I will use "jason".
  • grails create-app jason

2. Create a new domain object, for our example I will use
package example

class Book {
   String author
   String title
}
3. Scaffold the Book artifacts
grails generate-all example.Book
4. Edit the Conf/UrlMappings.groovy.  For now we will just hardcode the url mappings for book, so add the following to the top of the mappings block, such that your UrlMappings.groovy looks like:
static mappings = {
"/book/"(controller: "book", parseRequest:true){
action = [GET:'list', POST:'save']
}
"/book/$id?"(controller: "book", parseRequest:true){
action = [GET:'show'] // could also make this action = [GET:'show', PUT:'update', DELETE:'delete'], but you would need to modify those methods slightly
}

"/$controller/$action?/$id?"{
...
5. Make a few small edits to the BookController, adding the Grails JSON converter and editing save() , list(), and show() methods to return the persisted objects as JSON.
    import grails.converters.JSON;
   ...
    def save() {
        def bookInstance = new Book(params)
        if (!bookInstance.save(flush: true)) {
            render(view: "create", model: [bookInstance: bookInstance])
            return
        }
        render bookInstance as JSON
    }
    def list(Integer max) {
        params.max = Math.min(max ?: 10, 100)
        render Book.list(params) as JSON;
    }

    def show(Long id) {
        def bookInstance = Book.get(id)
        if (!bookInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), id])
            redirect(action: "list")
            return
        }

        render bookInstance as JSON;
    }

6. Start your app, grails run-app
7. Post to the application
  1. Open Postman, enter URL request here: http://localhost:8080/jason/book
  2. Select "Post", since we are adding a new Book object
  3. Select Header button
    1. Header: Content-Type
    2. Value: application/json or text/json
  4. Select "raw" from the parameter options (form-data, x-www-form..., raw)
  5. Enter the following in the textbox provided
    {"title": "Programming Grails", "author": "Burt Beckwith"}
  6. Click Send
  7. You should receive a response like:
    {
      "class": "example.Book",
      "id": 1,
      "author": "Burt Beckwith",
      "title": "Programming Grails"
    }
8. Repeat step 8 with different JSON parameters, such as
{"title": "The Definitive Guide to Grails", "author": "Graeme Rocher"}
9. "Get" a Book by id from the application.  Because we mapped the GET action to 'show' in the UrlMappings, we should get the Book by id.
  1. Open Postman, enter URL request here: http://localhost:8080/jason/book/1
    • Alternatively, you could make the url http://localhost:8080/jason/book and add a URL param id=1
  2. Select "Get", since we are retrieving an existing Book object
  3. Select Header button
    1. Header: Content-Type
    2. Value: application/json or text/json
  4. Click Send
  5. You should receive a response like:
    {
      "class": "example.Book",
      "id": 1,
      "author": "Burt Beckwith",
      "title": "Programming Grails"
    }
10. "Get" all Books from the application.  Because we mapped the GET action to "list" in the UrlMappings, we should get all the Books in the application.
  1. Open Postman, enter URL request here: http://localhost:8080/jason/book/
  2. Select "Get", since we are retrieving an existing Book object
  3. Select Header button
    1. Header: Content-Type
    2. Value: application/json or text/json
  4. Click Send
  5. You should receive a response like:
[
    {
        "class": "example.Book",
        "id": 1,
        "author": "Burt Beckwith",
        "title": "Programming Grails"
    },
    {
        "class": "example.Book",
        "id": 2,
        "author": "Graeme Rocher",
        "title": "The Definitive Guide to Grails"
    }
]

11. You can also check that this is working using the in the Grails dbconsole
  • http://localhost:8080/jason/dbconsole
  • By default, the JDBC URL is jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000, but if you changed it in conf/datasource.groovy, change this value accordingly





Friday, June 7, 2013

org.xml.sax.SAXParseException; ... The processing instruction target matching "[xX][mM][lL]" is not allowed.

In an attempt to test the XML output of a Grails service, I created the control string for my xml unit test as follows:
def testXML = '''
<?xml version='1.0' standalone='no'?>
  <!DOCTYPE labels SYSTEM "label.dtd">
  <labels _FORMAT='labelFormat' >
    <label>
      <variable name='study'>my_study</variable>
      <variable name='visit'>my_visit</variable>
    </label>
  </labels>
'''
This generated the following exception:
org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 9; The processing instruction target matching "[xX][mM][lL]" is not allowed.
  at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:251)
    com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:300)
    org.custommonkey.xmlunit.XMLUnit.buildDocument(XMLUnit.java:383)
    org.custommonkey.xmlunit.XMLUnit.buildDocument(XMLUnit.java:370)
    org.custommonkey.xmlunit.Diff.<init>(Diff.java:101)
    org.custommonkey.xmlunit.Diff.<init>(Diff.java:93)
After some searching I found a StackOverflow question that lead me to the problem. If you specify processing instruction (PI) for your XML in a groovy mulit-line string, you must start the xml content on the first line of the string, because "Whitespace is not allowed between the opening less-than character and the element tagname or between the prefix, colon, and local name of an element or attribute.". So the xml string in the beginning should be:
def testXML = '''<?xml version='1.0' standalone='no'?>
   <!DOCTYPE labels SYSTEM "label.dtd">
   <labels _FORMAT='labelFormat' >
    <label>
     <variable name='study'>my_study</variable>
     <variable name='visit'>my_visit</variable>
    </label>
   </labels>
  '''
or simply add an forward slash after the opening quotes '''/, like
def testXML = '''/
<?xml version='1.0' standalone='no'?>
   <!DOCTYPE labels SYSTEM "label.dtd">
   <labels _FORMAT='labelFormat' >
    <label>
     <variable name='study'>my_study</variable>
     <variable name='visit'>my_visit</variable>
    </label>
   </labels>
  '''