Upload
jose-valim
View
238
Download
0
Embed Size (px)
DESCRIPTION
My presentation about Remarkable 3.0, discussing also about tests in general.
Citation preview
José Valim 2009
José Valimhttp://plataformatec.com.br
http://[email protected]
How do I test my applications?
José Valim 2009
Who am I?
• Brazilian, 22-years-old, Master of Science in Engineering, Italy;
• Started with Ruby and Rails 2 years ago;
• Gems/plugin developer (Remarkable, Inherited Resources, Rails footnotes, Simple form and counting);
• Rails core contributor (11 patches and counting).
José Valim 2009
How do I test my applications?
José Valim 2009
You should ask it everyday during
breakfast.
José Valim 2009
Or at least before each project.
José Valim 2009
You might also want to know why do you test.
José Valim 2009
Or even why you should not test.
José Valim 2009
What I already heard…
José Valim 2009
What I already heard…
José Valim 2009
A few reasons to test
• Ensure that everything works;
• Tests as documentation;
• Leads you to write better code.
José Valim 2009
A few reasons NOT to test
I wrote it, then it works (yes, I heard it!);
Trust other people code. Should I test a has_one or a validates_presence_of in my models?
• Ensure that everything works
José Valim 2009
A few reasons NOT to test
Your code should document itself;
• Tests as documentation
• Leads you to write better code
People spend more time writing tests than writing the code, which is what really matters.
José Valim 2009
Don’t judge.
José Valim 2009
Think.
José Valim 2009
How do I test?
• Waterfall? TDD? BDD?
• Test::Unit? Rspec?
• Isolation? Integration? Cucumber?
• Time? Fast?
José Valim 2009
Test::Unit? Rspec?
• Test::Unit is faster and simpler;
• Rspec code is more readable, not that fast;
• Rspec is great if you want to use your tests as documentation/specification.
José Valim 2009
Integration? Isolation?
• Integration = Rails default approach
Unit test your models and functional tests with your controllers and views.
• Isolation = Rspec default approach
Each MVC layer is tested separately with mocks and stubs. Then use Cucumber to integrate those layers.
José Valim 2009
My experience
• I finish my tasks and tests faster when working in the integration way;
• Mocks and stubs are hard to maintain (does it has to be this way?);
• Not sure if integration runs faster than isolation and vice-versa.
José Valim 2009
Simplifying tests!
José Valim 2009
Simplifying tests (in rspec)!
José Valim 2009
To use the tool, you have to understand the reasons.
José Valim 2009
Why to test Rails validations?
• You have to write the tests first;
• Validations are one liner. Easy to add, easy to remove;
• Documentation/specification;
• I do it.
José Valim 2009
How to test Rails validations?
• Test::Unit way:
class UserTest < Test::Unit
def test_user_not_valid_if_name_is_blank
user = User.create
assert user.invalid?
assert_equal :blank, user.errors[:name]
end
end
José Valim 2009
How to test Rails validations?
• Rspec way:
describe User do
it “should not be valid if name is blank” do
user = User.create
user.should_not be_valid
user.errors[:name].should == :blank
end
end
José Valim 2009
How to test Rails validations?
• Remarkable way:
describe User do it { should validate_presence_of(:name) } end
• Or:
describe User do should_validate_presence_of :name end
José Valim 2009
How to test Rails validations?
• Remarkable way:
describe User do it { should validate_presence_of(:name) } end
• Or:
describe User do should_validate_presence_of :name end
Conversion from matchers to macros happens automatically on Remarkable
José Valim 2009
Remarkable ActiveRecord 2.x
• Matchers for all validations with all options;
• Matchers for all associations, few options supported;
• Named scopes and mass assignment matchers;
• Database matchers;
• Callback, instance and class method matchers.
José Valim 2009
Remarkable ActiveRecord 3.0
• Matchers for all validations with all options;
• Matchers for all associations, almost all options supported;
• Named scopes and mass assignment matchers;
• Database matchers;
• Callback, instance and class method matchers.
José Valim 2009
Why have isolation tests?
• Design option;
• You love cucumber;
• I do it sometimes, sometimes I don’t.
José Valim 2009
How to isolate my controllers?
• Rspec way:
describe TasksController do describe “responding to #POST create” do it "exposes a newly created task as @task" do Task.should_receive(:new).with({'these' => 'params'}). and_return(mock_task(:save => true)) post :create, :task => {:these => 'params'} assigns[:task].should equal(mock_task) end
it "redirects to the created task" do Task.stub!(:new).and_return(mock_task(:save => true)) post :create, :task => {} response.should redirect_to(task_url(mock_task)) end end end
José Valim 2009
How to isolate my controllers?
• Rspec way:
describe TasksController do describe “responding to #POST create” do it "exposes a newly created task as @task" do Task.should_receive(:new).with({'these' => 'params'}). and_return(mock_task(:save => true)) post :create, :task => {:these => 'params'} assigns[:task].should equal(mock_task) end
it "redirects to the created task" do Task.stub!(:new).and_return(mock_task(:save => true)) post :create, :task => {} response.should redirect_to(task_url(mock_task)) end end end
José Valim 2009
Problems with Rspec way
•It’s not readable. I have to read the stubs before reading the action.
it "redirects to the created task" do
Task.stub!(:new).and_return(mock_task(:save => true))
post :create, :task => {}
response.should redirect_to(task_url(mock_task))
end
José Valim 2009
Problems with Rspec way
• It’s not DRY. I have to write expectations (:should_receive) and stubs (:stub!).
Task.should_receive(:new).with({'these' => 'params'}).
and_return(mock_task(:save => true))
Task.stub!(:new).and_return(mock_task(:save => true))
José Valim 2009
How to isolate my controllers?
• Remarkable 3.0 way:
describe TasksController do
describe :post => :create, :task => {:these => 'params'} do
expects :new, :on => Task,
:with => { :these => 'params' },
:returns => mock_task
should_assign_to :task, :with => mock_task
should_redirect_to { task_url(mock_task) }
end
end
José Valim 2009
How to isolate my controllers?
• Remarkable 3.0 way:
describe TasksController do describe :post => :create, :task => {:these => 'params'} do expects :new, :on => Task, :with => { :these => 'params' }, :returns => mock_task
should_assign_to :task, :with => mock_task should_redirect_to { task_url(mock_task) } end end
Perform action using expectations (:should_receive)
José Valim 2009
How to isolate my controllers?
• Remarkable 3.0 way:
describe TasksController do describe :post => :create, :task => {:these => 'params'} do expects :new, :on => Task, :with => { :these => 'params' }, :returns => mock_task
should_assign_to :task, :with => mock_task should_redirect_to { task_url(mock_task) } end end
Perform action using stubs (:stub!)
José Valim 2009
Why to speed up my tests?
José Valim 2009
Why to speed up my tests?
José Valim 2009
How to speed up my tests?• Rspec way (one request per test = slow):
describe TasksController do describe “responding to #POST create” do it "exposes a newly created task as @task" do Task.should_receive(:new).with({'these' => 'params'}). and_return(mock_task(:save => true)) post :create, :task => {:these => 'params'} assigns[:task].should equal(mock_task) end
it "redirects to the created task" do Task.stub!(:new).and_return(mock_task(:save => true)) post :create, :task => {} response.should redirect_to(task_url(mock_task)) end end end
José Valim 2009
How to speed up my tests?
• Terrible way (but fast):
describe TasksController do describe “responding to #POST create” do it “should assign to @task and redirect” do Task.should_receive(:new).with({'these'=>'params'}). and_return(mock_task(:save => true))
post :create, :task => {:these => 'params'}
assigns[:task].should equal(mock_task) response.should redirect_to(task_url(mock_task)) end end end
José Valim 2009
How to speed up my tests?
• Rspec solution?
before(:all) do
# It does not work without tweaking
end
• Test::Unit solution?
José Valim 2009
How to speed up my tests?
• Remarkable 3.0 way:
describe TasksController do
describe :post! => :create do
expects :new, :on => Task, :returns => mock_task
should_assign_to :task, :with => mock_task
should_redirect_to { task_url(mock_task) }
end
end
José Valim 2009
How to speed up my tests?
• Remarkable 3.0 way:
describe TasksController do
describe :post! => :create do
expects :new, :on => Task, :returns => mock_task
should_assign_to :task, :with => mock_task
should_redirect_to { task_url(mock_task) }
end
end
BANG!
José Valim 2009
How to speed up my tests?
• Remarkable 3.0 way:
describe TasksController do
describe :post! => :create, :task => {} do
should_assign_to :task, :with => mock_task
should_redirect_to { task_url(mock_task) }
end
endWorks without stubs and mocks too.
José Valim 2009
How to speed up my tests?
• Remarkable 3.0 BANG way:
Experimental. But if you have any problem, just remove the bang!
Its core might be ported back to rspec.
José Valim 2009
And for those who use tests as specification…
José Valim 2009
And for those who use tests as specification…
• example/specs.rb
describe User do
should_validate_presence_of :name, :email
xshould_validate_presence_of :age
should_validate_length_of :name, :within => 3..40
should_validate_uniqueness_of :email, :case_sensitive => false
should_validate_numericality_of :age, :only_integer => true,
:greather_than_or_equal_to => 18, :allow_blank => true
end
José Valim 2009
And for those who use tests as specification…
• ruby example/specs.rb –cfs en
Example disabled: require age to be set
User- should require name and email to be set- should ensure length of name is within 3..40 characters- should require unique values for email case insensitive- should ensure numericality of age allowing only integer values
and allowing blank values
Finished in 0.262418 seconds
José Valim 2009
And for those who use tests as specification…
• ruby example/specs.rb –cfs pl
Przykład niepełnosprawny: wymagać podania wiek
Użytkownik
- powinien wymagać podania imię i e-mail
- powinien upewnienia co do ilości znaków imię pomiędzy 3..40
- powinien wymagać niepowtarzalnych wartości dla e-mail z brakiem
znaczenia wielkości liter
- powinien upewnienia o numeryczności wiek zezwalając tylko na całe
wartości i zezwalając pustą wartość
Finished in 0.255462 seconds
José Valim 2009
And for those who use tests as specification…
• ruby example/specs.rb –cfs pl
Przykład niepełnosprawny: wymagać podania wiek
Użytkownik
- powinien wymagać podania imię i e-mail
- powinien upewnienia co do ilości znaków imię pomiędzy 3..40
- powinien wymagać niepowtarzalnych wartości dla e-mail z brakiem
znaczenia wielkości liter
- powinien upewnienia o numeryczności wiek zezwalając tylko na całe
wartości i zezwalając pustą wartość
Finished in 0.255462 seconds REMARKABLE 3.0
SPECS WITH I18N
José Valim 2009
So Remarkable is…
• A framework for rspec matchers which provides automatic conversion to macros;
• Decouple matcher logic from content, using .yml files and providing I18n;
• Has a DSL and provides common helpers .
José Valim 2009
So Remarkable is…
• Remarkable 3.0 is composed of:
Remarkable Remarkable ActiveRecord
Matchers and some I18n tweaks Remarkable Rails
Controller matchers Macro Stubs (:expects DSL)
José Valim 2009
So Remarkable is…
• Remarkable 3.x will be composed of:
Remarkable Remarkable ActiveRecord Remarkable Rails Remarkable Datamapper? Remarkable Sequel?
José Valim 2009
So Remarkable is…
• Remarkable 3.x will be composed of:
Remarkable Remarkable ActiveRecord Remarkable Rails Remarkable Datamapper? Remarkable Sequel?
VOLUNTEERS?
José Valim 2009
When? Where?
• Remarkable 3.0 beta will be released mid April;
• Got get it on Github:http://github.com/carlosbrando/remarkable
• Follow my blog:http://josevalim.blogspot.com/