fixt
Build Status
badge

Set of useful test utilities. All the libraries are available in JCenter Maven repository.

1. Fixt

Fixt helps organize fixtures for your fixtures in a directories with the same name as your test class.

fixt

Given a test residing in src/test/java or src/test/groovy with name org.example.MyTest then the fixtures' location will src/test/resources/org/example/MyTest keeping all the files for given test in a single folder.

If you generate the fixture files from your tests it is always better to specify the path to the test resources folder using test.resources.folder system property or TEST_RESOURCES_FOLDER environment variable.

In Gradle, you can easily achive this by using following snippet:

Setting Test Resources Folder in Gradle
test {
    environment 'TEST_RESOURCES_FOLDER', new File(project.projectDir, 'src/test/resources').canonicalPath
}

Fixt guarantee that the directories are created before the test run so adding new fixture files will be easier.

1.1. Installation

Gradle
repositories {
    jcenter()
}

dependencies {
    testCompile 'com.agorapulse.testing:fixt:0.2.2'
}
Maven
<dependency>
    <groupId>com.agorapulse.testing</groupId>
    <artifactId>fixt</artifactId>
    <version>0.2.2</version>
    <scope>test</scope>
</dependency>

1.2. Usage

Example Specification
package com.agorapulse.testing.fixt

import org.junit.Rule
import org.junit.rules.TemporaryFolder
import spock.lang.Specification
import spock.util.environment.RestoreSystemProperties

@RestoreSystemProperties
class FixtSpec extends Specification {

    public static final String TEXT_FILE = 'text.txt'
    public static final String TEXT_CONTENT = 'Hello Text\n'
    public static final String STREAM_FILE = 'stream.txt'
    public static final String STREAM_CONTENT = 'Hello Stream\n'
    public static final String EMPTY_FILE = 'empty.txt'
    public static final String EMPTY_CONTENT = ''

    @Rule
    TemporaryFolder tmp = new TemporaryFolder()

    Fixt fixt = Fixt.create(this)                                                       (1)

    void 'reading existing files'() {
        expect:
            fixt.readStream(STREAM_FILE).text == STREAM_CONTENT                         (2)
            fixt.readText(TEXT_FILE) == TEXT_CONTENT                                    (3)
            fixt.readText(EMPTY_FILE) == EMPTY_CONTENT
    }

    void 'writing files'() {
        when:
            Fixt fixt = Fixt.create(ReferenceClass)                                     (4)
            File testResources = tmp.newFolder()

            System.setProperty('test.resources.folder', testResources.canonicalPath)

            fixt.mkdirs()                                                               (5)
        then:
            Fixt.testResourcesLocation == testResources.canonicalPath

        when:
            ByteArrayInputStream stream = new ByteArrayInputStream(STREAM_CONTENT.bytes)
            fixt.writeStream(STREAM_FILE, stream)                                       (6)
            fixt.writeText(TEXT_FILE, TEXT_CONTENT)                                     (7)

            File contextTestFolder = new File(testResources, 'com/agorapulse/testing/fixt/ReferenceClass')
        then:
            contextTestFolder.exists()
            new File(contextTestFolder, STREAM_FILE).text == STREAM_CONTENT
            new File(contextTestFolder, TEXT_FILE).text == TEXT_CONTENT
    }

}
1 Declare Fixt for the specification
2 Read the content of the file as stream
3 Read the content of the file as text
4 Create Fixt for the specific class (ReferenceClass)
5 Create all necessary directories if they do not exist yet
6 Write the content of the stream into a file
7 Write the text into a file

2. Grails Job Testing Support

Grails Job Testing Support brings the same trait-based tesing support to jobs created by the Quartz plugin as the original https://testing.grails.org/]Grails Testing Support]. It provides JobUnitTest<J> trait which your specification can implement in order to test the job more easily.

2.1. Installation

Gradle
repositories {
    jcenter()
}

dependencies {
    testCompile 'com.agorapulse.testing:grails-job-testing-support:0.2.2'
}

2.2. Usage

Example Job
package com.agorapulse.testing.grails.job.test

import groovy.transform.CompileDynamic

@CompileDynamic
@SuppressWarnings([
    'FieldTypeRequired',
    'NoDef',
])
class TestJob {

    static triggers = {
        simple name: 'mySimpleTrigger', startDelay: 60000, repeatInterval: 1000
    }

    static sessionRequired = true
    static concurrent = true
    static group = 'MyGroup'
    static description = 'Example job with Simple Trigger'

    Runnable runnable

    void execute() {
        runnable.run()
    }

}
Example Specification
package com.agorapulse.testing.grails.job.test

import com.agorapulse.testing.grails.job.JobUnitTest
import org.quartz.SimpleTrigger
import spock.lang.Specification

