Skip to content
Skip to breadcrumbs
Skip to header menu
Skip to action menu
Skip to quick search
Quick Search
Browse
Pages
Blog
Labels
Attachments
Mail
Advanced
What’s New
Space Directory
Feed Builder
Keyboard Shortcuts
Confluence Gadgets
Log In
Dashboard
Groovy
Copy Page
You are not logged in. Any changes you make will be marked as
anonymous
. You may want to
Log In
if you already have an account. You can also
Sign Up
for a new account.
This page is being edited by
.
Paragraph
Paragraph
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Preformatted
Quote
Bold
Italic
Underline
More colours
Strikethrough
Subscript
Superscript
Monospace
Clear Formatting
Bullet list
Numbered list
Outdent
Indent
Align left
Align center
Align right
Link
Table
Insert
Insert Content
Image
Link
Attachment
Symbol
Emoticon
Wiki Markup
Horizontal rule
tinymce.confluence.insert_menu.macro_desc
Info
JIRA Issue
Status
Gallery
Tasklist
Table of Contents
Other Macros
Page Layout
No Layout
Two column (simple)
Two column (simple, left sidebar)
Two column (simple, right sidebar)
Three column (simple)
Two column
Two column (left sidebar)
Two column (right sidebar)
Three column
Three column (left and right sidebars)
Undo
Redo
Find/Replace
Keyboard Shortcuts Help
<p><table class="wysiwyg-macro" data-macro-name="excerpt" data-macro-parameters="atlassian-macro-output-type=BLOCK|hidden=true" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2V4Y2VycHQ6aGlkZGVuPXRydWV8YXRsYXNzaWFuLW1hY3JvLW91dHB1dC10eXBlPUJMT0NLfQ&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"><p>explores testing features of Groovy and some other testing frameworks</p></td></tr></table></p> <table class="wysiwyg-macro" data-macro-name="section" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e3NlY3Rpb259&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"> <table class="wysiwyg-macro" data-macro-name="column" data-macro-parameters="width=65%" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvbHVtbjp3aWR0aD02NSV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"> <p>Groovy is great for Agile development in general and testing in particular because:</p> <ul> <li>it has built-in support for the JUnit testing framework</li> <li>it has built-in mocking capabilities</li> <li>it provides a very expressive language in which to write tests which can utilise Closure syntax and Groovy's other features which support the creation of testing domain specific languages (DSLs)</li> <li>it can easily leverage Java testing frameworks and IDE support</li> <li>it's built-in AntBuilder support makes it easy to set up integration tests</li> </ul> <p>This page explores testing features of Groovy and some other testing frameworks that you may sometimes wish to use to complement Groovy's built-in capabilities.</p> <p>Two main examples are used:</p> <ul> <li>A Stack Example</li> <li>An Item Storer Example</li> </ul> </td></tr></table> <table class="wysiwyg-macro" data-macro-name="column" data-macro-parameters="width=35%" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvbHVtbjp3aWR0aD0zNSV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"> <table class="wysiwyg-macro" data-macro-name="note" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e25vdGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"> <p>Some of the examples on this page rely on annotations: a feature in Groovy 1.5+ when used with Java 5 and above.</p></td></tr></table></td></tr></table></td></tr></table> <h2>A Stack Example</h2> <p>Inspired by the <a href="http://rspec.rubyforge.org/">RSpec</a> stack example, suppose we want to test the following Groovy classes:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> class StackOverflowException extends RuntimeException {} class StackUnderflowException extends RuntimeException {} class FixedStack { public static MAXSIZE = 10 private items = [] def push(object) { if (items.size() == MAXSIZE) throw new StackOverflowException() items << object } def pop() { if (!items) throw new StackUnderflowException() items.pop() } def peek() { if (!items) throw new StackUnderflowException() items[-1] } boolean isEmpty() { items.isEmpty() } boolean isFull() { items.size() == MAXSIZE } } </pre></td></tr></table> <p>We can test this with vanilla Groovy using the following tests:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> class NonEmptyFixedStackTest extends GroovyTestCase { private stack void setUp() { stack = new FixedStack() [ "a", "b", "c" ].each{ x -> stack.push x } } void testPreConditions() { assert !stack.isEmpty() } void testShouldAddToTheTopWhenSentPush() { stack.push "d" assert stack.peek() == "d" } void testShouldBeUnchangedWhenSentPushThenPop() { stack.push "anything" stack.pop() assert stack.peek() == "c" } void testShouldReturnTheTopItemWhenSentPeek() { assert stack.peek() == "c" } void testShouldNotRemoveTheTopItemWhenSentPeek() { assert stack.peek() == "c" assert stack.peek() == "c" } void testShouldReturnTheTopItemWhenSentPop() { assert stack.pop() == "c" } void testShouldRemoveTheTopItemWhenSentPop() { assert stack.pop() == "c" assert stack.pop() == "b" } } class EmptyFixedStackTest extends GroovyTestCase { private stack = new FixedStack() void testPreConditions() { assert stack.isEmpty() } void testShouldNoLongerBeEmptyAfterPush() { stack.push "anything" assert !stack.isEmpty() } void testShouldComplainWhenSentPeek() { shouldFail(StackUnderflowException) { stack.peek() } } void testShouldComplainWhenSentPop() { shouldFail(StackUnderflowException) { stack.pop() } } } class FullFixedStackTest extends GroovyTestCase { private stack void setUp() { stack = new FixedStack() (1..FixedStack.MAXSIZE).each{ x -> stack.push x } } void testPreConditions() { assert stack.isFull() } void testShouldRemainFullAfterPeek() { stack.peek() assert stack.isFull() } void testShouldNoLongerBeFullAfterPop() { stack.pop() assert !stack.isFull() } void testShouldComplainOnPush() { shouldFail(StackOverflowException) { stack.push "anything" } } } class AlmostFullFixedStackTest extends GroovyTestCase { private stack void setUp() { stack = new FixedStack() (1..<FixedStack.MAXSIZE).each{ x -> stack.push x } } void testPreConditions() { assert !stack.isFull() } void testShouldBecomeFullAfterPush() { stack.push "anything" assert stack.isFull() } } class AlmostEmptyFixedStackTest extends GroovyTestCase { private stack void setUp() { stack = new FixedStack() stack.push "anything" } void testPreConditions() { assert !stack.isEmpty() } void testShouldRemainNotEmptyAfterPeek() { stack.peek() testPreConditions() } void testShouldBecomeEmptyAfterPop() { stack.pop() assert stack.isEmpty() } } </pre></td></tr></table> <p>Of course, even within vanilla Groovy we have a few options. E.g., we could use no test class, just a script, in which case, the naming conventions required by JUnit 3.x (used above) wouldn't apply. Or, we could have used JUnit 4 and annotations to allow us to use alternate naming conventions for the methods. Alternatively, we could use a single test class with different test fixtures, e.g. <code>fullStack</code> and <code>emptyStack</code>. The other frameworks <a class="confluence-link" href="#UsingOtherFrameworks" data-anchor="UsingOtherFrameworks" data-linked-resource-default-alias="UsingOtherFrameworks" data-base-url="http://docs.codehaus.org">mentioned below</a> allow additional possibilities for organising and naming our tests.</p> <h2>An Item Storer Example</h2> <p>Suppose we have the following Java interface (Groovy supports interface-oriented programming as well as dynamic programming using duck-typing and here we want to illustrate Java test/mock libraries later):</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> // Java public interface Reverser { Object reverse(Object item); } </pre></td></tr></table> <p>Now suppose we have an implementation method as follows:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> class GroovyReverser implements Reverser { def reverse(item) { if (item instanceof Number) return -item return item.reverse() } } </pre></td></tr></table> <p>For numbers, the <code>reverse()</code> method will negate them. Thanks to duck-typing, other objects that we try to reverse will just call their respective types <code>reverse()</code> method if it exists, e.g. so it will work for <code>String</code> and <code>List</code> objects.</p> <p>Now suppose we make use of a reverser in some code we are trying to test.</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> class Storer { def stored Reverser reverser = new GroovyReverser() def put(item) { stored = item } def get() { return stored } def getReverse() { return reverser.reverse(stored) } } </pre></td></tr></table> <h3>Integration Testing</h3> <p>We can integration test this class as follows:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> def checkPersistAndReverse(cut, value, reverseValue) { cut.put(value) assert value == cut.get() assert reverseValue == cut.getReverse() } def testBehavior(cut) { checkPersistAndReverse cut, 123.456, -123.456 checkPersistAndReverse cut, 'hello', 'olleh' checkPersistAndReverse cut, [1, 3, 5], [5, 3, 1] } testBehavior(new Storer()) </pre></td></tr></table> <h3>Mocking Groovy Classes</h3> <p>The above integration tests exercise our class under test with the production <code>reverser</code> in place. For this particular example, we might argue that such a test is appropriate and sufficient. However, in more complicated scenarios, the dependent collaboration class (<code>GroovyReverser</code> in this case) might be difficult or expensive to construct. In those cases, we would want to replace it with a mock or stub. See <a class="confluence-link" href="/display/GROOVY/Groovy+Mocks" data-linked-resource-id="59174" data-linked-resource-type="page" data-linked-resource-default-alias="Groovy Mocks" data-base-url="http://docs.codehaus.org">Groovy Mocks</a> for a definition of terms.</p> <p>Here is how we would use Groovy's built-in mock support to test our class under test in isolation:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> import groovy.mock.interceptor.MockFor def mockReverser = new MockFor(GroovyReverser.class) mockReverser.demand.reverse{ arg -> -arg } mockReverser.demand.reverse{ arg -> arg.reverse() } mockReverser.use { testBehavior(new Storer()) } </pre></td></tr></table> <p>Note that we didn't need to do anything to inject our mocks into the class under test. Inside the <code>use</code> method, all attempts to create a <code>GroovyReverser</code> object will be replaced by a mock object. This also works for Java objects created by Groovy objects, as the following shows:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> mockReverser = new MockFor(JavaReverser.class) mockReverser.demand.reverse{ arg -> -arg } mockReverser.demand.reverse{ arg -> arg.reverse() } mockReverser.use { testBehavior(new Storer(reverser:new JavaReverser())) } </pre></td></tr></table> <p>Here <code>JavaReverser</code> is a class that we have defined as follows:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> import java.util.List; import java.util.ArrayList; import java.util.ListIterator; public class JavaReverser implements Reverser { public Object reverse(Object item) { if (item instanceof Number) { Number n = (Number) item; return new Double(-n.doubleValue()); } if (item instanceof String) { String s = (String) item; int size = s.length(); StringBuffer sb = new StringBuffer(size); for (int i = size - 1; i >= 0; i--) { sb.append(s.charAt(i)); } return sb.toString(); } if (item instanceof List) { List l = (List) item; int size = l.size(); List result = new ArrayList(size); ListIterator iter = l.listIterator(size); while (iter.hasPrevious()) { result.add(iter.previous()); } return result; } throw new UnsupportedOperationException(); } } </pre></td></tr></table> <p>Hmmm. Quite a bit longer than the Groovy version. But that's another story.</p> <h3>Testing Java classes which use other Java classes</h3> <p>Now, consider now the following Java class:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> public class JavaStorer { Object stored; Reverser reverser; public JavaStorer(Reverser reverser) { this.reverser = reverser; } void put(Object item) { stored = item; } Object get() { return stored; } Object getReverse() { return reverser.reverse(stored); } } </pre></td></tr></table> <p>We now want to test this too and we want to use Groovy to write the tests. Unfortunately, Groovy's built-in mock support won't help us here. It doesn't allow us to test Java classes like this one as it relies on hooking into Groovy's object lifecycle mechanisms. Instead, we can use any of the available Java mocking packages such as the ones <a class="confluence-link" href="/display/GROOVY/Using+Other+Testing+Frameworks" data-linked-resource-id="12681270" data-linked-resource-type="page" data-linked-resource-default-alias="Using Other Testing Frameworks" data-base-url="http://docs.codehaus.org">mentioned here</a>.</p> <h3>GSpec</h3> <p>GSpec is a testing framework written in Groovy, which this example is based on. See <a class="confluence-link" href="/display/GROOVY/Using+GSpec+with+Groovy" data-linked-resource-id="76918" data-linked-resource-type="page" data-linked-resource-default-alias="Using Gspec with Groovy" data-base-url="http://docs.codehaus.org">this page</a> for a full explanation of using GSpec for BDD style testing.</p>
Please type the word appearing in the picture.
Attachments
Labels
Location
Watch this page
< Edit
Preview >
Loading…
Save
Cancel
Next hint
search
attachments
weblink
advanced