Showing posts with label testing. Show all posts
Showing posts with label testing. Show all posts

Friday, January 18, 2019

Spring REST testing

After too much Internet searching, I was unable to find an easy solution to repeated duplication in my Spring MockMVC tests of REST controller endpoints. For years now, the endpoints we write have typically sent or received JSON. This is what I mean:

mockMvc.perform(post("/some/endpoint")
        .contentType(APPLICATION_JSON_UTF8)
        .accept(APPLICATION_JSON_UTF8)
        .content(someRequestJson))
        .andExpect(status().isCreated())
        .andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_UTF8_VALUE))
        .andExpect(header().string(LOCATION, "/some/endpoint/name-or-id"))
        .andExpect(content().json(someResponseJson));

All the repeated "APPLICATION_JSON_UTF8"s, in every controller test!

If there is an existing Spring testing solution, I'd love to hear about it. Rather than wait, I wrote up a small extension of @WebMvcTest to default these values.

First, an annotation for Spring to use in setting up a MockMvc (javadoc elided):

@Documented
@Import(JsonMockMvcConfiguration.class)
@Retention(RUNTIME)
@Target(TYPE)
@WebMvcTest
public @interface JsonWebMvcTest {
    @AliasFor(annotation = WebMvcTest.class)
    String[] properties() default {};

    @AliasFor(annotation = WebMvcTest.class)
    Class[] value() default {};

    @AliasFor(annotation = WebMvcTest.class)
    Class[] controllers() default {};

    @AliasFor(annotation = WebMvcTest.class)
    boolean useDefaultFilters() default true;

    @AliasFor(annotation = WebMvcTest.class)
    ComponentScan.Filter[] includeFilters() default {};

    @AliasFor(annotation = WebMvcTest.class)
    ComponentScan.Filter[] excludeFilters() default {};

    @AliasFor(annotation = WebMvcTest.class)
    Class[] excludeAutoConfiguration() default {};
}

Note it is a near exact lookalike of @WebMvcTest (minus the deprecated parameter). The important bits are:

  1. Marking this annotation with @WebMvcTest, a kind of extension through composition.
  2. Adding @Import to bind custom configuration to this annotation.
  3. Tying the same-named annotation parameters to @WebMvcTest, so this annotation is a drop-in replacement of that one.

Next a configuration class, imported by the annotation, to customize MockMvc:

@Configuration
public class JsonMockMvcConfiguration {
    @Bean
    @Primary
    public MockMvc jsonMockMvc(final WebApplicationContext ctx) {
        return webAppContextSetup(ctx)
                .defaultRequest(post("/")
                        .contentType(APPLICATION_JSON_UTF8)
                        .accept(APPLICATION_JSON_UTF8_VALUE))
                .alwaysExpect(header().string(
                        CONTENT_TYPE, APPLICATION_JSON_UTF8_VALUE))
                .build();
    }
}

Some points about this class:

  • @Primary is not necessary for Spring, but helped IntelliJ — perhaps I got lucky with Spring without @Primary, and IntelliJ highlighted a real problem.
  • It took quite a while to get defaultRequest(...) working. I was unable to (re)implement the relevant interfaces, and eventually found that passing any MockHttpServletRequestBuilder sufficed. Spring "merges" (overlays) the actual request builder from the test over this default, replacing POST and "/" with whichever HTTP method and path the test uses (eg, GET "/bob"). Only the header customization is used.

Example usage:

@JsonWebMvcTest(SomeController.class)
class SomeControllerTest {
    @Autowired
    private MockMvc jsonMockMvc;

    @Test
    void shouldCheckSomething()
            throws Exception {
        jsonMockMvc.perform(post("/some/endpoint")
                .content(someRequestJson))
                .andExpect(status().isCreated())
                .andExpect(header()
                        .string(LOCATION, "/some/endpoint/new-name"))
                .andExpect(content().json(someResponseJson));
    }
}

See the Basilisk project for source code and sample usage. (Basilisk is a demonstration project for my team illustrating Spring usage and conventions.)

Sunday, July 29, 2018

Kotlin JPA patterns

Kotlin JPA pattern

The problem

Kotlin does many wonderful things for you. For example, the data classes create helpful constructors, and automatically implement equals and hashCode in a reasonable way.

Similarly, JPA works magic—expecially in the context of Spring Data.

So how do I test that my Kotlin entity is correctly annotated for JPA? The simplest thing would be a "round trip" test: create an entity, save it to a database, read it back, and confirm the object has the same values. Let's start with a simple entity, and the simplest possible test:

@Entity
data class Greeting(
        val content: String,
        @Id @GeneratedValue
        val: Int id = 0)
@DataJpaTest
@ExtendWith(SpringExtension::class)
internal class GreetingRepositoryIT(
        @Autowired val repository: GreetingRepository,
        @Autowired val entityManager: EntityManager) {
    @DisplayName("WHEN saving a greeting properly annotated")
    @Nested
    inner class Roundtrip {
        @Test
        fun `THEN is can be read back`() {
            val greeting = Greeting("Hello, world!")

            repository.saveAndFlush(greeting.copy())
            entityManager.clear()

            assertThat(repository.findOne(Example.of(greeting)).get())
                    .isEqualTo(greeting)
        }
    }
}

Some things to note:

  1. To ensure we truly read from the database, and not the entity manager's in-memory cache, flush the object and clear the cache.
  2. As saving also updates the entity's id field, save a copy, so our original is untouched.
  3. Be careful to use saveAndFlush on the Spring repository, rather than entityManager.flush(), which requires a transaction, and would add unneeded complexity to the test.

But this test fails! Why?

The unsaved entity (remember, we made a copy to keep the original pristine) does not have a value for id, and the entity read back does. Hence, the automatically generated equals method says the two objects differ because of id (null in the original vs some value from the database).

Further, the Spring Data QBE (QBE) search for our entity includes id in the search criteria. Even changing equals would not address this.

What to do?

The solution

It turns out we need to address two issues:

  1. The generated equals takes id into account, but we are only interested in the data values, not the database administrivia.
  2. The test look up in the database includes the SQL id column. Although we could try repository.getOne(saved.id), I'd prefer to keep using QBE, if the code is reasonable.

To address equals, we can rely on an interesting fact about Kotlin data classes: only default constructor parameters are used, not properties in the class body, when generating equals and hashCode. Hence, I write the entity like this, and equals does not include id, while JPA is stil happy as it relies on getter reflection:

@Entity
data class Greeting(
        val content: String) {
    @Id
    @GeneratedValue
    val id = 0
}

To address the test, we can ask QBE to ignore id when fetching our saved entity back from the database:

