General
Gems
- Seeing Is Believing
- Surrogate
- Mountain Berry Fields
- Deject
- Pbcopy
- LetterPress Is Not As Good As Boggle
- Haiti
Web Based
Surrogate
Handrolled Mocking Framework
When testing, we often want to mock out some object. Perhaps it is expensive to load that code, or not yet written, or produces unreliable results, or interacts with external systems. Whatever the reason, we deal with this by using a mock object: an object that looks like the real one, but that allows us to inject return values, and query it after the fact to see how it was used.
There are many solutions for this already, but they primarliy are done with dynamic mocking, such as rspec-mocks. My issue with this approach is that we must define the mock object everywhere we wish to use it, allowing mistakes across definitions, and diffusing the interface across all the tests; dynamic mocking does not (in any of the cases I know of) enforce arity; and does not ensure that the methods we define match up with the methods on the real object, thus the mock can (and does) diverge from the real object, without tests failing.
Surrogate deals with this by giving you a way to declare what the interface is, in a real object, which will then generally behave as expected, but also giving you a standard way to override return values, query about what happened after the fact, and ensure congruity between the mock and real object. In fact, it does this in such a way that you can assert congruity between the mock class and any other class, allowing the Surrogate to be used as an interface that real objects can implement, and test their adherence to.
require 'surrogate/rspec' class MockClient Surrogate.endow self define(:request) { 'default value' } end describe 'using the mock' do let(:client) { MockClient.new } it 'has defaults' do client.request.should == 'default value' end it 'can be overridden' do client.will_have_request 'injected value' client.request.should == 'injected value' end it 'can be asserted against' do client.was_not told_to :request client.request client.was told_to :request end end
since we use the MockClient in our tests, we need to make sure that it looks like the real client that we will use in production.
require 'surrogate/rspec' class MockClient Surrogate.endow self define(:request) { 'default value' } end class RealClient def request end end describe MockClient do it 'substitutes for the mock client' do RealClient.should substitute_for MockClient end end
This would fail if the names differed, or if the arguments differed, or if there were methods that did not match across the two (there are options you can pass to substitute_for
that will tell it what to care about)
Surrogate has a lot more functionality than this, it supports class-level methods, checks argument types, argument names, has support for predicates and verbs, factories for initializing with defaults (allowing for easy switching between rspec-mocks and Surrogate), it supports all of rspec-mocks argument matchers.
Surrogate is for handrolling mock objects.