Adam Milligan's blog
As I wrote here we've used OCHamcrest matchers for some time for writing expectations in Cedar, but have found them unsatisfying. We wanted convenient matchers, like the ones Jasmine provides for JavaScript, but for Objective C. To that end, we added Cedar-specific expectation functions and matchers that specifically solve the problems we had with OCHamcrest.
To use Cedar's expectations you need to make a couple small changes to your spec files:
1) Cedar matchers use C++ templates. Tell the compiler to expect some C++ code by changing the file extension on your spec files from .m to .mm.
2) Cedar matchers live in a C++ namespace. At the top of your spec file, after the includes, add this line:
using namespace Cedar::Matchers;
At Pivotal we write a lot of tests, or specs if you prefer. We TDD nearly everything we write, in every language we write in and on every platform we write for, so we actively work to improve every aspect of our testing tools. Personally, as I've written tests in Objective C I've found that the syntax of expectations has left much to be desired.
Like many people who write Objective C, I've spent a fair bit of time with languages like Ruby and JavaScript. When I write specs I often yearn for the simplicity of the expectation syntax in those languages. For example, some simple expectations in JavaScript, using Jasmine:
expect(composer.name).toEqual("Ludwig");
expect(composer.symphonies.count).toEqual(9);
expect(composer.symphonies).toContain("Eroica");
expect(composer.symphonies).not.toContain("Appassionata");
In comparison, the same expectations in Objective C, using OCHamcrest:
assertThat(composer.name, equalTo(@"Ludwig"));
assertThatUnsignedInt([composer.symphonies count], equalToUnsignedInt(9u));
assertThat(composer.symphonies, hasItem(@"Eroica"));
assertThat(composer.symphonies, isNot(hasItem(@"Appassionata"));
I've been a C++ developer ever since I discovered the language in the early 90's and I realized that my beloved Pascal had nothing on objects. I've spent plenty of time working with other languages, of course, and over the past year or so I've written almost exclusively Ruby. But, one thing I've missed about C++ is the ease with which you can make objects act like functions.
In case your C++ is a little rusty, here's an example:
class Fibonacci {
public:
Fibonacci(): n1_(1), n2_(1) {}
int operator()() {
int result = n1_;
n1_ = n2_;
n2_ = result + n2_;
return result;
}
private:
int n1_, n2_;
};
What you're looking at there is an overload of the function call operator. No, I'm not kidding; that will compile and run. Instances of the Fibonacci class are called function objects, or functors.
Now, you're wondering to yourself why anyone would care. The answer is, this function can now carry state around with it:
Fibonacci fibonacci;
fibonacci(); // 1
fibonacci(); // 1
fibonacci(); // 2
Fibonacci()(); // 1
vector<int> v(5);
generate(v.begin(), v.end(), Fibonnaci()); // [1, 1, 2, 3, 5]
generate(v.begin(), v.end(), fibonnaci); // [3, 5, 8, 13, 21]
This may not seem particularly compelling for generating Fibonnaci numbers, but consider generators that may carry more complex state, or references to state owned by other objects. Consumers can also set initial state, such as a seed value for a random number generator, via the ctor.
Also, consider the generate method above. It expects a third parameter that supports function call semantics with arity of zero. And nothing else. That parameter could be a function pointer (should you desire statelessness and impenetrable syntax), a functor of any type, or anything else that supports operator (). That's duck-typing, my friends. In a statically-typed language. Dogs and cats sleeping together, and all that.
Again, you cry, why would anyone care? Well, blocks in Ruby carry around the state of the context in which they were created, but sometimes you want more. For instance, if you pass your Proc object around your code may be clearer with the state explicitly encapsulated. The initial state may simply not make sense as local variables when you create the Proc. You may want to save some secondary value that a consumer can query the functor for (how many Fibonacci numbers has this generator generated). Or, perhaps you want consumers to be able to mutate the state in some way.
In any case, this functor approach wacked me over the head recently while I was looking at some code that used the Rails Symbol#to_proc. We all know that Rails adds voodoo to symbols so that
User.find(:all).collect(&:name)
is equivalent to
User.find(:all).collect { |u| u.name }
And, we all know that this works because the & operator, when applied to an object in a parameter list, will implicitly call #to_proc on that object and then convert the result to a block. This is vanilla Ruby functionality, Rails just adds #to_proc to the Symbol class.
So, duh. Check it out:
class Fibonacci
def initialize
@n1 = @n2 = 1
end
def to_proc
@proc ||= Proc.new do
result = @n1
@n1, @n2 = @n2, @n1 + @n2
result
end
end
end
fibonacci = Fibonacci.new
fibonacci.call # 1
fibonacci.call # 1
fibonacci.call # 2
Fibonacci.new.call # 1
(1..5).collect(&Fibonacci.new) # [1, 1, 2, 3, 5]
(1..5).collect(&fibonacci) # [3, 5, 8, 13, 21]
VoilĂ , a functor. I'd love to hear from anyone who has used this technique to do something really cool.
