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
Sign Up
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><a href="http://popper.tigris.org/">Popper</a> extends <a href="http://sourceforge.net/projects/junit">JUnit</a> to allow you to specify <em>theories</em>. Theories are assertions about your code's behavior that may be true over (potentially) infinite sets of input values. You might find it useful to pose theories about your Groovy code too.</p> <h3>Example</h3> <p>Let's consider how we might test the following class (example taken from the Popper web site):</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 Dollar { int amount Dollar(int amount) { this.amount = amount } Dollar times(int value) { amount *= value; return this } Dollar divideBy(int value) { amount /= value; return this } } </pre></td></tr></table> <p>With traditional JUnit code, we might test it 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 org.junit.Test import org.junit.runner.JUnitCore class StandardTest { @Test void multiplyThenDivide() { assert new Dollar(10).times(6).divideBy(6).amount == 10 } } JUnitCore.main('StandardTest') </pre></td></tr></table> <p>This tests the method for one <code>amount</code> value and one <code>m</code> value. Next steps might be to triangulate so that additional values are also tested. In general though, it might be difficult to know when you have done enough values (when to stop) and also what invariants of your class may hold if you simply keep adding more tests without sufficient refactoring. With these factors in mind, Popper provides facilities to make invariants and preconditions of your classes obvious as well as providing an extensible framework for adding new test values.</p> <p>Here is how you might use Popper to test the above class. First, we have avoided using <a href="http://code.google.com/p/hamcrest/">Hamcrest</a> style assertions in our Groovy code. Groovy's built-in <code>assert</code> method usually allows such assertions to be expressed very elegantly without any additional framework. We'll create a small helper class to allow Groovy-style assertions to be used for method pre-conditions:</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 static net.saff.theories.assertion.api.Requirements.* import net.saff.theories.assertion.api.InvalidTheoryParameterException import net.saff.theories.runner.api.TheoryContainer class GroovyTheoryContainer extends TheoryContainer { def assume(condition) { try { assert condition } catch (AssertionError ae) { throw new InvalidTheoryParameterException(condition, is(condition)) } } def assumeMayFailForIllegalArguments(Closure c) { try { c.call() } catch (IllegalArgumentException e) { throw new InvalidTheoryParameterException(e, isNull()) } } } </pre></td></tr></table> <p>Now, our test becomes:</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 org.junit.* import org.junit.runner.* import net.saff.theories.methods.api.Theory import net.saff.theories.runner.api.* @RunWith(Theories) class PopperTest extends GroovyTheoryContainer { private log = [] // for explanatory purposes only public static int VAL1 = 0 public static int VAL2 = 1 public static int VAL3 = 2 public static int VAL4 = 5 @Theory void multiplyIsInverseOfDivide(int amount, int m) { assume m != 0 assert new Dollar(amount).times(m).divideBy(m).amount == amount log << [amount, m] } @After void dumpLog() { println log } } JUnitCore.main('PopperTest') </pre></td></tr></table> <p>We have added an additional log variable to this example to explain how Popper works. By default, Popper will use any public fields in our test as test data values <code>VAL1</code> through <code>VAL4</code> in our example. It will determine all combinations of the available variables and call the <code>multiplyIsInverseOfDivide()</code> for each combination. This is a very crude way to select test instance values but works for simple tests like this one.</p> <p>You should also note the <code>assume</code> statement. In our example, we haven't catered for <code>m</code> being 0 which would result in a divide by zero error. The assume statement allows this method precondition to be made explicit. When Popper calls the test method, it will silently ignore any test data combinations which fail the method preconditions. This keeps the preconditions obvious and simplifies creating test data sets.</p> <p>Here is the output from running this 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> JUnit version 4.3.1 .[[0, 1], [0, 2], [0, 5], [1, 1], [1, 2], [1, 5], [2, 1], [2, 2], [2, 5], [5, 1], [5, 2], [5, 5]] Time: 0.297 OK (1 test) </pre></td></tr></table> <p>We wouldn't normally recommend sending this kind of information to standard out when running your test, but here it is very illustrative. Note that all four test values have been used for the <code>amount</code> variable but only three values have been used for <code>m</code>. This is exactly what we want here.</p> <p>Popper supports an extensible framework for specifying more elaborate algorithms for selecting test data. Instead of the public variables, we can define our own parameter supplier. Here is one which supplies data between a <code>first</code> value and a <code>last</code> value. First the annotation definition (coded in Java):</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 import net.saff.theories.methods.api.ParametersSuppliedBy; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @ParametersSuppliedBy(BetweenSupplier.class) public @interface Between { int first(); int last(); } </pre></td></tr></table> <p>And the backing supplier (coded in Groovy):</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 net.saff.theories.methods.api.* import java.util.* public class BetweenSupplier extends ParameterSupplier { public List getValues(test, ParameterSignature sig) { def annotation = sig.supplierAnnotation annotation.first()..annotation.last() } } </pre></td></tr></table> <p>Now our Groovy test example could become:</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 org.junit.* import org.junit.runner.* import net.saff.theories.methods.api.Theory import net.saff.theories.runner.api.* @RunWith(Theories) class PopperBetweenTest extends GroovyTheoryContainer { private int test, total // for explanatory purposes only @Theory void multiplyIsInverseOfDivide( @Between(first = -4, last = 2) int amount, @Between(first = -2, last = 5) int m ) { total++ assume m != 0 assert new Dollar(amount).times(m).divideBy(m).amount == amount test++ } @After void dumpLog() { println "$test tests performed out of $total combinations" } } JUnitCore.main('PopperBetweenTest') </pre></td></tr></table> <p>When run, this yields:</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-default-parameter="none" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6bm9uZX0&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> JUnit version 4.3.1 .49 tests performed out of 56 combinations Time: 0.234 OK (1 test) </pre></td></tr></table> <p>The supplied test values for the test method are <code>(-4, -2), (-4, -1), (-4, 0), ..., (2, 5)</code>. The data where m is equal to 0 will be skipped as soon as the <code>assume</code> statement is reached.</p> <h3>Bowling Example</h3> <p>We can also Groovy to make the <a href="http://popper.tigris.org/BowlingTests.java">bowling example</a> a little more succinct:</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 net.saff.theories.methods.api.* import net.saff.theories.runner.api.* import org.junit.runner.* @RunWith(Theories.class) class BowlingTests extends GroovyTheoryContainer { public static Game STARTING_GAME = new Game() public static Game NULL_GAME = null public static Bowl THREE = new Bowl(3) public static Bowl FOUR = new Bowl(4) public static Bowl NULL_BOWL = null @DataPoint public Bowl oneHundredBowl() { new Bowl(100) } public static int ONE_HUNDRED = 100 public static int ZERO = 0 @Theory public void shouldBeTenFramesWithTwoRollsInEach(Game game, Bowl first, Bowl second) { assume game && first && second assume game.isAtBeginning() assume !first.isStrike() assume !second.completesSpareAfter(first) 10.times { game.bowl(first) game.bowl(second) } assert game.isGameOver() } @Theory public void maximumPinCountIsTen(Bowl bowl) { assume bowl assert bowl.pinCount() <= 10 } @Theory public void pinCountMatchesConstructorParameter(int pinCount) { assumeMayFailForIllegalArguments { assert new Bowl(pinCount).pinCount() == pinCount } } } JUnitCore.main('BowlingTests') </pre></td></tr></table>
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