SunJUG » Spock

Spock

Last modified by John Thompson on 02/22/2012 07:22:59 PM

Spock is a testing and specification framework for Java and Groovy applications. What makes it stand out from the crowd is its beautiful and highly expressive specification language. Thanks to its JUnit runner, Spock is compatible with most IDEs, build tools, and continuous integration servers. Spock is inspired from JUnit, jMock, RSpec, Groovy, Scala, Vulcans, and other fascinating life forms.

Attached is the Spock Presentation from the January SunJug meeting. 

This is an example of a unit test for some C24 witten in JUnit

JUnit Example


package biz.c24.examples.csv;

import biz.c24.io.api.data.ComplexDataObject;

import biz.c24.io.api.data.Element;

import biz.c24.io.api.data.IOXPathFactory;

import biz.c24.io.api.data.ValidationManager;

import biz.c24.io.api.presentation.Source;

import org.apache.log4j.Appender;

import org.apache.log4j.ConsoleAppender;

import org.apache.log4j.Level;

import org.apache.log4j.SimpleLayout;

import org.junit.Assert;

import org.junit.Test;

import java.io.FileInputStream;

import java.io.IOException;

import java.util.Date;

import java.util.GregorianCalendar;

/**

 * Created by IntelliJ IDEA.

 * User: jdavies

 * Date: 08/11/2011

 * Time: 19:22

 */

public class TransactionsTest {

    @Test

    public void testValidation() {

        ComplexDataObject bean = getTestComplexDataObject();

        try {

            new ValidationManager().validateByException(bean);

        } catch (Exception e) {

            Assert.fail(e.getMessage());

        }

    }

    @Test

    public void testXPath() {

        ComplexDataObject bean = getTestComplexDataObject();

        try {

            Number number = IOXPathFactory.getInstance("sum(//Row1[Currency='GBP']/Amount)").getNumber(bean);

            Assert.assertTrue(number.intValue() == 150);

        } catch (Exception e) {

            Assert.fail(e.getMessage());

        }

    }

    @Test

    public void testDateValidationFail() {

        ComplexDataObject bean = getTestComplexDataObject();

        Transactions transactions = (Transactions) bean;

        Date firstOfApr = new GregorianCalendar(2011, 3, 1).getTime();

        transactions.getRow1()[5].setTransactionDate(firstOfApr);

        try {

            new ValidationManager().validateByException(bean);

            Assert.fail("This should fail validation");

        } catch (Exception e) {

            Assert.assertTrue(true);

        }

    }

    @Test

    public void testRowCountFail() {

        ComplexDataObject bean = getTestComplexDataObject();

        Transactions transactions = (Transactions) bean;

        transactions.getRow2().setTotal(123);

        try {

            new ValidationManager().validateByException(bean);

            Assert.fail("This should fail validation");

        } catch (Exception e) {

            Assert.assertTrue(true);

        }

    }

    @Test

    public void testCardMod10Fail() {

        ComplexDataObject bean = getTestComplexDataObject();

        Transactions transactions = (Transactions) bean;

        transactions.getRow1()[3].setCardNumber("1234-2345-3456-4567");

        try {

            new ValidationManager().validateByException(bean);

            Assert.fail("This should fail validation");

        } catch (Exception e) {

            Assert.assertTrue(true);

        }

    }

    @Test

    public void testCreateNewRows() {

        ComplexDataObject bean = getTestComplexDataObject();

        Transactions transactions = (Transactions) bean;

        for (int i = 0; i < 1000; i++) {

            Row1 row = new Row1();

            row.setNameElement("John Davies");

            row.setTransactionDate(new Date());

            row.setAmount(Math.random() * 10000);

            row.setCardNumber("1234-2345-3456-4563");

            row.setCommission(row.getAmount() * 0.01);

            row.setCountry("UK");

            row.setCurrency("GBP");

            row.setExpiryDate(new GregorianCalendar(2012, 12, 31).getTime());

            row.setVendorID(99999L);

            transactions.addRow1(row);

        }

        transactions.getRow2().setTotal(transactions.getRow2().getTotal() + 1000);

        try {

            new ValidationManager().validateByException(bean);

        } catch (Exception e) {

            Assert.fail(e.getMessage());

        }

    }

    public static boolean isValid(String cardNumber) {

        int sum = 0;

        int digit = 0;

        int addend = 0;

        boolean timesTwo = false;

        java.lang.StringBuilder stringBuffer = new java.lang.StringBuilder();

        char c;

        for (int i = 0; i < cardNumber.length(); i++) {

            c = cardNumber.charAtinformation;

            if (java.lang.Character.isDigit(c)) {

                stringBuffer.append(c);

            }

        }

        java.lang.String digitsOnly = stringBuffer.toString();

        for (int i = digitsOnly.length() - 1; i >= 0; i--) {

            digit = java.lang.Integer.parseInt(digitsOnly.substring(i, i + 1));

            if (timesTwo) {

                addend = digit * 2;

                if (addend > 9) {

                    addend -= 9;

                }

            } else {

                addend = digit;

            }

            sum += addend;

            timesTwo = !timesTwo;

        }

        int modulus = sum % 10;

        return modulus == 0;

    }

    private ComplexDataObject getTestComplexDataObject() {

        Element element = TransactionsElement.getInstance();

        Appender appender = new ConsoleAppender(new SimpleLayout());

        element.getLog().addAppender(appender);

        element.getLog().setLevel(Level.OFF);

        ComplexDataObject bean = null;

        try {

            // Parse

            Source source = element.getModel().source();

            source.setInputStream(new FileInputStream("resources/Transactions-good.txt"));

            bean = source.readObject(element);

            source.getInputStream().close();

            element.getLog().removeAppender(appender);

        } catch (IOException e) {

            e.printStackTrace();

        }

        return bean;

    }

}

Same Tests - but re-written in Spock

package biz.c24.examples.csv

import spock.lang.*

import biz.c24.io.api.data.ComplexDataObject;

import biz.c24.io.api.data.Element;

import biz.c24.io.api.data.IOXPathFactory;

import biz.c24.io.api.data.ValidationManager;

import biz.c24.io.api.presentation.Source;

import org.apache.log4j.ConsoleAppender;

import org.apache.log4j.Level;

import org.apache.log4j.SimpleLayout

import biz.c24.io.api.data.ValidationException;

/**

 * Created by IntelliJ IDEA.

 * User: john

 * Date: 11/9/11

 * Time: 11:04 AM

 * To change this template use File | Settings | File Templates.

 */

class TransactionsSpec extends Specification {

    def "Test Validation"() {

        when:

        def validationManager = new ValidationManager()

        then:

        validationManager.validateByException(getTestComplexDataObject())

    }

    def "Test XPath"() {

        when:

        Number number = IOXPathFactory.getInstance("sum(//Row1[Currency='GBP']/Amount)").getNumber(getTestComplexDataObject())

        then:

        number == 150

    }

    def "Test Date Validation Fail"() {

        given:

        def bean = getTestComplexDataObject()

        when:

        bean.getRow1()[5].transactionDate = new Date().parse('yyyy/mm/dd', '2011/4/1')  // look ma, duck typing, no cast to keep the compiler happy

        new ValidationManager().validateByException(bean);

        then:

        thrown(Exception)

    }

    def "Test Row Count Fail"() {

        given:

        def bean = getTestComplexDataObject().getRow2().setTotal(123)

        when:

        new ValidationManager().validateByException(bean)

        then:

        thrown(Exception)

    }

    def "Test Card Mod 10 Check Fail"() {

        given:

        def bean = getTestComplexDataObject().getRow1()[3].cardNumber = "1234-2345-3456-4567"

        when:

        new ValidationManager().validateByException(bean)

        then:

        thrown(Exception)

    }

    def "Create new Rows"() {

        given:

        def bean = getTestComplexDataObject()

        (1..1000).each {

            def amount = Math.random() * 10000

            def commission = amount * 0.01

            //the map constructor can be a slow way of object creation, but I wanted to see if it would play nice with the c24 code

            def row = new Row1(nameElement: "John Thompson", transactionDate: new Date(), amount: amount,

                    cardNumber: "1234-2345-3456-4563", commission: commission, country: "US", currency: "USD",

                    expiryDate: new Date().parse('yyyy/mm/dd', '2012/12/31'), vendorID: 9999L)

            bean.addRow1(row)

        }

        bean.row2.total = bean.row2.total + 1000

        def validator = new ValidationManager()

        when:

        def validate = validator.validateByException(bean)

        then:

        true // test will fail if validator pops exception

    }

    def "Create new Rows with validation exception"() {

        given:

        def bean = getTestComplexDataObject()

        (1..100).each {

            def amount = Math.random() * 10000

            def commission = amount * 0.01

            //the map constructor can be a slow way of object creation, but I wanted to see if it would play nice with the c24 code

            def row = new Row1(nameElement: "John Thompson", transactionDate: new Date(), amount: amount,

                    cardNumber: "1234-2345-3456-4563", commission: commission, country: "US", currency: "USD",

                    expiryDate: new Date().parse('yyyy/mm/dd', '2012/12/31'), vendorID: 9999L)

            bean.addRow1(row)

        }

        bean.row2.total = bean.row2.total + 1000

        def validator = new ValidationManager()

        when:

        validator.validateByException(bean)

        then:

        thrown(ValidationException)

    }

