If you aren't entirely convinced about the importance of using Unit tests to test your Models after trying the steps found in the Importance of Unit Tests post, try these changes that continue on that example.
Again, Create an application folder
>rails testing
And again, Create a model, note* specify words to be a text string
testing>ruby script/generate model Display words:string
However, this time do NOT Edit the model file. But instead, Edit the migration file, db\migrate\###_create_displays.rb
def self.up
create_table :displays do |t|
t.column :words, :string, :default => 'no text entered', :null => false
t.timestamps
end
end
And then,
Create the table
testing>rake db:migrate
Populate the table with an entry
testing>ruby script/console
>>Display.create(:words => 'test text string')
Create a controller
testing>ruby script/generate controller Displays index
Edit the controller file, \app\controllers\displays_controller.rb
def index
@display_text = Display.find(:all)
respond_to do |format|
format.html # index.html.erb
end
end
Edit the view file, \app\views\displays\index.html.erb
<h1>Displays#index</h1>
<% @display_text.each do |d| %>
<p>
<%= d.words %>
</p>
<% end %>
Verify that everything is working so far.
testing>ruby script/server
Open your browser to http://localhost:3000/displays and you should see
Edit the unit test file, \test\unit\display_test.rb It should accept a text string entry, should have some text, and not be a numeric entry.
def test_should_create_entry
entry1 = Display.create(:words => 'more test text')
assert entry1.valid?
end
def test_words_should_not_be_nil
entry2 = Display.create(:words => nil)
assert entry2.errors.on(:words)
end
def test_words_should_not_be_empty
entry3 = Display.create(:words => '')
assert entry3.errors.on(:words)
end
def test_words_should_not_be_numeric
entry4 = Display.create(:words => 123)
assert entry4.errors.on(:words)
end
Edit the functional test file, \test\functional\displays_controller_test.rb
def test_should_show_index
get :index
assert_response :success
assert_template 'index'
assert_not_nil assigns(:display_text)
end
# :count is from fixtures file
# test\fixtures\displays.yml
def test_should_show_entries
get :index
assert_select 'p', :count => 2
end
Verify that the tests pass
testing>rake test:units
This time you should see*
1) ERROR:
test_words_should_not_be_nil(DisplayTest)
......
2) Failure:
test_words_should_not_be_empty(DisplayTest)
......
<nil> is not true.
3) Failure:
test_words_should_not_be_numeric(DisplayTest)
......
<nil> is not true.
4 tests, 3 assertions, 2 failures, 1 errors
*On my console the test_words_should_not_be_nil error shows many lines of the trace. Of particular interest is the line
./test/unit/display_test.rb:15:in `test_words_should_not_be_nil'Again, although words was specified as a string, the test_words_should_not_be_numeric test fails.
And without the line
validates_presence_of :wordsin the model file, the test_words_should_not_be_empty test also fails.
Importantly, the test_words_should_not_be_nil test does not fail, but this time errors. By looking at the log\test.log file we can find
[0;1mSQLite3::SQLException: displays.words may not be NULL: INSERT INTO "displays" ("updated_at", "words", "created_at") VALUES('YYYY-MM-DD hh:mm:ss', NULL, 'YYYY-MM-DD hh:mm:ss')Try
testing>ruby script/console
>> Display.create(:words => )
SyntaxError: compile error
(irb):1: syntax error, unexpected ')'
from (irb):1
>> Display.create(:words => nil)
=> #<Display id: 2, words: nil, created_at .......>
>> Display.create(:words => '')
=> #<Display id: 3, words: "", created_at: .......>
What happened to the default value we specified in the migration file?
testing>ruby script/server
Open your browser to http://localhost:3000/displays and look at the view-source, you should see
Summary:
Without adequate data control in the model file, even though the database field was specified as a string, not NULL, and had a default value, it still accepted a numeric value and errored with a nil value. The default value was unused.
Without adequate unit testing, these bugs may have gone undetected until a later stage in the application's development.