@DataJpaTest
@ExtendWith(SpringExtension::class)
internal class GreetingRepositoryIT(
        @Autowired val repository: GreetingRepository,
        @Autowired val entityManager: EntityManager) {
    @DisplayName("WHEN saving a greeting properly annotated")
    @Nested
    inner class Roundtrip {
        @Test
        fun `THEN is can be read back`() {
            val greeting = Greeting("Hello, world!")

            repository.saveAndFlush(greeting.copy())
            entityManager.clear()

            val matcher = ExampleMatcher.matching()
                    .withIgnoreNullValues()
                    .withIgnorePaths("id")
            val example = Example.of(greeting, matcher);

            assertThat(repository.findOne(example).get()).isEqualTo(greeting)
        }
    }
}

In a larger database, I'd look into providing an entity.asExample() to avoid duplicating ExampleMatcher in each test.

Java approach

The closest to Kotlin's data classes for JPA entities is Lombok's @Data annotation, together with @EqualsAndHashCode(exclude = "id") and @Builder(toBuilder = true), however the expressiveness is lower, and clutter higher.

The test would be largely the same modulo language, replacing greeting.copy() with greeting.toBuilder().build(). Alternatively, rather than a QBE matcher, one could write greeting.toBuilder().id(null).build().

This last fact leads to an alternative with Kotlin: include id in the data class' default constructor, and in the test compare the QBE result as findOne(example).get().copy(id = null) without a matcher.

Conclusion

What Kotlin JPA patterns have you discovered?

Tuesday, May 01, 2018

JaCoCo, Gradle, and exclusions

The setup

My team is working on a Java server, as part of a larger project project, using Gradle to build and JaCoCo to measure testing code coverage. The build fails if coverage drops below fixed limits (branch, instruction, and line)—"verification" in JaCoCo-speak.

We follow the strategy of The Ratchet: as dev pairs push commits into the project, code coverage may not drop without group agreement, and if coverage rises, the verification limits rise to match. This ensures we have ever-rising coverage, and avoid new code which lacks adequate testing.

The problem

At a work project, we're struggling to get JaCoCo to ignore some new, configuration-only Java classes. These classes have no "real" implementation code to test, are used to setup communication with an external resource, yet are high line-count (static configuration via code). So they drag down our code coverage limits, and there is no effective way to unit test them sensibly. (They are best tested as system tests within our CI pipeline using live programs and remote resources.)

JaCoCo has what seems at first blush a sensible way to exclude these configuration classes from testing:

jacocoTestVerificationCoverage {
    violationRules {
        rule {
            excludes ['hm.binkley.labs.saml.SomeConfig']
            limit {
                counter = 'LINE'
                minimum = 0.90
            }
        }
    }
}

Unfortunately, this does nothing. There is no warning or error, and coverage continues to include the whole code base.

A solution

After a lot of experimenting and StackOverflow research, this answer from Juan Vimberg worked exactly as we needed. Following his approach:

final def excludedClasses = ['hm.binkley.labs.saml.SomeConfig']

jacocoTestVerificationCoverage {
    violationRules {
        rule {
            limit {
                counter = 'LINE'
                minimum = 0.90
            }
        }
    }

    afterEvaluate {
        classDirectories = files(classDirectories.files.collect {
            fileTree(dir: it, excludes: excludedClasses.collect {
                it.replace('.', '/') + '.class'
            })
        })
    }
}

The list of excluded classes is extracted so the same trick can be used in the generated reports:

jacocoTestReport {
    executionData test, databaseTest
    reports {
        html.enabled = true
        xml.enabled = true
        csv.enabled = false
    }
    afterEvaluate {
        classDirectories = files(classDirectories.files.collect {
            fileTree(dir: it, excludes: excludedClasses.collect {
                it.replace('.', '/') + '.class'
            })
        })
    }
}

Something to consider: using wildcards (hm.binkley.labs.saml.*) may take additional work.

Why?

Why does this work, and the "obvious" way does not?

JaCoCo has more than one notion of scoping. The clearest one is the counters: branches, classes, instructions, lines, and methods.

Not as well documented is the scope of checks: bundles, classes, methods, packages, and source files. These are not mix-and-match. For example, exclusions apply to classes. Lyudmil Latinov has the best hints I've found on how this works.

Thursday, January 11, 2018

Automated acceptance criteria

Automated Acceptance Criteria

Automated Acceptance Criteria

I have a dream (Story Card)

What is my dream story card? I don't mean: What's the story I'd most like to work on! I mean: What should a virtual story card look like (as opposed to card stock on a wall)? This may be a trivial question. But for me the user experience working with stories is very important: I work with them daily, write them, discuss them, work on them, accept them, etc. I want the feel of the card to be thoughtful, like that fellow to the right.

And more than that. I am lazy, impatient, hubristic. The acceptance criteria, I want them testable, literally testable in that each has a matching test I can execute. Given my laziness, I don't want to switch systems and run tests; I'd like to execute acceptance criteria directly from the story card.

So is there a system like that today? No. There are bits and pieces though.

Not all story card systems are equal

Some story card systems are particularly awkward to read, understand or use. Special demerits for:

  • A hard-coded workflow that the team cannot be change to fit how they work: the team is expected to fit the tool
  • Workflow state transition buttons are nice, but not so nice is unconfigurable labels, especially when the button labels are misleading
  • A hard-coded or limited hierarchy of stories, so if a team uses epics or features or themes or whatever to organize stories, and there are more than one level to this, the team is out of luck
  • Lack of quality RESTful support, in particular, no simple identifier for story cards, so linking directly to cards is opaque, useless or completely absent

A scenario

Post-development testing on this team is a fairly ordinary role. The developers say: a new web page is ready. Testers then validate the same page features each time for each new page, simple things:

  • Can an account with security role X log in?
  • Can X submit the form? (Or not submit if forbidden?)
  • What form defaults appear for role X? Do they reflect role X?

(Yes, I know — what about developer testing? Bear with me.)

On it goes, the same work each time. Redundant, repetitive, error-prone, fiddly. And worst of all—boring, BORING! This is traditional, manual "user testing" at its worst.

What's to be done? Can we fix this?

After all, the testing is valuable: nobody wants broken web pages; everybody wants to log in. But the tester is valuable, too, more valuable even: is this the most valuable testing a human could do? Surely people are more clever, more insightful than this. And what about all the other page features not tested because of time spent on the basics?

Well, people are more clever than this.

Clearing the path

What guides testing? If you're using nearly any form of modern user story writing, this includes something like "Acceptance Criteria". These are the gates that permit story development to be called successful: the testers are gatekeepers in the manual testing world. In the manual world these criteria might be congregated into a single "Requirements Document" or similar (think: big, upfront design).

We can do better! Gatekeeper-style testing assumes a linear path from requirements to implementation to testing, just as waterfall considers these activities as distinct phases. But we know agile approaches do better than waterfall in most cases. Why should we build our teams to mirror waterfall? Of course the answer is to structure teams to look agile, just as the team itself practices agile values.

