DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Languages
  4. GrUnit: Groovy way of unit testing

GrUnit: Groovy way of unit testing

Alex Tkachman user avatar by
Alex Tkachman
·
Mar. 31, 10 · Interview
Like (0)
Save
Tweet
Share
14.78K Views

Join the DZone community and get the full member experience.

Join For Free
There is continuation of this article now

We, Java/Groovy developers, write unit tests all the time. I believe there is no need to convince anybody why is that important. My goal today is to show small but convinient experimental tool included in to Groovy++ (with potential to be backported in to Groovy Core). You can find more information about GrUnit and Groovy++ at Groovy++ project homepage

So what is GrUnit?

GrUnit is Groovy AST (abstract syntax tree) transformation, which allows just two things

  • writing JUnit tests a little bit less verbose
  • making tests executable as Groovy scripts

Let us start with example

@Typed package org.mbte.groovypp.compiler

import org.codehaus.groovy.tools.FileSystemCompiler
import org.codehaus.groovy.control.CompilerConfiguration

def finder = new FileNameFinder ()

String [] names = finder.getFileNames("./StdLibTest/tests/", "**/*Test.groovy")
new FileSystemCompiler (new CompilerConfiguration()).compile (names)

Looks like regular Groovy script, right? And it is regular Groovy script. But at the same time it is JUnit test case with just one test. Here is output after execution of the script. 

.
Time: 3.558

OK (1 test)

Educated reader can notice that this is normal output of junit.textui.TestRunner. That's correct. Our class extends brilliant GroovyTestCase and main method of the class simply calls test runner. Of course, body of our script becomes body of the only one test in the test case.

The only thing we need to do is to place the script in a file with .grunit extension.

How do we achive that? Magic?

Of course there is no magic involved. The tecnique we use (extremely powerful and very popular in Groovy world) is compile time AST transformation. AST transformation is possibility to extend compiler behavior with plugins, which at some stages of compilation modify interanl presentation of the code.

Transformations we do in our case

  • our script class should extend GroovyTestCase
  • main method should call junit.textui.TestRunner
  • body of the script should be transformed in to public void testXXX () method

Now let us add one more test to our script. It can be done very easy by adding testXXX {...} call at any place on top level of our script

testTestsExists {
assertTrue new File("./StdLibTest/tests/").exists()
}

def finder = new FileNameFinder ()

String [] names = finder.getFileNames("./StdLibTest/tests/", "**/*.groovy")
new FileSystemCompiler (new CompilerConfiguration()).compile (names)

And voila! Output of our script now looks differently - we have two tests running

..
Time: 3.372

OK (2 tests)

You want more? Our pleasure

testTestsExists {
assertTrue new File("./StdLibTest/tests/").exists()
}

testSuperclassName {
assertEquals "groovy.util.GroovyTestCase", this.class.superclass.name
}

def finder = new FileNameFinder ()

String [] names = finder.getFileNames("./StdLibTest/tests/", "**/*.groovy")
new FileSystemCompiler (new CompilerConfiguration()).compile (names)

Which executes already 3 tests

...
Time: 3.642

OK (3 tests)

Maybe you want to use some other base class for your test? No problem. Just add extendsTest XXX on top level of the script

extendsTest GroovyShellTestCase

testTestsExists {
assertTrue new File("./StdLibTest/tests/").exists()
}

testInstance {
assertEquals "groovy.util.GroovyShellTestCase", this.class.superclass.name
}

def finder = new FileNameFinder ()

String [] names = finder.getFileNames("./StdLibTest/tests/", "**/*.groovy")
new FileSystemCompiler (new CompilerConfiguration()).compile (names)

And of course setUp()/tearDown() are also supported. Again we just need to add it somewhere on the top level of our script. The code below is one of most important tests in Groovy++ code base - we test that we able to compile our standard library

extendsTest GroovyFileSystemCompilerTestCase

@Field FileNameFinder finder

setUp { finder = new FileNameFinder () }

tearDown { finder = null }

testFinderNotNull { assertNotNull finder }

testCompilerNotNull { assertNotNull compiler }

def names = finder.getFileNames("./StdLib/src/", "**/*.groovy")
names.addAll(finder.getFileNames("./StdLib/src/", "**/*.java"))
compiler.compile (names as String[])

But the maybe most interesting part of story is that very often you don't need setUp/tearDown. Let us rewrite test above using so called accumulated tests

extendsTest GroovyFileSystemCompilerTestCase

testCompilerNotNull { assertNotNull compiler }

def srcDir = "./StdLib/src/"
testSrcExists {
assertTrue new File(srcDir).exists()
}

def finder = new FileNameFinder()
checkNamesNonEmpty {
def names = finder.getFileNames(srcDir, "**/*.groovy")
names.addAll(finder.getFileNames(srcDir, "**/*.java"))
assertFalse names.empty
}

compiler.compile (names as String[])

Accumulated tests is a way to intermix script code with testXXX{...} tests. All script code before testXXX{} statement will be included in to the body of newly generated test method. We can also use chackXXX{...} instead of testXXX{...}. It will also lead to creation of new test method but code of closure inside checkXXX will be accumulated for use by later tests.

The main point is to have very easy way to split tests in to more granular parts for ease of debugging. Let us see what previous code snippet replace. Probably our code looks a little bit nicer

class CompileStdLibTest extends GroovyFileSystemCompilerTestCase {
void testCompilerNotNull () {
assertNotNull compiler
}

void testSrcExist () {
def srcDir = "./StdLib/src/"
assertTrue new File(srcDir).exists()
}

void testNamesNonEmpty () {
def srcDir = "./StdLib/src/"
def finder = new FileNameFinder ()

def names = finder.getFileNames(srcDir, "**/*.groovy")
names.addAll(finder.getFileNames(srcDir, "**/*.java"))

assertFalse names.empty
}

void test$main {
def srcDir = "./StdLib/src/"
def finder = new FileNameFinder ()

def names = finder.getFileNames(srcDir, "**/*.groovy")
names.addAll(finder.getFileNames(srcDir, "**/*.java"))

assertFalse names.empty

compiler.compile(names as String[])
}

static void main (String [] args) {
junit.textui.TestRunner.run(CompileStdLibTest)
}
}

At the end I want to notice that there is nothing Groovy++ specific in GrUnit (except it is written in Groovy++ and included in to Groovy++ distro), so it can be easily ported in to Groovy Core if at some point we will decide it make sense.

I hope it was not boring. Thank you for reading and till next time.
unit test Groovy (programming language)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Best CI/CD Tools for DevOps: A Review of the Top 10
  • Fixing Bottlenecks in Your Microservices App Flows
  • Public Key and Private Key Pairs: Know the Technical Difference
  • Choosing the Right Framework for Your Project

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: