Importance of good Models

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

Displays#index
test text string

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 :words

in 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

<h1>Displays#index</h1>

<p>
test text string
</p>

<p>

</p>

<p>

</p>

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.

Technorati Tags: , , ,

Importance of Unit Tests

If you are new to writing Rails applications, as I am, you may wonder about the importance of writing tests, especially if everything works as expected in the browser. If so, try this very simple example that demonstrates the importance of testing the model with unit tests.

Create an application folder

>rails testing

Create a model, note* specify words to be a text string

testing>ruby script/generate model Display words:string

Edit the model file, \app\models\display.rb, to ensure that words exists

validates_presence_of :words

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

Displays#index
test text string

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

You should see

1) Failure:
test_words_should_not_be_numeric(DisplayTest)
......
<nil> is not true.
4 tests, 4 assertions, 1 failures, 0 errors
......
2 tests, 5 assertions, 0 failures, 0 errors

Populate the table with a numeric entry

testing>ruby script/console
>>Display.create(:words => 456)
=> #<Display id: 2, words: 456, created_at: ......>

So what's going on? When the Display model was generated words was specified as a string, and the db\migrate\###_create_displays.rb file has this in it.

def self.up
create_table :displays do |t|
t.string :words

t.timestamps
end
end

check in the browser again.

testing>ruby script/server

Open your browser to http://localhost:3000/displays
There they are

Displays#index
test text string

456

Edit the model file again, \app\models\display.rb, using a simple regular expression to check for the presence of at least one text character.

validates_format_of :words,
:with => /[a-z]+/i

Run the tests again.

testing>rake test

This experience with this very simple application has convinced me of the importance of writing tests. I hope it has convinced you too.

WordPress Functions 2.6

WordPress has changed quite a bit since version 2.3, including a net increase of 12 classes and
577 functions.

Again, I used a script that searches WordPress files using regular expressions to find Classes and Functions.
64 PHP Classes and 2,116 PHP Functions were found.

The PHP Classes and Functions of WordPress 2.6

* Note *
The list does not include any found in plugin or theme files as these will differ from blog to blog. Nor does it differentiate between "stand alone" functions and functions that are members of a class, such as the Snoopy functions.

If you know of any functions that are not in the list, please leave a comment so I can modify the regex used to find them.

Technorati Tags: ,

John McCain's ads are LIES. Here's the video proof.

Video proof: <a href="http://www.youtube.com/watch?v=IH0xzsogzAk">John McCain's ads are LIES.</a>