So how do we make Acceptance Criteria more agile?

Enter the Three Amigos

In current agile practice, a story card is not a ready to play until approved by the The Three Amigos: BA, Dev, QA. Each plays their part, brings their perspective, contributes to meeting team-agreed "Definition of Ready".

A key component of playable cards are the Acceptance Criteria — answering the question, "What does success look like?" when a story is finished.

The perspectives include:

  • BA: Is the story told right? — What is the way to describe the work?
  • Dev: Is the story the right size? — What is the complexity of the work?
  • QA: Is it the right story to tell? — What is the value of the work?

What are Acceptance Criteria?

But where does this simple testing come from? Any software delivery process beyond "winging it" has some requirements

Well-written agile stories have Acceptance Criteria. What are these? An Acceptance Criteria (AC) is a statement in a story that a tester (QA) can use to validate that all or a portion of the story is complete. The formulaic phrasing for ACs I like best is:

GIVEN some state of the world
WHEN some change happens
THEN some observable outcome results

Generally for web applications this means when I do something with a web page in the application, then the page changes in some particular way or submits a request and the response page has some particular quality or property (or the negative case that is does not have that quality or property).

In some cases it is even simpler: just check that a particular web address fully loads, for example, when testing login access to pages.

Martin Fowler's Test Pyramid

So what's the question?

Wherever possible we want to automate. If something can be done for us by a computing machine, we don't want to spend human time on it. Humans can move on to more interesting, valuable work when existing tasks can be automated.

Consider the Test Pyramid (image right): automating lower-value tests focuses people on higher-value ones. You get more human attention and insight on the kinds of tests which best improve the value of software. You win more.

The Story

This is a sample story card with a simplistic implementation of AACs. Live buttons call back into the story system, to be mapped to calls in the testing system. (Another implementation might have the buttons directly call to the testing system, avoiding an extra call into the story system but showing details in the page source about the testing system.)

Title

Narrative

AS A AAC author
I WANT a mock executable story
SO THAT others can see the value

Details

No actual criteria were validated in the execution of these tests. This is only a mock.

Acceptance criteria

Summary: 1 missing, 1 untested, 1 running, 1 passed, 1 failed, 1 errored, 1 disabled
GIVEN magical thinking
WHEN in Missingland
THEN there's no test
No test (yet) - create one!
GIVEN magical thinking
WHEN in Newland
THEN nothing has happened yet
Test never run - be the first!
GIVEN magical thinking
WHEN in Fastland
THEN tests run quickly
15% done (3s)
GIVEN magical thinking
WHEN in Happyland
THEN Unicorns
GIVEN magical thinking
WHEN in Sadland
THEN there be Dragons
Expected: Dragons, got: Puppies
GIVEN magical thinking
WHEN in Crazyland
THEN nothing works right
Test timed out after 90 seconds
GIVEN magical thinking
WHEN in Slowland
THEN tests are disabled
@dev1 @qa2: BLOCKED on widget spanner

Acceptance Criteria states

Every AC potentially has a message from the testing system giving more detail on state. These are noted below.

Missing

This AC has no matching test in the testing system. Use the Create button to create a new test. This does not run the test.

The message is boilerplate to remind users to create tests.

Untested

The AC has a matching test in the testing system, but the test has never been run. Use the Test button to run the test.

Typically there is no message for this state.

Running

The matching test for the AC is running in the testing system. Use the Cancel button to stop the test, or wait for it to complete.

The message, is supported by the testing system, should give a notion of progress. See REST and long-running jobs for how to do this.

Passed

The matching test for the AC passed last time it ran. Use the Test button to run the test again.

Typically there is no message for this state.

Failed

The matching test for the AC failed last time it ran. Use the Test button to run the test again.

The message must give a notion of why the test failed.

Errored

The matching test for the AC errored last time it ran. Use the Test button to run the test again.

The message must give a notion of why the test errored.

Disabled

The matching test for the AC is disabled in the testing system. Update the testing system to reenable.

The message, if supported, should give a reason the test is disabled when available.

Potential problems

Nothing is free. Potential problems with AACs include:

Integrations

There are no existing integrations along these lines. You need to build your own against JIRA, Mingle, FitNesse, Cucumber, etc. Whether the story system drives the interaction, or the test system does, may depend on the exact combination. Best would be if both systems can call the other.

Scaling

As more AACs run, the complete suite takes longer. For example, adding 1 minute of AAC/story, and 5 stories/iteration, in a 12 iteration project takes 60 minutes to run. This is not specific to AACs but a general problem with acceptance tests. It's still much cheaper than the manual steps for each test, but prohibitive for a developer to run the whole suite locally.

Best practice is for the 3 amigos to run only the tests specific to a story before calling that story Ready to Accept.

Update

Until I published this post on Blogger, I really wasn't certain how it would look on that platform. I manually tested the visuals from Chrome with the local post, and it looked good. After seeing it in Blogger, however, the "aside" sections are laid out poorly, overlapping the text. I won't relay the page: mistakes are the best way to improve, and a subtext of this post is Experiment & Learn Rapidly. Public experiements are the most faithful kind: no opportunity to fudge and pretend all was well on the first try.

Friday, August 11, 2017

How to write clean Java

I cannot tell you how to write good Java, but I can help you write clean Java. As usual, automation is key. It's all about the tooling.

The best thing about good tooling is that they work together: each covers a different area, does not impeded another tool, and fixing one complaint a tool reveals often fixes complaints from other tools.

This advice applies to any programming language, not just Java. Java, having the most mature ecosystem, is instructive.

The tools

Use good source control
Git is your best choice. Use either trunk-based development with feature toggles (feature flags), or gitflow patterns, depending on your needs and organization. Require full builds and testing before pushing to a shared repository; hooks can help together with your build tool.
Use good build automation
Maven or Gradle are your best choices; either is excellent. Teach the tool to be strict and fail builds if anything is not just right. Treat your build configuration as code, part of the same repository and held to the same hygiene standards. Keep your local build fast.
Use a good editor
IntelliJ is the best choice. Use inspections, intentions, and reformatting obsessively: code diffs should only show real changes, not whitespace or formatting. Use plugins to ease integrating IntelliJ with other tooling.
Keep your style consistent
Checkstyle is your best choice; other choices are needed for non-Java JVM languages. Fail your build if checkstyle complains. Keep your IntelliJ formatting and Checkstyle rules consistent. Generally, choose either Sun or Google coding standards, and be leary of deviating from them; if unsure, pick Sun.
Fully test your code
JaCoCo works well. Fail your build if coverage drops below a threshhold; rachet up the threshhold as coverage improves. Start low and aim for a 95%+ threshhold. Follow the Test Pyramid; save your end-to-end tests for CI, or run locally only once before pushing commits.
Be zealous in looking for problems
FindBugs, PMD, or Error Prone are your best choices (pick one); other choices may work better for non-Java JVM languages. Fail your build if your choice complains. Be judicious in disabling complaints (for example, FindBugs "experimental" checks, should likely be disabled).
Use code generation
Lombok is your first choice. Add others as needed (or build domain-specific code generators). Generated code does not need test coverage, style checks, or bug detection if the generator is clean and well-tested: trust it.

