Groovy Testing

Embed Size (px)

Citation preview

Blu scuro

Groovy testing

Davide Rossi

Jug Milano, 19/02/2009

Contents

- Groovy built-in testing framework

- Unit test in Groovy: GroovyTestCase

- Testing for exceptions

- Groovy Mock and Stub

- The Java way: easymock, jMock...

- The Groovy way: using Categories

- The Groovy way: using ExpandoMetaClass

- The Groovy way: using Expandos and Maps

- The Groovy way: using stubFor and mockFor

- Testing Grails Actions

- Conclusion

Groovy testing framework

- Groovy can be used to test Groovy and Java code

- Groovy has built-in support for Junit framework

- GroovyTestCase extends Junit TestCase

- Adds many assertion methods (assertToString, AssertArrayEquals, shouldFail...)

- Assertions are parts of the language

ex:

def number = 2

assert (number + 2) == 4

GroovyTestCase

class SimpleUnitTest extends GroovyTestCase {

void testSimple() {

def integerList = [1, 2, 3]

assertEquals 2, integerList[1]

assertEquals 6, integerList[0] + integerList[1] + integerList[2]

}

}

GroovyTestCase: testing for exceptions

class ClassToTest {

def methodWhichThrowsException() {

// Doing some work...

throw new NumberFormatException("Warning!")

}

}

void testSimple() {

def objectToTest = new ClassToTest()

shouldFail(NumberFormatException) {

objectToTest.methodWhichThrowsException()

}

}

Mocks and Stubs

- Mocks and stubs are used to substitute real collaborators of the tested class

- With mocks and stubs you can unit test your code in isolation without depend form external code

- stubs have weak expectations: verify state of mocked objects

- mocks have strong expectations: verify state and behaviour of mocked objects

The Java way

- Easymock, jMock, jMockit, Mockito...

public void testGetPrice() {

CurChange mockedDependency = createMock(CurChange.class);

expect(mockedDependency.getPriceByEUR(EUR)).andReturn(new BigDecimal(100));

replay(mockedDependency);

BigDecimal price = systemUnderTest.getPrice(EUR);

assertNotNull(price);

verify(mockedDependency);

}

The Groovy way

Groovy dynamic nature allows easy mocks implementation

- Mock using mockFor and stubFor

- Mock using Categories

- Mock using ExpandoMetaClass

- Mock using Expandos and Maps

Mock using mockFor and stubFor

- Inspired by easyMock, with same power and semantic

- Similar to Java

- Can be used everywhere a mock is needed

- You define expectations and verify them

- Mock Groovy and Java Collaborators

- Behavior specified via Closures, allowing static or calculated return values, throwing exceptions, asserting argument values...

- Not dependent on any external mock library

StubFor and mockFor

def save = {

try {

def file = new File(baseDir, params.id)

file.text = params.text

render "success"

} catch (Exception e) {

render "exception ${e.message}"

}

}

stubFor

void testSave() {

def testObj = new ClassToTest()

def fileMock = new StubFor(java.io.File)

def resultText

fileMock.demand.setText() { resultText = it }

fileMock.demand.close {}

fileMock.use {

testObj.save()

}

assertEquals "Qui va il contenuto del file" , resultText

}

mockFor

def save = {

def params = ['baseDir': '.', 'id': 'NuovoFile', 'text': 'Qui va il contenuto del file']

try {

def file = new File(params.baseDir, params.id)

file.text = params.text

file.text = "Qui aggiungo testo"

file.close()

} catch (Exception e) {

log "exception ${e.message}"

}

}

mockFor

void testSave() {

def testObj = new ClassToTest()

def fileMock = new MockFor(java.io.File)

def resultText1, resultText2

fileMock.demand.setText() { resultText1 = it }

fileMock.demand.setText() { resultText2 = it }

fileMock.demand.close(1..1) {}

fileMock.use {

testObj.saveForMock()

}

assertEquals "Qui va il contenuto del file" , resultText1

assertEquals "Qui aggiungo testo" , resultText2

}

Mocking using Categories

What are Categories ? A MOP method injection technique

class StringStrangeFormatUtil {

def static specialFormat(String self) {

if (self.size() < 10) {

return "***${self}***"

}

}

}

use(StringStrangeFormatUtil) {

println "ciao".specialFormat()

}

Mocking using Categories

def methodForMockUsingCategoriesOrMetaclass() {

int value = collaboratorMethod() * 3

return value

}

def collaboratorMethod() {

// Attendere prego...

// Please Wait...

return 10

}

Mocking using Categories

class TestMockUsingCategories extends GroovyTestCase {

void testMethodForMockUsingCategories() {

def testObj = new ClassToTest()

use(MockHelper) {

def result = testObj.methodForMockUsingCategoriesOrMetaclass()

assertEquals 399, result

}

}

}

class MockHelper {

def static collaboratorMethod(ClassToTest self) { 133 }

}

Mocking using ExpandoMetaClass

void testMethodForMockUsingCategoriesOrMetaclass() {

def emc = new ExpandoMetaClass(ClassToTest)

emc.collaboratorMethod = { -> 133 }

emc.initialize()

def testObj = new ClassToTest()

testObj.metaClass = emc

def result = testObj.methodForMockUsingCategoriesOrMetaclass()

assertEquals 399, result

}

Mocking using Expandos

def saveFile(file) {

file.write "Testo del file."

}

void testSaveFile() {

def fileMock = new Expando(text: ", write: {text = "Testo dinamico dell'Expando" })

def testObj = new ClassToTest()

testObj.saveFile(fileMock)

assertEquals "Testo dinamico dell'Expando" , fileMock.text

}

Mocking using Maps

void testSaveFile() {

def text = ''

def fileMock = [write : { text = "Testo dinamico della mappa" }]

def testObj = new ClassToTest()

testObj.saveFile(fileMock)

assertEquals "Testo dinamico della mappa" , text

}

Testing Grails Actions

- Integration test: Grails uses a HSQL DB and injects environment objects (session, controllers...)

def team1, team2, team3, team4

void setUp() {

team1 = new Team(name: 'Varese', championship: 'Serie C2', city: 'Varese', supporters: 1000, foundationYear: 1910).save()

....

}

void tearDown() {

Team.list()*.delete()

Testing Grails Action

void testSearch() {

def tc = new TeamController()

tc.params.championship = "Serie C2"

tc.search()

assertEquals "/team/list", tc.modelAndView.viewName

assertEquals 1, tc.modelAndView.model.teamCount

assertTrue tc.modelAndView.model.teamList.contains(team1)

}

Conclusions

- Some techniques (like Expandos or Categories) are useful when testing Groovy classes

- Mocking using maps is useful when testing Java classes but you have to pass the mocked object to the Class Under Test

- mockFor and stubFor are very powerful and are used as an alternative to frameworks like easymock, jMock...

- more frameworks are coming (Gmock)

- The future: Behavior driven development (BDD) ? easyB...

Reference

- Groovy in Action (D. Konig, A. Glover, P. King, G. Laforge, J. Skeet) Manning

- Programming Groovy Dynamic productivity for the Java developer (Venkat Subramaniam) Pragmatic Programmera

- http://groovy.codehaus.org/Testing+Guide

Click to edit the title text format

Click to edit the outline text format

Second Outline Level

Third Outline Level

Fourth Outline Level

Fifth Outline Level

Sixth Outline Level

Seventh Outline Level

Eighth Outline Level

Ninth Outline Level

Click to edit the title text format

Click to edit the outline text format