Builder Methods
Code: domain-driven design, immutability, Ruby, testing, validity
Comments Off on Builder Methods
I was reading Corey Haines’ book Understanding the Four Rules of Simple Design (capsule review on the 2014 book reviews post) when I read:
In fact, over time I’ve developed a guideline for myself that external callers can’t actually use the base constructor for an object. Put another way: the outside world can’t use
new
to instantiate an object with an expectation of a specific state. Instead, there must be an explicitly named builder method on the class to create an object in a specific, valid state.
This was a brief aside at the tail of a longer discussion of interdependent and redundant tests. It really caught my attention and I’d like to hear it more thoroughly investigated. In the hopes of attracting Corey’s attention, I offer this cat pic and an exploration of the benefits of a similar practice I have:
From Fixtures to Factories
Code: factories, factory_girl, fixtures, Mocha, Rails, Ruby, testing
Comments Off on From Fixtures to Factories
Automated tests need example data, and it’s a pain to have to construct a complete object in every test, especially when there are a lot of non-optional fields.
Why I Write Tests
Code: bugs, testing, tests
Comments Off on Why I Write Tests
I’ve had a few folks ask me if I really write tests for all my projects, like I mentioned in the last line of my second email in You’re Not Refactoring. Really?
You’re Not Refactoring
Code: Extreme Programming, refactoring, TDD, testing
Comments Off on You’re Not Refactoring
Changing code is a great way to break it, especially in really subtle ways that you won’t pick up on for weeks or months. The maintainer of Unangband, Andrew Doull, wrote that Refactoring Is Hell. I sent him a note in response to that blog post that I think I may just as well have blogged. So:
Now Do You Know It Works?
Code: reliability, testing, verification
Comments Off on Now Do You Know It Works?
You’re writing code to store a file on Amazon S3. It’s a popular, powerful, widely-used and highly-reliable service, and you know the Amazon S3 API pretty well. So you write a function that takes a file and a key name (filename), then calls the HTTP PUT to store the data. Do you know it works?
Painless Upgrade to Rails 2.0
Code: ActiveResource, NearbyGamers, Rails, Rails 2.0, routing, Ruby, TDD, test, testing, tests, upgrade, web
Comments Off on Painless Upgrade to Rails 2.0
I spent a dead-easy 2.5 hours last night updating NearbyGamers to Rails 2.0. My svn commit message read (with links added here for convenience):
Simple Ruby Mocking
Code: dependency injection, mocking, mocks, Ruby, testing, tests, unit tests
Comments Off on Simple Ruby Mocking
I mentioned at the end of my last post on testing that I wrote some code to do mocking for my unit tests in Ruby. Writing a small mock library was very much reinventing the wheel, but I needed to do it to earn a deeper understanding of mocks.
I’m writing some code (“Fetcher”) to talk to a POP3 server, fetch mail, and pass it off to another process. One of the tests deals with what happens when the POP3 server is down or otherwise unreachable.
def test_setup_server_down
pop3 = Mock.new
pop3.expect(:new, [MAIL_SERVER, MAIL_POP3_PORT]){ raise Timeout::Error.new("execution expired") }
f = Fetcher.new(pop3)
# further assertions that f acts correctly
end
This says that the Mock object expects a call to the new method with the given arguments, and when the call happens it runs the block. The block could return anything, but in this case it raises the same error as Net::Pop3 does when it can’t contact the server. After that the test can go on to make whatever assertions it needs to verify that the exception was handled properly.
The Mock object has a list of calls it expects to see and keeps a list of how it’s been called (yes, this could just be one list with an index but I thought it was mentally simpler this way). The test sets up what calls it should expect to see with what arguments (or blank for any) and block to run (or blank for none). When a method is called on the mock object, method_missing logs the call and executes the given block (raising a fuss if the call didn’t match what it expected).
class Mock
attr_reader :calls, :called
# the stub arg makes it just record all calls
def initialize
@calls = []
@called = []
end
# Pass nil for args to ignore the actual args in the call.
# Proc is optional; default is empty proc returning nil.
def expect(method, *args, &proc)
@calls << {:method => method, :args => args.first, :proc => (proc or Proc.new{})}
end
def method_missing(method, *args)
@called << {:method => method, :args => args}
expect = @calls.shift
raise "Unexpected mock call #{method.to_s}(#{args.join(', ')})" if expect.nil?
raise "Wrong mock call #{method.to_s}(#{args.join(', ')}); expected #{expect[:method]}(#{expect[:args].join(', ')})" if method != expect[:method] or (expect[:args] != nil and args != expect[:args])
expect[:proc].call(*args)
end
end
It’s a straightforward little object, and I also added some code to raise a fuss if expected calls weren’t made. This does have the downside that any tests defining their own teardown need to call super.
class Mock
def fail_if_not_empty
# Empty the call stack so that this obj doesn't throw errors for
# every later test between now and this object getting gc'd
calls, @calls = @calls, []
raise "Mock calls uncalled: n" + calls.collect { |call| "#{call[:method]}(#{call[:args]} { #{call[:proc] })" }.join(" ") unless calls.empty?
end
end
class Test::Unit::TestCase
def teardown
finish_mocks
end
def finish_mocks
ObjectSpace.each_object(Mock) do |m|
m.fail_if_not_empty
end
end
end
This has been a handy piece of code to test the code I’ve written in the last two weeks, but it’s not good enough. I have to use a technique called dependency injection to test Fetcher.new, where the outside code passes it a POP3 object instead of its initialize just using Net::POP3. Useful for testing, but my code is badly repetitive when all the instantiation calls have to do this exact same setup. (As an aside, Jacob Proffitt recently started an interesting conversation on dependency injection, took criticism, and responded. Good reading.)
I was pondering how to extend the Mock object to let me mock class methods (eg a call to Net::POP3.new) when I realized I’d gone as far as I should down the do-it-yourself road. I’d heard of Mocha and it took me all of ten minutes to think to look at its cheat sheet where that’s the first example.
After spending two hours or so writing and tweaking this code, the best thing for it is to be thrown away. I’ve learned about mocking by doing it and I’m better-prepared to understand someone else’s larger and better library.
Fixtures in Ruby Unit Tests
Code: fixtures, Ruby, testing, tests, unit tests
Comments Off on Fixtures in Ruby Unit Tests
I’m writing some Ruby scripts that sort and store lots of small files. After a day or two of hacking I had the basic code working, ran through a few thousand files, and a malformed file blew up the sorter. That was OK, the sorter was intentionally naive and lacking in error handling; I wanted it to hack it together and try out a few approaches before committing to serious development.
Now it’s time to treat it as real code, which means getting it under test. Tests are especially useful for ensuring tricky edge cases like malformed input don’t quietly break during other changes. When I started writing unit tests for the sorter I wanted fixtures similar to the Rails fixtures so I could write:
require File.dirname(__FILE__) + '/../test_helper'
require 'message'
class MessageTest < Test::Unit::TestCase fixtures :message def test_initialization f = Message.new message(:good) # assert the object was parsed correctly, etc. end end
The basic usage is fixtures :type to load the data and create the named function (in this case, message) for fetching data. First, I wrote some YAML fixtures for my data and put them in test/fixtures/message.yaml. You can see the :good key in use here:
good: | [the body of a good message here] missing_header: | [more text] bad_checksum: | [more text]
Because I'm parsing textfiles my fixtures look pretty simple, but the YAML library for Ruby makes serialization of any very easy.
So here's the code I put in test/test_helper.rb to set this up. I really like the [list].flatten.each idiom so the call from the unit test class can cleanly pass one or more types, eg: fixtures :message and fixtures :message, :user.
class Test::Unit::TestCase
@@fixtures = {}
def self.fixtures list
[list].flatten.each do |fixture|
self.class_eval do
# add a method name for this fixture type
define_method(fixture) do |item|
# load and cache the YAML
@@fixtures[fixture] ||= YAML::load_file("test/fixtures/#{fixture.to_s}.yaml")
@@fixtures[fixture][item.to_s]
end
end
end
end
end
(Note to Rails coders: I deliberately left out the pluralization, I like having the File class in file.rb with the fixtures in text/fixtures/file.rb and the database table named file and so on. I don't mind Rails' convention of pluralizing to "files" in the latter two examples, but I'd rather have a single identifier.)
Last night at the Rudolph Hering Society meeting Ian Bicking and Atul Varma explained mocking to me. I wrote a (very) little Ruby this morning to do mocking and I'll post that in a day or two when I'm sure I didn't do it too badly.
Adequacy is Inadequate
Code: certification, motivation, testing
Comments Off on Adequacy is Inadequate
This post started as a comment on Reg Braitwaithe’s post Certification? Bring it on! and metastasized into a post of its own.
It sounds like you’ve given up on software development. You’re drawing a line in the sand at the outside edge of a program to say, “Here, this far and no further, here is where I can objectively judge software. This is my one firm place from which I’ll move the world.”