Update

Dan Wallach reminded me of Error Prone, added above.

Monday, March 13, 2017

Frequent commits

Pair posting with guest Sarah Krueger!

A source control pattern for TDD

At work we recently revisited our commit practices. One issue spotted: we didn't commit often enough. To address we adopted the source control pattern in this post. There are lots of benefits; the one that mattered to me most: No more throwing the baby out with the bathwater, that is, no more two hour coding sessions only to start again and lose the good with the bad.

So we worked out this command-line pattern using single unit-of-work commits (without git rebase -i!):

# TDD cycle: edit code, run tests, rather-rinse-repeat until green
$ git pull --rebase --autostash && run tests && git commit -a --amend --no-edit
# Simple unit-of-work commit, push, begin TDD cycle again
$ git commit --amend && git push && git commit --allow-empty -m WIP

What is this?

  1. Start with a pull. This ensures you are always current, and find conflicts as soon as possible.
  2. Run your full tests. This depends on your project, for example, mvn verify or rake. If some tests are slow, split them out, and add a full test run before pushing.
  3. Amend your work to the current commit. This gives you a safe fallback known to pass tests. Worst case you might lose some recent work, but not hours worth. (Hint: run tests often.)
  4. When ready to push, update the commit message to the final message for public push.
  5. Push. Share. Make the world better.
  6. Restart the TDD cycle with an empty commit using a message that makes sense to you, for example "WIP" (work in progress); the message should be obvious not to push. Key: the TDD cycle command line only amends commits, so you need a first, empty commit to amend against.

Why?

They key feature of this source control pattern is: Always commit after reaching green on tests; never commit without testing. When tests fail, the commit fails (the && is short-circuit logical and).

In the math sense, this pattern makes testing and committing one-to-one and onto. Since TDD requires frequent running of tests, this means frequent commits when those tests pass. To avoid a long series of tiny commits when pushing, amend to collect a unit of work.

Bootstrapping

The TDD cycle depends on an initial, empty commit. The first time using this source control pattern:

# Do this after the most recent commit, before any edits
$ git commit --allow-empty -m WIP

Adding files

This pattern, though very useful, does not address new files. You do need to run git add with new files to include them in the commit. Automatically adding new files can be dangerous if gitignore isn't set up right.

It depends on your style

The exact command line depends on your style. You could include a script to run before tests, or before commit (though the latter might be better done with a git pre-commit hook). You might prefer merge pulls instead of rebase pulls. If your editor runs from the command line you might toss $EDITOR at the front of the TDD cycle.

The command lines assume git, but this source control pattern works with any system that supports similar functionality.

Fine-grained commits

An example of style choice. If you prefer fine-grained commits to unit-of-work single commits (depending on your taste or project; they're both good practice):

# TDD cycle: edit code, run tests, rather-rinse-repeat until green
$ git pull --rebase --autostash && run tests && git commit -a
# Fine-grained commits, push, begin TDD cycle again
$ git push

Improving your life

No matter your exact command line, it can be made friendlier for you. Yes, shell history can story your long chain of commands. What if they vary slightly between programmers sharing a project, or what if there is a common standard approach? Extend git. Let's call our example subcommand "tdd". Save this in a file named git-tdd in your $PATH:

#!/bin/sh
set -e
case $1 in
    test ) git pull --rebase --autostash && run tests && git commit -a --amend --no-edit ;;
    accept ) git commit --amend && git push && git commit --allow-empty -m WIP ;;
esac

Now your command line becomes:

$ git tdd test  # Repeat until unit of work is ready
$ git tdd accept

The source is in GitHub.

Updated:

An editing error left out the Why? section when initially posted.

Remember to autostash.

A follow up post: Push early, push often, push on green.

Saturday, March 11, 2017

Two BDD styles in Kotlin

Experimenting with BDD syntax in Kotlin, I tried these two styles:

fun main(args: Array<String>) {
    println(So
            GIVEN "an apple"
            WHEN "it falls"
            THEN "Newton thinks")
}

data class BDD constructor(
        val GIVEN: String, val WHEN: String, val THEN: String) {
    companion object {
        val So = So()
    }

    class So {
        infix fun GIVEN(GIVEN: String) = Given(GIVEN)
        data class Given(private val GIVEN: String) {
            infix fun WHEN(WHEN: String) = When(GIVEN, WHEN)
            data class When(private val GIVEN: String, private val WHEN: String) {
                infix fun THEN(THEN: String) = BDD(GIVEN, WHEN, THEN)
            }
        }
    }
}

And:

fun main(args: Array<String>) {
    println(GIVEN `an apple`
            WHEN `it falls`
            THEN `Newton thinks`
            QED)
}

infix fun Given.`an apple`(WHEN: When) = When()
infix fun When.`it falls`(THEN: Then) = Then(GIVEN)
infix fun Then.`Newton thinks`(QED: Qed) = BDD(GIVEN, WHEN)

inline fun whoami() = Throwable().stackTrace[1].methodName

data class BDD(val GIVEN: String, val WHEN: String, val THEN: String = whoami()) {
    companion object {
        val GIVEN = Given()
        val WHEN = When()
        val THEN = Then("")
        val QED = Qed()
    }

    class Given
    class When(val GIVEN: String = whoami())
    class Then(val GIVEN: String, val WHEN: String = whoami())
    class Qed
}

Comparing main() methods, which is easier to read or use? I haven't tried implementing, just have looked at testing code style. Note that I'm using the infix feature of Kotlin to have my BDD "GIVEN/WHEN/THEN" as punctuation free as I'm able.