    def getTestComplexDataObject() {

        Element element = TransactionsElement.getInstance()

        def appender = new ConsoleAppender(new SimpleLayout())

        element.log.addAppender(appender)

        element.log.level = Level.OFF

        Source source = element.model.source()

        source.inputStream = new FileInputStream("resources/Transactions-good.txt")

        ComplexDataObject bean = source.readObject(element)

        source.inputStream.close()

        element.log.removeAllAppenders()

        bean

    }

}

Example Spock Spec for a Grails Controller

In this example, Groovy Mock objects and Spock Mock objects are used.


package biz.c24.examples.csv

import spock.lang.*

import biz.c24.io.api.data.ComplexDataObject;

import biz.c24.io.api.data.Element;

import biz.c24.io.api.data.IOXPathFactory;

import biz.c24.io.api.data.ValidationManager;

import biz.c24.io.api.presentation.Source;

import org.apache.log4j.ConsoleAppender;

import org.apache.log4j.Level;

import org.apache.log4j.SimpleLayout

import biz.c24.io.api.data.ValidationException;

/**

 * Created by IntelliJ IDEA.

 * User: john

 * Date: 11/9/11

 * Time: 11:04 AM

 * To change this template use File | Settings | File Templates.

 */

class TransactionsSpec extends Specification {

    def "Test Validation"() {

        when:

        def validationManager = new ValidationManager()

        then:

        validationManager.validateByException(getTestComplexDataObject())

    }

    def "Test XPath"() {

        when:

        Number number = IOXPathFactory.getInstance("sum(//Row1[Currency='GBP']/Amount)").getNumber(getTestComplexDataObject())

        then:

        number == 150

    }

    def "Test Date Validation Fail"() {

        given:

        def bean = getTestComplexDataObject()

        when:

        bean.getRow1()[5].transactionDate = new Date().parse('yyyy/mm/dd', '2011/4/1')  // look ma, duck typing, no cast to keep the compiler happy

        new ValidationManager().validateByException(bean);

        then:

        thrown(Exception)

    }

    def "Test Row Count Fail"() {

        given:

        def bean = getTestComplexDataObject().getRow2().setTotal(123)

        when:

        new ValidationManager().validateByException(bean)

        then:

        thrown(Exception)

    }

    def "Test Card Mod 10 Check Fail"() {

        given:

        def bean = getTestComplexDataObject().getRow1()[3].cardNumber = "1234-2345-3456-4567"

        when:

        new ValidationManager().validateByException(bean)

        then:

        thrown(Exception)

    }

    def "Create new Rows"() {

        given:

        def bean = getTestComplexDataObject()

        (1..1000).each {

            def amount = Math.random() * 10000

            def commission = amount * 0.01

            //the map constructor can be a slow way of object creation, but I wanted to see if it would play nice with the c24 code

            def row = new Row1(nameElement: "John Thompson", transactionDate: new Date(), amount: amount,

                    cardNumber: "1234-2345-3456-4563", commission: commission, country: "US", currency: "USD",

                    expiryDate: new Date().parse('yyyy/mm/dd', '2012/12/31'), vendorID: 9999L)

            bean.addRow1(row)

        }

        bean.row2.total = bean.row2.total + 1000

        def validator = new ValidationManager()

        when:

        def validate = validator.validateByException(bean)

        then:

        true // test will fail if validator pops exception

    }

    def "Create new Rows with validation exception"() {

        given:

        def bean = getTestComplexDataObject()

        (1..100).each {

            def amount = Math.random() * 10000

            def commission = amount * 0.01

            //the map constructor can be a slow way of object creation, but I wanted to see if it would play nice with the c24 code

            def row = new Row1(nameElement: "John Thompson", transactionDate: new Date(), amount: amount,

                    cardNumber: "1234-2345-3456-4563", commission: commission, country: "US", currency: "USD",

                    expiryDate: new Date().parse('yyyy/mm/dd', '2012/12/31'), vendorID: 9999L)

            bean.addRow1(row)

        }

        bean.row2.total = bean.row2.total + 1000

        def validator = new ValidationManager()

        when:

        validator.validateByException(bean)

        then:

        thrown(ValidationException)

    }

    def getTestComplexDataObject() {

        Element element = TransactionsElement.getInstance()

        def appender = new ConsoleAppender(new SimpleLayout())

        element.log.addAppender(appender)

        element.log.level = Level.OFF

        Source source = element.model.source()

        source.inputStream = new FileInputStream("resources/Transactions-good.txt")

        ComplexDataObject bean = source.readObject(element)

        source.inputStream.close()

        element.log.removeAllAppenders()

        bean

    }

}

Tags:
Created by Steven Goldsmith on 01/07/2012 04:58:54 AM

This wiki is licensed under a Creative Commons 2.0 license
XWiki Enterprise 3.1 - Documentation