It was obvious that we need some kind of Word.add_content method. So we started with tests for that method. The final result of those tests is as following:
def assert_count_after_add(count, content)
Word.add_content(content)
assert_equal count, Word.count
end
def test_add_content
assert_count_after_add 2, ""
assert_count_after_add 3, "single"
assert_count_after_add 5, "two words"
assert_count_after_add 9, "four wordzz are funny"
assert_count_after_add 10, "duplicate duplicate"
end
As you can see there is a custom assertion. We also test that if there is already a word in the database we don't want it to be added again.
The implementation for the add_content:
def self.add_content content
if not content.empty?
content.split(" ").each {|eng_word|
if Word.find(:first,
:conditions => "eng = '#{eng_word}'") == nil
Word.new(:eng=>eng_word).save
end
}
end
end
It's not the prettiest piece of code but it should be fine for now.
Oops, it looks like we don't have a test for the fact that words are saved with their english translation only...
After implementing the add_content method we realized that now our database could be filled with words that are not translated (pl field is empty). It means we want to change the implementation of the 'Word.random' method so that it only displays words that are already translated. There is no point in displaying the english version only...
It sounds like we need word.translated? method. Let's write some tests:
def test_translated
assert !Word.new.translated?
assert (Word.new :pl=>"tak", :eng=>"yes").translated?
assert !(Word.new :pl=>"tak").translated?
assert !(Word.new :eng=>"yes").translated?
end
The implementation is simple:
def translated?
pl and eng
end
Now, it's time for adding a test for the fact that Word.random only returns translated words. Again, we call the test several times so that we can assume it works. We can probably refactor it later to some nicer way. We add a word to the database and then assert that it wasn't chosen even after 100 calls. Any ideas how to test it better?
def test_use_words_with_translation
Word.new( :pl=>'tak', :eng=>'yes').save
Word.add_content("hello")
randoms = []
100.times {randoms << Word.random.eng}
assert (randoms.include? "hello") == false
end
The new implementation looks like that:
def self.random
(Word.find(:all).select {|word|word.translated?}.sort_by {rand})[0]
end
Unfortunately, there was no time to create a user interface for adding a content. Sounds, like a nice topic for the next session.
3 comments:
nice second part - hope that there will be a third (and more).
just two things..
with the fixture from part one still in place, the new test_add_content didn't work. I had to add a +2 to get it green.
in the first part you used "eng" as database column and in the second part "en" - would be nice if both (and future parts) would use the same names (I changed all to "eng").
so long,
Hendrik
Hi hendrik,
Well spotted! Thanks!
Fixed both problems.
Using fixtures causes problems like that. I'm moving more into the direction of mock objects now. Take a look my new article from the "Words application series":
http://andrzejonsoftware.blogspot.com/2007/06/testing-rails-controllers-with-mock.html
Thanks! Overall very helpful and I had to learn a lot about the right way to use Rails migrations.
BTW, the first part of the tutorial about the tests should fail... does not seem to hold true with Rails 2 on my Mac. They run without issuing any message. Then once I add a model object... when I rake the tests I get told to run a migration first.
That's when my migrations issues started.
However, it is nice to work through all this in TDD with that goal. I look forward to the next post.
Post a Comment