In the one case—using strings to describe cases—, an implementation would be more similar to Spec or Cucumber, which usually uses pattern matching to associate text with implementation. In the other case—using functions to describe cases—, an implementation goes directly into the function definition. In either case, Kotlin only supports binary infix functions, not unary (of course, you say, that's what "infix" means!), so I need either an initial starting token (So in the strings case) or an ending one (QED in the functions case).

I'm curious how implementation sorts out.

(Code here.)

UPDATE:

I have working code now that runs these BDD sentences but remain unclear which of the two styles (strings vs functions) would be easier to work with:

fun main(args: Array<String>) {
    var apple: Apple? = null
    upon("an apple") {
        apple = Apple(Newton(thinking = false))
    }
    upon("it falls") {
        apple?.falls()
    }
    upon("Newton thinks") {
        assert(apple?.physicist?.thinking ?: false) {
            "Newton is sleeping"
        }
    }

    println(So
            GIVEN "an apple"
            WHEN "it falls"
            THEN "Newton thinks")
}

Vs:

fun main(args: Array<String>) {
    println(GIVEN `an apple`
            WHEN `it falls`
            THEN `Newton thinks`
            QED)
}

var apple: Apple? = null

infix fun Given.`an apple`(WHEN: When) = upon(this) {
    apple = Apple(Newton(thinking = false))
}

infix fun When.`it falls`(THEN: Then) = upon(this) {
    apple?.falls()
}

infix fun Then.`Newton thinks`(QED: Qed) = upon(this) {
    assert(apple?.physicist?.thinking ?: false) {
        "Newton is sleeping"
    }
}

The strings style is certainly more familiar. However, mistakes in registering matches of "GIVEN/WHEN/THEN" clauses appear at runtime and do not provide much help.

The functions style is more obtuse. However, mistakes cause compile-time errors that are easier to understand, and your code editor can navigate between declaration and usage.

Friday, September 16, 2016

Google Test, generated source, and GNU Make

I had trouble with this arrangement:

  • Using Pro*C for a client to generate "C" files from .pc sources
  • Google Test for unit testing
  • GNU Make

What was the problem?

Ideally I could write a pattern rule like this:

%-test.o: %.c %-test.cc

This means when I want to compile my C++ test source, it required make first run the Pro*C preprocessor to generate a "C" source used in the test. Why? Google tests follow this template:

#include "source-to-test.c"
#include <gtest/gtest.h>
// Tests follow

Google test includes your source file (not header) so the test code has access to static variables and functions (think "private" if you're from Java or C#).

So my problem is make is very clever with rules like:

%.foo: %.bar
%.qux: %.foo

And knows that if you want a "bob.qux", which needs a "bob.foo", and there's no "bob.foo" but there is a file named "foo.bar", make follows the recipe for turning a "bar" into a "foo", and this satisfies the rule for "bob.qux".

However the simple rule I guessed at:

%-test.o: %.c %-test.cc

Doesn't work! GNU Make has a corner case when there are multiple prerequisites (dependencies), and won't make missing files even where there's another rule saying how to do so.

There is another way to state what I want:

%-test.o: %-test.cc: %.c

This is called a static rule. It looks promising, but again doesn't work. GNU make does not support patterns (the "%") in static rules. I would need to write each case out explicitly, e.g.:

a-test.o: a-test.cc: a.c

While this does work, it's also a problem.


What's wrong with being explicit?

Nothing is wrong with "explicit" per se. Usually it's a good thing. In this case, it clashes with the rule of "say it once". For each test module a programmer writes, he would need to edit the Makefile with a new, duplicative rule. So when new tests break, instead of thinking "my code is wrong", he needs to ask "is it my code, or my build?" Extra cognitive burden.


What does work?

There is a way to get the benefit of a static rule without the duplication, but it's hackery—good hackery, to be sure, but violating the "rule of least surprise". Use make's powers to rewrite the Makefile at run time:

define BUILD_test
$(1:%=%.o): $(1:%-test=%.c) $(1:%=%.cc)
        $$(COMPILE.cc) $$(OUTPUT_OPTION) $(1:%=%.cc)
$(1): $(1:%=%.o)
endef
$(foreach t,$(wildcard *-test.cc),$(eval $(call BUILD_test,$(t:%.cc=%))))

What a mouthful! If I have a "foo-test.cc" file, make inserts these rules into the build:

foo-test.o: foo.c foo-test.cc
        $(COMPILE.cc) $(OUTPUT_OPTION) foo-test.cc
foo-test: foo-test.o

I'd like something simpler, less inscrutable. Suggestions welcome!

Saturday, May 21, 2016

Metaprogramming with Bash

Most programmers do not take full advantage of the languages they work in, though some languages make this a real challenge. Take metaprogramming, or programs that have some self-knowledge. LISP-family languages make this easy and natural; those with macros even more so. Bytecode languages (think Java), and even more so object code languages (think "C"), fall back on extra-linguistic magic such as AOP rewriting.

Text-based languages lay in a middle ground. Best known is Bash. Rarely do programmers take full advantage of Bash features, and few would think of metaprogramming. Not as clean as LISP macros, it is still straight forward.

As an example of function rewriting note line 21. The surrounding function body redefines an existing function, incorporating the original's body into itself. This is very similar to aspect-oriented programming with a "around" advice. Much care is taken to preserve the original function context.

This is a fully working script—give it a try!

#!/bin/bash

export PS4='+${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): } '

pgreen=$(printf "\e[32m")
pred=$(printf "\e[31m")
pboldred=$(printf "\e[31;1m")
preset=$(printf "\e[0m")
pcheckmark=$(printf "\xE2\x9C\x93")
pballotx=$(printf "\xE2\x9C\x97")
pinterrobang=$(printf "\xE2\x80\xBD")

function _register {
    case $# in
    1 ) local -r name=$1 arity=0 ;;
    2 ) local -r name=$1 arity=$2 ;;
    esac
    read -d '' -r wrapper <<EOF
function $name {
    # Original function
    $(declare -f $name | sed '1,2d;$d')

    __e=\$?
    shift $arity
    if (( 0 < __e || 0 == \$# ))
    then
        __tally \$__e
    else
        "\$@"
        __e=\$?
        __tally \$__e
    fi
}
EOF
    eval "$wrapper"
}

let __passed=0 __failed=0 __errored=0
function __tally {
    local -r __e=$1
    $__tallied && return $__e
    __tallied=true
    case $__e in
    0 ) let ++__passed ;;
    1 ) let ++__failed ;;
    * ) let ++__errored ;;
    esac
    _print_result $__e
    return $__e
}

function _print_result {
    local -r __e=$1
    case $__e in
    0 ) echo -e $pgreen$pcheckmark$preset $_scenario_name ;;
    1 ) echo -e $pred$pballotx$preset $_scenario_name ;;
    * ) echo -e $pboldred$pinterrobang$preset $_scenario_name ;;
    esac
}

function check_exit {
    (( $? == $1 ))
}

function make_exit {
    local -r e=$1
    shift
    (exit $e)
    "$@"
}

function check_d {
    [[ $PWD == $1 ]]
}

function change_d {
    cd $1
}

function variadic {
    :
}

function early_return {
    return $1
}

function eq {
    [[ "$bob" == nancy ]]
}

function normal_return {
    (exit $1)
}

function f {
    local -r bob=nancy
}

function AND {
    "$@"
}

function SCENARIO {
    local -r _scenario_name="$1"
    shift
    local __tallied=false
    local __e=0
    pushd $PWD >/dev/null
    "$@"
    __tally $?
    popd >/dev/null
}

