Example – delete lines containing @SuppressWarnings('DuplicateStringLiteral') string in all test files:

sed -i '' "/@SuppressWarnings('DuplicateStringLiteral')/d" test/**/*.groovy

Find text and delete every containing line with sed

Hamcrest matchers in Spock

Working with Spock we forget about a hidden gem – Hamcrest matchers. They are assertions turned into method objects. A matcher is usually parametrized with an expected value and is executed latel on the actual one.

When can we use it? For example in a table-driven specification with expected values in the table.

An expected value can be one of the following:

  • exact value
  • ‘not available’ text
  • any number

How can we express these assertions without Hamcrest?

then:
if (exactValue) {
    assert field == exactValue
} else if (notAvailable) {
    assert field == messageSource.getMessage('na', null, LocaleContextHolder.locale)
} else if (isNumber) {
    assert field.number
} else {
    assert false, 'Wrong test configuration'
}

where:
exactValue | notAvailable | isNumber
'12345'    | null         | null
null       | true         | null
null       | null         | true

Bufff…

  • there is a lot of boilerplate code
  • the specification is misleading – what does it mean that expected exactValue is null? Is it really null? Or simply this condition should not be checked?

How can we refactor this code using Hamcrest matchers?

then:
that field, verifiedBy

where:
verifiedBy << [
    org.hamcrest.CoreMatchers.equalTo('12345')
    notAvailable(),
    isNumber(),
]

...

def notAvailable() {
    org.hamcrest.CoreMatchers.equalTo messageSource.getMessage('na', null, LocaleContextHolder.locale)
}

def isNumber() {
    [
        matches: { actual -> actual.number },
        describeTo: { Description description -> 'should be a number' }
    ] as BaseMatcher
}

Result – shorter and more expressive feature method code.

You can find the full source code with mocked messageSource in this gist. For more information about Hamcrest matchers, check Luke Daley’s article.

Nesting categories in Groovy

Recently I discovered that Groovy categories can be nested. It means, you can inject a category into another one:

@Category(GroovyObject)
class FirstCategory {
    def method1() { }
}

@Category(GroovyObject)
@Mixin(FirstCategory)
class SecondCategory {
    def method2() {
        method1()
    }
}

@Mixin(SecondCategory)
class Mixee {
    def yetAnotherMethod() {
        method2()
    }
}

Before that ‘discovery’ I was injecting all categories into the final class. My code was not expressing the explicit dependency between the second and the first category:

@Mixin([FirstCategory SecondCategory)
class Mixee

Happy mixin’!

Smalltalk/Objective-C method calls in Groovy – @AsMessage AST transformation

I like method call (or message passing syntaxis) in Objective-C (inspired by Smalltalk). You can achieve more readable code that can be read like a natural language (taken from OCMock tests):

[mock stringByPaddingToLength:20 withString:@"foo" startingAtIndex:5];

In my current customer project we mock/stub some methods through metaclass or by using Grails mocks. (Spock up to 0.6 version is not able to mock neither dynamic nor static methods). To improve test code readability, I extract mock creation and configuration into a separate helper method:

def mockAddChargeOn(args, order) {
    order.metaClass.addChargeCalled = false
    order.metaClass.addCharge = { actualAmount, actualDueDate ->
        order.addChargeCalled = true
        assert actualAmount == args.expectsAmount
        assert actualDueDate == args.expectsDueDate
        args.returns
    }
}

so I can mock addCharge like this:

mockAddChargeOn order, expectsAmount: 1.0, expectsDueDate: tomorrow(), returns: aCharge

As you can see, I use named parameters extensively. Their drawback is lack of documentation through method signature. You have to read through the method body or rely on your IDE. IntelliJ IDEA is smart enough to figure out which named parameters a method supports and shows them in code completion. But not everybody uses this IDE.

I came up with a solution based on an AST transformation. If you annotate a method with @AsMessage, the transformation adds a new method with named parameters calling the original method:

@AsMessage
def mockAddChargeOn(order, expectsAmount, expectsDueDate, returns)

// Added by the AST transformation
def mockAddChargeOn(args, order) {
    mockAddChargeOn order, args.expectsAmount, args.expectsDueDate, args.returns
}

Your methods have clear signatures and you can call them using a more fluent syntax.

You find the source code and full documentation on GitHub:

https://github.com/mgryszko/message-transformation

If you are brave enough to test it out, spot a bug or want a new feature, use GitHub issues. Or you catch me on Twitter (@mgryszko).

Final note

I’m not going to apply this transformation on public API. My ‘target group’ is initially test code. The transformation should not substitute short method signatures with few parameters. Keep in mind than long signatures make method difficult to understand (Steve McConnell in Code Complete recommends using less than seven parameters; Robert C. Martin in Clean Code reduces this number to three).

Script for downloading all owned tracks in Wikiloc

Disclaimer

Jordi Ramot, Wikiloc’s creator, asked me to remove the script from GitHub, argumenting that it could add an additional load to already over stressed servers. I agreed, since I’m a avid Wikiloc user and I’d like to have this awesome and free service up and running.

Original post

You have tens or hundreds of track in Wikiloc. You didn’t store them locally on your computer. Now you want to download them. Going through the list of tracks in Wikiloc and downloading them individually is tiresome.

On the other hand, I wanted to practice my Ruby skills. The solution is a small script written in Ruby. You can download it from GitHub: https://github.com/mgryszko/wikiloc-downloader

It uses Mechanize and Nokogiri for the scraping part (you have to install the Mechanize gem (gem install mechanize).

The usage is simple:

Usage: download_all_tracks [options]
    -u USER                          email or user name
    -p PASSWORD

I hope this functionality will be implemented directly in Wikiloc – your data is yours, so you should be able to download it.

Follow

Get every new post delivered to your Inbox.

Join 373 other followers