@SuppressWarnings('ClassStartsWithBlankLine')
class TestJobSpec extends Specification implements JobUnitTest<TestJob> {               (1)

    Runnable mock = Mock(Runnable)

    void setup() {
        job.runnable = mock                                                             (2)
    }

    void 'test trigger job'() {
        when:
            trigger job                                                                 (3)
        then:
            noExceptionThrown()

            1 * mock.run()
    }

    void 'check cofiguration'() {
        expect:
            triggers.any {                                                              (4)
                it instanceof SimpleTrigger && it.repeatInterval == 1000
            }
            jobClass.concurrent                                                         (5)
            jobClass.sessionRequired
            jobClass.description == 'Example job with Simple Trigger'
            jobClass.group == 'MyGroup'
    }

}
1 Implement JobUnitTest with the particular job under test
2 You can inject any mocks into the job instance
3 Trigger the job
4 Access the list of configured triggers
5 Access the job metadata

3. Office Unit

Office Unit is trivial tool for matching Microsoft Office documents. It leverage their nature of being archive files with XML content. It compares the content of the files using XmlUnit but it by default ignores date created and last updated timestamps. The typical usecase is to compare the reference file against the newly generated to keep the status quo of the exports.

For Excel documents, you should consider using SpreadsheetQuery instead.

3.1. Installation

Gradle
repositories {
    jcenter()
}

dependencies {
    testCompile 'com.agorapulse.testing:office-unit:0.2.2'
}
Maven
<dependency>
    <groupId>com.agorapulse.testing</groupId>
    <artifactId>office-unit</artifactId>
    <version>0.2.2</version>
    <scope>test</scope>
</dependency>

3.2. Usage

Example Specification
void 'no differences: #control with #test'() {
    expect:
        new OfficeUnit().compare(                                                   (1)
            loadFile(control),
            loadFile(test)
        ).empty
    where:
        control         | test
        'test1.pptx'    | 'test2.pptx'
        'test1.xlsx'    | 'test2.xlsx'
}

void '#differencesCount difference(s): #control with #test'() {
    given:
        OfficeUnit officeUnit = new OfficeUnit()
    expect:
        officeUnit.compare(                                                         (2)
                loadFile(control),
                loadFile(test)
        ).size() == differencesCount
    where:
        control         | test              | differencesCount
        'test1.xlsx'    | 'test3.xlsx'      | 1
        'test1.xlsx'    | 'test4.xlsx'      | 8
        'test1.xlsx'    | 'test5.xlsx'      | 159
        'test1.pptx'    | 'test3.pptx'      | 2469
}

void 'ignore some other part'() {
    given:
        OfficeUnit officeUnit = new OfficeUnit()
            .ignore('/sst[1]/si[3]/t[1]/text()[1]')                                 (3)
    expect:
        officeUnit.compare(
            loadFile('test1.xlsx'),
            loadFile('test3.xlsx')
        ).empty
}
1 Compare two Microsoft Office files and expect no difference
2 Expect some differences
3 Ignore some differences by XPath expression

4. Spock

Set of useful Spock Framework extensions.

  • @Detached will automatically attach mock created by DetachedMockFactory

  • @OverrideParentFeatures will override any parent features of the same name

4.1. Installation

Gradle
repositories {
    jcenter()
}

dependencies {
    testCompile 'com.agorapulse.testing:spock:0.2.2'
}
Maven
<dependency>
    <groupId>com.agorapulse.testing</groupId>
    <artifactId>spock</artifactId>
    <version>0.2.2</version>
    <scope>test</scope>
</dependency>

4.2. Usage

Detached Specification
package com.agorapulse.testing.spock

import spock.lang.Specification

class DetachedSpec extends Specification {

    @Detached Runnable runnable = MyMockFactory.mockRunnable()                          (1)

    void 'mock is attached'() {
        when:
            runnable.run()
        then:
            1 * runnable.run()
    }

}
1 You can attach mock which has been created out of the scope of current specification
Parent Specification
package com.agorapulse.testing.spock

import spock.lang.Specification

class ParentSpec extends Specification {

    void 'ignore me in child'() {                                                       (1)
        expect:
            this.getClass() == ParentSpec || this.getClass() == GrandChildSpec
    }

    void 'ignore me in grandchild'() {
        expect:
            this.getClass() == ParentSpec || this.getClass() == ChildSpec
    }

}
1 Feature methods declared in parent specification cannot be normally overriden
Child Specification
package com.agorapulse.testing.spock

class ChildSpec extends ParentSpec {

    @OverrideParentFeatures                                                             (1)
    void 'ignore me in child'() {
        expect:
            this.getClass() == ChildSpec || this.getClass() == GrandChildSpec
    }

}
1 Using OverrideParentFeatures any method with the same name in any parent is skipped

5. Maintained by

4a44735a 5034 11e6 8e72 9f4b7139d7e0