_register f
_register normal_return 1
_register variadic 1
_register eq 
_register early_return 1
_register change_d 1
_register check_exit 1

echo "Expect 10 passes, 4 failures and 4 errors:"
SCENARIO "Normal return pass direct" normal_return 0
SCENARIO "Normal return fail direct" normal_return 1
SCENARIO "Normal return error direct" normal_return 2
SCENARIO "Normal return pass indirect" f AND normal_return 0
SCENARIO "Normal return fail indirect" f AND normal_return 1
SCENARIO "Normal return error indirect" f AND normal_return 2
SCENARIO "Early return pass indirect" f AND early_return 0
SCENARIO "Early return fail indirect" f AND early_return 1
SCENARIO "Early return error indirect" f AND early_return 2
SCENARIO "Early return pass direct" early_return 0
SCENARIO "Early return fail direct" early_return 1
SCENARIO "Early return error direct" early_return 2
SCENARIO "Variadic with none" f AND variadic
SCENARIO "Variadic with one" f AND variadic apple
SCENARIO "Local vars" f AND eq
here=$PWD
SCENARIO "Change directory" change_d /tmp
SCENARIO "Check directory" check_d $here
SCENARIO "Check exit" make_exit 1 AND check_exit 1

(( 0 == __passed )) || ppassed=$pgreen
(( 0 == __failed )) || pfailed=$pred
(( 0 == __errored )) || perrored=$pboldred
cat <<EOS
$ppassed$__passed PASSED$preset, $pfailed$__failed FAILED$preset, $perrored$__errored ERRORED$preset
EOS

Practical use of this script as a test framework would pull out SCENARIO and AND to a separate source script, included with "." or source, put the registered functions and their helpers in another source script, and provide command-line parsing to pick out which tests to execute. test-bitsh.sh is an example in progress.

Monday, May 02, 2016

Do not test Java getters and setters

An excellent project from Osman Shoukry to automate testing of Java getters and setters—that is when you have getters and setters to test. There's the rub: do not write getters or setters.

For starters they violate encapsulation, exposing your objects innards to others. Ok, but there are frameworks which require them, even in 2016. What to do?

Generate them:

@Getter
@Setter
@RequiredArgsConstructor
public final class SampleBean {
    private final String left;
    private String right;
}

With what result?

public final class SampleBean {
    private final String left;
    private String right;

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public String getLeft() {
        return this.left;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public String getRight() {
        return this.right;
    }

    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public void setRight(final String right) {
        this.right = right;
    }

    @java.beans.ConstructorProperties({"left"})
    @java.lang.SuppressWarnings("all")
    @javax.annotation.Generated("lombok")
    public SampleBean(final String left) {
        this.left = left;
    }
}

This code need never be tested. Test the generator, not the generated code. If the generator is correct, so is the generated code. In this case, Lombok is heavily tested.

As an alternative to the constructor taking field values, Jesse Wilson has interesting advice.

Thursday, April 07, 2016

BDD-style fluent testing in BASH

I wanted to impress on my colleagues that BASH was still hip, still relevant. And that I wasn't an old hacker. So I wrote a small BDD test framework for BASH.

Techniques

Fluent coding relies on several BASH features:

  • Variable expansion happens before executing commands
  • A shell function is indistinguishable from a program: they are called the same way
  • Local function variables are dynamically scoped but only within a function, so are visible to other functions called within that scope, directly or indirectly through further function calls

Together with Builder pattern, it's easy to write given/when/then tests. (Builder pattern here solves the problem not of telescoping constructors, but massive, arbitrary argument lists.)

So when you run:

function c {
    echo "$message"
}

function b {
    "$@"
}

function a {
    local message="$1"
    shift
    "$@"
}

a "Bob's your uncle" b c

You see the output:

Bob's your uncle

How does this work?

First BASH expands variables. In function a this means that after the first argument is remembered and removed from the argument list, "$@" expands to b c. Then b c is executed.

Then BASH calls the function b with argument "c". Similarly b expands "$@" to c and calls it.

Finally as $message is visible in functions called by a, c prints the first argument passed to a (as it was remebered in the variable $message), or "Bob's your uncle" in this example.

Running the snippet with xtrace makes this clear (assuming the snippet is saved in a file named example):

bash -x example
+ a 'Bob'\''s your uncle' b c
+ local 'message=Bob'\''s your uncle'
+ shift
+ b c
+ c
+ echo 'Bob'\''s your uncle'
Bob's your uncle

So the test functions for given_jar, when_run and then_expect (along with other, similar functions) work the same way. Keep this in mind.

Application

So how does this buy me fluent BDD?

Given these functions:

function then_expect {
    local expectation="$1"
    shift

    if [[ some_test "$pre_condition" "$condition" "$expectation" ]]
    then
        echo "PASS: $scenario"
        return 0
    else
        echo "FAIL: $scenario"
        return 1
    fi
}

function when {
    local condition="$1"
    shift
    "$@"
}

function given {
    local pre_condition="$1"
    shift
    "$@"
}

function scenario {
    local scenario="$1"
    shift
    "$@"
}

When you write:

scenario "Some test case" \
    given "Something always true" \
    when "Something you want to test" \
    then_expect "Some outcome"

Then it executes:

some_test "Something always true" "Something you want to test" "Some outcome"

And prints one of:

PASS Some test case
FAIL Some test case

A real example at https://github.com/binkley/shell/tree/master/testing-bash.

Sunday, February 01, 2015

Dice, Test First and saving time

tl;dr — Test first saves time. Also a dice expression calculator.

The setup

Larry Wall famously remarked, "the three great virtues of a programmer: laziness, impatience, and hubris." A skill earned by good programmers is knowing when to favor which virtue. I failed while writing a parser for "dice expressions" ala Dungeons & Dragons. (Example: "3d6 + 1" means roll 3 6-sided dice, sum the results and add 1.)

The parser is straight-forward using the popular ANTLR4 tools:

grammar DiceExpression;

expr: left=expr op=('*' | '/') right=expr #opExpr
    | left=expr op=('+' | '-') right=expr #opExpr
    | '(' group=expr ')' #groupExpr
    | van=('adv' | 'dis')? number=NUMBER? sides=SIDES #diceExpr
    | number=NUMBER #numberExpr
    ;

dice: number=NUMBER? sides=SIDES;

NUMBER: '1'..'9' '0'..'9'*;
SIDES: 'd' ('4' | '6' | '8' | '10' | '12' | '20' | '100')
    { setText(getText().substring(1)); };

WS: [ \t]+ -> skip;

The grammar supports a 5th Edition feature, Advantage/Disadvantage—where you get to roll twice and pick the better/worse result—, with the keywords adv/dis. A less-5th Edition-specific grammar would leave out that term from "#diceExpr".

Enter the rube

All tests pass: good! Then I thought, "What about whitespace in dice rolls or with adv/dis?" I was concerned with expressions such as "1 +disd6" or "3 d4". Surely those are invalid, but I don't check for them. So I began adding explicit whitespace parsing in the #diceExpr rule. Mistake!

Several of my tests failed for perfectly good dice expressions.

Okay, so I reverted my grammar changes. To understand what was going on, I added tests I should have started with before editing (lines marked "FAIL"):

@RunWith(Parameterized.class)
public final class DiceExpressionTest {
    @Parameters(name = "{index}: '{0}'")
    public static Collection<Object> parameters() {
        return asList(args("1", true),
                args("d6", true),
                args("3d6", true),
                args("1+3d6", true),
                args("adv d6", true),
                args("dis 3d6", true),
                args("disd6", false), // FAIL
                args("1 + 3 d6", false)); // FAIL
    }

    private final ExpectedException thrown = ExpectedException.none();

    @Parameter(0)
    public String in;
    @Parameter(1)
    public boolean ok;

    @Test
    public void shouldParse() {
        if (!ok)
            thrown.expect(ParseCancellationException.class);

        // Just check parsing works, ignore results
        DiceExpression.valueOf(in).evalWith(Roll.die());
    }

    private static Object[] args(final Object... args) {
        return args;
    }
}

And ... the "FAIL" tests pass with the original grammar. This got me to thinking more about how ANTLR works. It is a lexer/parser. The lexer grabs strings from the input and hands them to the parser to figure out. The lexer has no brains, it's an expert at chopping up input. (Great post on this.)

The cases I worried about? The lexer does not know how to chop up the first one ("disd6"): it matches no token pattern. The parser does not understand the tokens in the second one ("1 + 3 d6"): it expects an arithmetic operator between "3" and "d6". My grammar already did the right thing.

Had I started with tests for these cases, instead of jumping right to coding, I would have not spent time messing with whitespace in the grammar. Coding first cost time rather than saved it.

Enlightenment by coding Kōan

Write tests first! No, really. Even when you "know" the solution, write tests first. There are many ways it saves time in the long run, it helps you think about your API, and it can save time in the short run. My dice expression example is not unique: test-first avoids unneeded work.

Epilogue

What does the dice expression implementation look like?

public final class DiceExpression {
    private final ExprContext expression;

    @Nonnull
    public static DiceExpression valueOf(@Nonnull final String expression) {
        final DiceExpressionLexer lexer = new DiceExpressionLexer(
                new ANTLRInputStream(expression));
        final DiceExpressionParser parser = new DiceExpressionParser(
                new CommonTokenStream(lexer));
        parser.setErrorHandler(new BailErrorStrategy());

        return new DiceExpression(parser.expr());
    }

    private DiceExpression(final ExprContext expression) {
        this.expression = expression;
    }

    public int evalWith(final Roll roll) {
        return expression.accept(new EvalWithVisitor(roll));
    }

    private static final class EvalWithVisitor
            extends DiceExpressionBaseVisitor<Integer> {
        private final Roll roll;

        public EvalWithVisitor(final Roll roll) {
            this.roll = roll;
        }

        @Override
        public Integer visitOpExpr(final OpExprContext ctx) {
            final int left = visit(ctx.left);
            final int right = visit(ctx.right);
            switch (ctx.op.getText().charAt(0)) {
            case '+': return left + right;
            case '-': return left - right;
            case '*': return left * right;
            case '/': return left / right;
            }
            throw new ArithmeticException(
                    "Unknown operator: " + ctx.getText());
        }

        @Override
        public Integer visitGroupExpr(final GroupExprContext ctx) {
            return visit(ctx.group);
        }

        @Override
        public Integer visitDiceExpr(final DiceExprContext ctx) {
            return Dice.valueOf(ctx).evalWith(roll);
        }

        @Override
        public Integer visitNumberExpr(final NumberExprContext ctx) {
            return Integer.valueOf(ctx.number.getText());
        }
    }
}

And Dice, overly complicated until I can think through Advantage/Disadvantage further:

public class Dice
        implements Comparable<Dice> {
    @FunctionalInterface
    public interface Roll {
        int roll(final int sides);

        static Roll die() {
            final Random random = new Random();
            return sides -> random.nextInt(sides) + 1;
        }
    }

    public enum Advantage {
        DIS(2, Math::min),
        NONE(1, (a, b) -> a),
        ADV(2, Math::max);

        private final int rolls;
        private final IntBinaryOperator op;

        Advantage(final int rolls, final IntBinaryOperator op) {
            this.rolls = rolls;
            this.op = op;
        }

        public final int evalWith(final Roll roll, final Dice dice) {
            return range(0, rolls).
                    map(n -> dice.rollWith(roll)).
                    reduce(op).
                    getAsInt();
        }

        public final String toString(final Dice dice) {
            switch (this) {
            case NONE:
                return dice.stringOf();
            default:
                return name().toLowerCase() + ' ' + dice.stringOf();
            }
        }
    }

    private final Optional<Integer> number;
    private final int sides;
    private final Advantage advantage;

    @Nonnull
    public static Dice valueOf(@Nonnull final String expression) {
        final DiceExpressionLexer lexer = new DiceExpressionLexer(
                new ANTLRInputStream(expression));
        final DiceExpressionParser parser = new DiceExpressionParser(
                new CommonTokenStream(lexer));
        parser.setErrorHandler(new BailErrorStrategy());

        return valueOf((DiceExprContext) parser.expr());
    }

    @Nonnull
    static Dice valueOf(@Nonnull final DiceExprContext ctx) {
        final Optional<Integer> number = Optional.ofNullable(ctx.number).
                map(Token::getText).
                map(Integer::valueOf);
        final Integer sides = Integer.valueOf(ctx.sides.getText());
        final Advantage advantage = Optional.ofNullable(ctx.van).
                map(Token::getText).
                map(String::toUpperCase).
                map(Advantage::valueOf).
                orElse(NONE);
        return new Dice(number, sides, advantage);
    }

    public Dice(@Nonnull final Optional<Integer> number, final int sides,
            final Advantage advantage) {
        this.number = number;
        this.sides = sides;
        this.advantage = advantage;
    }

    public Dice(@Nullable final Integer number, final int sides,
            final Advantage advantage) {
        this(Optional.ofNullable(number), sides, advantage);
    }

    public Dice(@Nullable final Integer number, final int sides) {
        this(Optional.ofNullable(number), sides, NONE);
    }

    public int number() {
        return number.orElse(1);
    }

    public int sides() {
        return sides;
    }

    public Advantage advantage() {
        return advantage;
    }

    public int evalWith(final Roll roll) {
        return advantage.evalWith(roll, this);
    }

    protected int rollWith(final Roll roll) {
        return rangeClosed(1, number()).
                map(ignored -> roll.roll(sides())).
                sum();
    }

    @Override
    public int compareTo(@Nonnull final Dice that) {
        // Sort by roll average - adv/dis messes this up though
        final int compare = Integer.compare(number() + (sides() + 1),
                that.number() * (that.sides() + 1));
        return 0 == compare ? advantage().compareTo(that.advantage())
                : compare;
    }

    @Override
    public String toString() {
        return advantage.toString(this);
    }

    protected String stringOf() {
        return number.
                map(number -> number + "d" + sides()).
                orElse("d" + sides());
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        final Dice that = (Dice) o;

        return sides() == that.sides() && number() == that.number()
                && advantage() == that.advantage();
    }

    @Override
    public int hashCode() {
        return 31 * (31 * sides() + number()) * advantage().hashCode();
    }
}
UPDATE: If you wonder about the parser visitor, you have to explicitly enable it in ANTLR4 (even though it is recommended over tree rewriting). Use the command line flag, or in Maven

<plugin>
    <groupId>org.antlr</groupId>
    <artifactId>antlr4-maven-plugin</artifactId>
    <version>${antlr4.version}</version>
    <configuration>
        <visitor>true</visitor>
    </configuration>
    <executions>
        <execution>
            <id>antlr4</id>
            <goals>
                <goal>antlr4</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Saturday, December 07, 2013

Tuesday, June 19, 2012

Time lies when you're calling functions

Noah Sussman notes Falsehoods programmers believe about time. My favorite:

  1. You can't be serious.

Nearly as good:

That thing about a minute being longer than an hour was a joke, right?

No.

Generating random Java strings

I found lots of misfitting advice searching Google for how to generate random strings in Java. The misfits assumed I was generating unique keys, often for web page cookies.

What I searched for was random Java strings along the lines of new Random().nextInt(), to use for probabilistic testing, but I failed to find this so I rolled my own.

Not a thing of beauty, but accomplished by goal. Hopefully someone might find this useful:

String randomString(final Random random,
        final int minLength, final int maxLength) {
    final int length = random.nextInt(maxLength - minLength) + minLength;
    final char[] chars = new char[length];
    for (int i = 0, x = chars.length; i < x; )
        do {
            final int cp = random.nextInt(0x10FFFF + 1);
            if (!Character.isDefined(cp))
                continue;
            final char[] chs = Character.toChars(cp);
            if (chs.length > x - i)
                continue;
            for (final char ch : chs)
                chars[i++] = ch;
            break;
        } while (true);

    return new String(chars);
}

To examine what you get back, consider Character.UnicodeBlock.of(int codePoint).

Friday, March 02, 2012

Showing test failure name for JUnit parameterized tests

A popular complaint against JUnit is lack of human-readable names for parameterized test cases. There are several solutions, this is mine.

/**
 * {@code ParameterizedTest} simplifies parametered JUnit tests by including a "case label" to
 * identify the failing branch.  (JUnit only provides an index).  The failure message format
 * becomes "descriptive string from JUnit: case label: assertion failure message".
 * <p/>
 * Fully preserves the stack trace of failing tests.
 * <p/>
 * Example: <pre>
 * public class MyTest {
 *     &#64;Parameters
 *     public static Collection<Object[]> parameters() {
 *         return ...; // See JUnit documentation for parameterized tests
 *     }
 *
 *     public MyTest(final String caseLabel, final X x, final Y y) {
 *         super(caseLabel);
 *         this.x = x;
 *         this.y = y;
 *     }
 *
 *     &#64;Test
 *     public void shouldWork() {
 *         // Body of test method ...
 *     }
 * }
 * </pre>
 * When {@code shouldWork()} fails the failure message is more meaningful, including the
 * {@link #caseLabel "case label"} in the JUnit error message.
 *
 * @author <a href="mailto:binkley@alumni.rice.edu">B. K. Oxley (binkley)</a>
 */
@RunWith(Parameterized.class)
public abstract class ParameterizedTest {
    private final String caseLabel;

    /**
     * The JUnit test rule (test extension) which fails bad parameterized cases but provides a
     * {@link #caseLabel case label} identifing the failed test branch.
     */
    @Rule
    public final TestRule parameterizedTestRule = new TestWatcher() {
        @Override
        protected void failed(final Throwable e, final Description description) {
            final AssertionError x = new AssertionError(
                    description.getDisplayName() + ": " + caseLabel + ": " + e.getMessage());
            x.setStackTrace(e.getStackTrace());
            throw x;
        }
    };

    /**
     * Constructs a new {@code ParameterizedTest} for the given <var>caseLabel</var>.
     *
     * @param caseLabel the case label, never missing
     */
    protected ParameterizedTest(@Nonnull final String caseLabel) {
        this.caseLabel = caseLabel;
    }
}

Monday, July 25, 2011

JMockit and static methods

Static methods are painful in Java when mocking but JMockit makes some impossible testing possible, though not easy:

public final class MockitEg {
    public static final int SHOE_SIZE = 13;

    public static int shoeSize() {
        System.out.println("MockitEg.shoeSize");
        return SHOE_SIZE;
    }

    private MockitEg() {
    }
}

public class MockitTest {
    private static final int MOCK_SHOE_SIZE = SHOE_SIZE + 29;

    @Test
    public void shouldMockStaticMethod() {
        new NonStrictExpectations() {
            final MockitEg mock = null;

            {
                MockitEg.shoeSize();
                result = new MockitEgDelegate();
            }
        };

        assertThat(MockitEg.shoeSize(),
                is(equalTo(MOCK_SHOE_SIZE)));
    }

    private static final class MockitEgDelegate
            implements Delegate {
        public static int shoeSize() {
            return MOCK_SHOE_SIZE;
        }
    }
}

The test passes.

Wednesday, September 29, 2010

Hamlet D'arcy on Mockito

A beautiful post from Hamlet D'arcy on Mockito - Pros, Cons, and Best Practices. My team at work uses EasyMock. I want to use Mockito for my next project; this post makes it an easier sell.

Wednesday, January 06, 2010

Those lucky web developers

Those lucky web developers have it so good. Automated functional testing for enterprise integration middleware is a complete pain. You try simulating a dozen distinct systems. Helps keep me warm at night just thinking about it.

Wednesday, August 26, 2009

Comments on Hipp's A Lesson In Low-Defect Software

Thanks to Alecco Locco for useful comments on Hipp's A Lesson In Low-Defect Software. the full title:

A Lesson In Low-Defect Software
-or-
A Journey From A Quick Hack
To A High-Reliability Database Engine

and how you can use the same techniques to reduce
the number of bugs in your own software projects

Those comments got me to read the paper.