[Tech talk] testing for the skeptical developer (tdd, bdd & other)
BDD style Unit Testing
-
date post
12-Sep-2014 -
Category
Technology
-
view
25.626 -
download
1
description
Transcript of BDD style Unit Testing
![Page 1: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/1.jpg)
How BDD style Unit Testing helps you write better
test code (and some caveat)
@ihower at RubyKaigi 2011 Ruby Taiwan & OptimisDev
![Page 2: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/2.jpg)
About Me
• Chang, Wen-Tien 張文鈿
• a.k.a. ihower
• http://ihower.tw
• http://twitter.com/ihower
I’m ihower and here is my blog and twitterI have program ruby from 2006 for ruby on railsAnd I’m Yet another rubyist since rails.
![Page 3: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/3.jpg)
From Taiwan
3.5hrs
I come from Taiwan which is far away about 4hours flightI’m sorry that I can’t speak Japanese, and English is not my native languageIf I speak Chinese, I guess you will escape now. So please forgive my english. I hope it’s simple to understand.
![Page 4: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/4.jpg)
I work at Optimis International, which is an america company in Taipei
![Page 5: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/5.jpg)
Ruby Taiwanhttp://ruby.tw
Beside daily job, I’m the founder and organizer of Ruby Taiwan community, I found the community since 2008.
![Page 6: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/6.jpg)
RubyConf Taiwan 2011/8/26-27http://rubyconf.tw
We are organizing rubyconf taiwan now. The date is augest 26 and 27 and it open registration now.Welcome to Taiwan.
![Page 7: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/7.jpg)
Last year photo. Nice small meeting room. pretty sweet.
![Page 8: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/8.jpg)
Agenda
• Why Unit Testing?
• How to do unit test in BDD style?
• Some BDD caveat
Today’s agenda. There will be three part:First, I will talk about why, then how to do, finally talk about some caveat.
![Page 9: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/9.jpg)
How many people do unit test?
Before get into BDD, how many people here do unit test all the way?Could you raise your hand?
![Page 10: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/10.jpg)
How many people use BDD framework
(RSpec)?
And how many people use rspec?
![Page 11: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/11.jpg)
VerificationAre we writing the code right?Unit Test
Customer Test(Acceptance Test)
ValidationAre we writing the right software?
Two kinds of test:
There are roughly two kinds of test:Unit test and Customer test(or called Acceptance test)Unit test is about verification, the question is are we writing the code right?Customer test is about validation, the question is are we writing the right software?
![Page 12: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/12.jpg)
Test individual class and methodUnit Test
Customer Test(Acceptance Test)
Test functionality of the over-all system
Two kinds of test:
Unit test test individual class and methodAnd Customer Test test functionality of the over-all system
![Page 13: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/13.jpg)
Today is all about Unit Testing
Today is all about unit testing
![Page 14: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/14.jpg)
1. Why Unit Testing?
Let get started part one
![Page 15: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/15.jpg)
1. VerificationDo we write the code right?
First, it does verification
![Page 16: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/16.jpg)
How to verify this?There are three execution path
def correct_year_month(year, month)
if month > 12 if month % 12 == 0 year += (month - 12) / 12 month = 12 else year += month / 12 month = month % 12 end end return [year, month] end
For example, how to verify this simple if else code.There are three execution path which means if you manually testyou need play it 3 times in different way
![Page 17: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/17.jpg)
via UI?
![Page 18: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/18.jpg)
via irb console?
![Page 19: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/19.jpg)
Manually test is inefficient
![Page 20: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/20.jpg)
Unit testing give us instant coding feedback
it "should return correct year and month" do correct_year_month(2011, 3).should == [2011, 3] correct_year_month(2011, 14).should == [2012,2] correct_year_month(2011, 24).should == [2012,12]end
automatic unit testing can give us instant feedbackyou can test many times you want.
![Page 21: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/21.jpg)
2.Check regressionSafety Net: when you add new feature and refactoring
test also check the regressionit’s a safety net when you add new feature or refactoringIt can give you confidence that you won’t break up any existed behavior
![Page 22: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/22.jpg)
3.API designguide you write better API, especially test first.
third, Since test code is the client for your production code,so if the test code is hard to write, it may means your API is not good.Writing unit testing can guide you write better API, especially when you write test first.
![Page 23: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/23.jpg)
4.DocumentationWhat the result should be?
Communication is the key for software development.
finally, uniting test can be your code documentation.It can answer you what’s the class and method result should be?Communication the key for successful software development.
![Page 24: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/24.jpg)
Brief Summary
• Your code is not trivial: Automatic test can save your verify/debug time
• Your code is not throwing way:Regression test
• Test first: Guide you write better API
• Tests as Documentation
![Page 25: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/25.jpg)
2. How to do unit test in BDD style?
![Page 26: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/26.jpg)
xUnit Framework
• Each test has four-phase:
• Setup
• Exercise
• Verify (assertion)
• TeardownFirst, We use xUnit framework to test code. Each test should be isolation and has four phase:1. Setup Data2. Exercise production code3. Verify the result 4. Cleanup the data
![Page 27: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/27.jpg)
Ruby Test::Unitclass OrderTest < Test::Unit::TestCase
def setup @order = Order.new end def test_order_amount assert_equal 0, @order.amount end end
1
23
The is the classical ruby test::unit codeRed 1 is setup Red 2 is exerciseRed 3 is verify. We assert the result equal to zero
![Page 28: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/28.jpg)
Minitest::Specdescribe Order do
before do @order = Order.new end it "should have default amount is 0" do @order.amount.must_equal 0 end
end
1
2 3
Here is the BDD style using minitest::specred 1 is setupred 2 is exercisered 3 is verify. the result must equal to zero
![Page 29: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/29.jpg)
First impression about BDD
• Syntax is different
• Like specification
• More readable
So, what’s the first impression about bdd style?Ok, the syntax is different, it looks like spec and may more readable Otherwise the structure is not much different
![Page 30: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/30.jpg)
What’s BDD?
• An improved xUnit Framework
• Focus on clearly describe the expected behavior
• The emphasis is Tests as Documentation rather than merely using tests for verification.
Let’s back to see what’s BDD means.It’s not totally new test framework, it just an improved xUnit frameworkIt focus on describe the expected behaviorIt emphasize tests as documentation, not just for verification.
![Page 31: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/31.jpg)
Terminology changedNew paradigm: Executable Specification
• “Test” becomes “Spec”
• “Assertion” becomes “Expectation”
• “test method” becomes “example” (RSpec)
• “test case” becomes “example group” (RSpec)
For correspond to the new paradigm: the test is the executable specificationThe “test” becomes “spec”“Assertion” becomes “Expectation”
![Page 32: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/32.jpg)
Ruby BDD tools
• RSpec
• Minitest/Spec
In Ruby, there are some choices:RSpec is the most powerful, has many features and many awesome syntax.Minitest/spec is the Ruby 1.9 build-in xunit library. It’s simple, fast, and just works!
![Page 33: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/33.jpg)
Learn BDD style
• It’s about syntax
• syntax
• syntax
• syntax
Let’s learn how to write BDD style test code.Basically, it’s just learn about syntax.
![Page 34: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/34.jpg)
BDD style element (1)Context
![Page 35: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/35.jpg)
one test case
a class or string
describe Order do # ...end
# or
describe "A Order" do # ...end
Instead of test case class, BDD style use “describe” block for one test caseIt accept parameter which means the stuff you want to describe, usually a class name or just description text.
![Page 36: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/36.jpg)
Can be nested
#method means instance method
.method means class method
describe Order do
describe "#amount" do # ... end
describe ".size" do # ... end
end
“describe” block can be nested.And inner “describe” usually describe method.Pound means instance methoddot means class method
![Page 37: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/37.jpg)
In RSpec,alias_method :context, :describe
describe Order do
context "when initialized" do describe "#amount" do # ... end
describe ".size" do # ... end
end
end
1
2
3
3
Is RSpec, it also alias describe to context.it used to organize test code into different context.
![Page 38: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/38.jpg)
BDD style element (2)Declarative test
![Page 39: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/39.jpg)
one test method: ityou can write test description in plain text
describe Order do
describe "#amount" do describe "when user is vip" do it "should discount five percent if total > 1000" do # ... end it "should discount ten percent if total > 10000" do # ... end end end
Each test method is “it” blockAnd it accept one parameter which is description text
![Page 40: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/40.jpg)
Setup method: beforedescribe Order do
before do @user = User.new( :is_vip => true ) @order = Order.new( :user => @user ) end end
the setup method is “before” block
![Page 41: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/41.jpg)
describe Order do
before do @user = User.new( :is_vip => true ) @order = Order.new( :user => @user ) end describe "#amount" do before do @order2 = Order.new( :user => @user ) end end end
nested beforebtw, the outer data will be shared, make the test faster
1
2
“before” block also support nestedMore important, the outer setup data will be shard for all inside test methods.Make the test faster since we need not create again every time.
![Page 42: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/42.jpg)
https://github.com/citrusbyte/contest
class SomeTest < Test::Unit::TestCase setup do @value = 1 end
test "sample test" do assert_equal 1, @value end
context "a context" do setup do @value += 1 end
test "more tests" do assert_equal 2, @value end
endendThere are library which make test/unit support context and declarative test ,
like contest
![Page 43: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/43.jpg)
https://github.com/jm/context
class UserTest < Test::Unit::TestCase
context "A new User" do before do @user = User.first end
test "should have the right full_name" do assert_equal "Dude Man", @user.full_name end end
Or context
![Page 44: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/44.jpg)
Rails support declarative test(but no nested context)
class PostsControllerTest < ActionController::TestCase
setup @post = posts(:one) end test "should show post" do get :show, :id => @post.id assert_response :success end
end
Even rails, It supports declarative test syntax. It use “test” block But no nested context.
![Page 45: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/45.jpg)
BDD style element (3)Expectation
![Page 46: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/46.jpg)
Expectation(Assertion)Minitest/Spec
@order.amount.must_equal [email protected]_equal 2000lambda{ order.ship! }.must_raise NotPaidError
BDD style assertion is called expectation.In menitest/spec, the methods are must_equal, wont_equal, must_raiseIn unit/test, some people may confuse assert method that first parameter and second parameter, which one is actual and which one is expect. But BDD style, it’s impossible to confuse it.
![Page 47: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/47.jpg)
Expectation(Assertion)RSpec
@order.amount.should == [email protected]_not == 2000lambda{ order.ship! }.should_raise NotPaidError# orexpect { order.ship! }.to raise_error(NotPaidError)
Here is rspec version. should equal and should_not equal
![Page 48: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/48.jpg)
Matchyhttps://github.com/jm/matchy
x.should == 42y.length.should_not be(4)lambda { raise "FAIL" }.should raise_error
There is also library can make test/unit support bdd style expectation only.
![Page 49: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/49.jpg)
BDD cares aboutthe output
![Page 50: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/50.jpg)
Output (rspec)
Really nice code documentation
RSpec output :)
RSpec does the output very well, you can see really nice code documentation
![Page 51: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/51.jpg)
HTML format
![Page 52: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/52.jpg)
Minitest Output :(
But I can’t not find how to generate code documentation in minitest/spec
![Page 53: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/53.jpg)
require 'minitest/pride'# Show your testing pride!
I found there is a minitest/pride you can required! It may help format the output, so I try it!
![Page 54: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/54.jpg)
Very funny @_@
And here is the output....... very funny. Good job!
![Page 55: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/55.jpg)
Use turn gemgem 'minitest' # use gem version instead of stdlibrequire 'minitest/spec'require 'turn'
MiniTest::Unit.use_natural_language_case_names = trueMiniTest::Unit.autorun
Anyway, There is a gem called trun which used in Rails 3.1It can generate nice test output for unit/testSo here I require it.Btw, I suggest you use the latest minitest gem version, not standard library. Since the gem version is much new. I totally agree tenderlove suggestion yesterday that we should move some standard library into gem.
![Page 56: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/56.jpg)
Minitest::Spec output :)
Here is the output, very nice.
![Page 57: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/57.jpg)
Brief Summary
• Three BDD style elements:
• context
• declarative test
• expectation
• test output can be code documentation
![Page 58: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/58.jpg)
So...
Why Syntax Matters?
![Page 59: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/59.jpg)
http://www.nytimes.com/2010/08/29/magazine/29language-t.html
Your language shape how you think!
Language will not limit you, but it does influence how you think.
![Page 60: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/60.jpg)
For example:
![Page 61: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/61.jpg)
test_datetime_format
# ruby/test/logger/test_logger.rbclass TestLogger < Test::Unit::TestCase
def test_datetime_format dummy = STDERR logger = Logger.new(dummy) log = log_add(logger, INFO, "foo") assert_match(/^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d.\s*\d+ $/, log.datetime) logger.datetime_format = "%d%b%Y@%H:%M:%S" log = log_add(logger, INFO, "foo") assert_match(/^\d\d\w\w\w\d\d\d\d@\d\d:\d\d:\d\d$/, log.datetime) logger.datetime_format = "" log = log_add(logger, INFO, "foo") assert_match(/^$/, log.datetime) end
end
This is a test code from ruby core for Loggerit test datetime_format method
![Page 62: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/62.jpg)
the same test in rubyspec# rubyspec/logger/logger/datetime_format_spec.rbdescribe "Logger#datetime_format" do # ... it "returns the date format used for the logs" it "returns nil logger is using the default date format"end
describe "Logger#datetime_format=" do # ...
it "sets the date format for the logs" do format = "%Y" @logger.datetime_format = "%Y" @logger.datetime_format.should == "%Y" @logger.add(Logger::WARN, "Test message") @log_file.rewind
regex = /2[0-9]{3}.*Test message/ @log_file.readlines.first.should =~ regex end
it "follows the Time#strftime format" endThis is the same test in rubyspec
you can see it divide into many describe and it block.
![Page 63: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/63.jpg)
In this case:
• Test::Unit version just do verify
• Spec version not only verify but also describe its behavior:
• It help us understand what’s the method should do
• It increases the test coverage
![Page 64: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/64.jpg)
The BDD syntax guides you focus on the
expected behavior
![Page 65: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/65.jpg)
Emphasis on Tests as Documentation
![Page 66: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/66.jpg)
Rather than merely using tests for
verification
![Page 67: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/67.jpg)
3. BDD style caveat
![Page 68: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/68.jpg)
1. Too magic?
![Page 69: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/69.jpg)
Metaprogramming?
• The BDD framework using metaprograming should not be the problem.
• The problem is some (RSpec) advanced awesome syntax may make the test code hard to understand or not intuitive.
Metaprogramming implementation should be the problem. If it is, then many ruby library is bad including rails.
The problem is some advanced awesome syntax may hard to understand.
![Page 70: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/70.jpg)
describe Order do its(:status) { should == "New" } end
implicit subject(RSpec)
For example. RSpec has a feature callsd implict subject.You can omit the receiver of expectation method.It also has a syntax call its, its...... its.... well, hard to explain... let see another equal version
![Page 71: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/71.jpg)
describe Order do before do @order = Order.new end it "should have default status is New" do @order.status.should == "New" end end
Equal to this version:
oh.... I see...its will covert Order instance and call its method
well, seems magic!
![Page 72: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/72.jpg)
describe PostsController do describe "PUT #update" do before do @post = Factory(:post) end it "allows an author to edit a post" do sign_in @post.author put :update, :id => post.id, :body => "new content"
response.should be_successful end
it "does not allow a non-author to edit a post" do sign_in Factory(:user) put :update, :id => post.id, :body => "new content"
response.should be_forbidden end endend
would like DRY?
1
2
3
2
3
Another example to test rails controller:red 1 is setup partred 2 is exercisered 3 is verify
The red 2 exercise are almost the same, so some people may want to DRY.
![Page 73: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/73.jpg)
But readable?describe PostsController do describe "PUT #update" do let(:post) { Factory(:post) }
before do sign_in user put :update, :id => post.id, :body => "new content" end
context "when logged in as post's author" do let(:user) { post.author }
it "allows the post to be updated" do response.should be_success end end
context "when logged in as another user" do let(:user) { Factory(:user) }
it "does not allow the post to be updated" do response.should be_forbidden end end endend
3
3
2
1
So they move the code into before blockand rspec has a feature called “let”, it’s a lazy and memorized method for setup data.
Finally in the test method, Only left assertion: response.should be_success
![Page 74: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/74.jpg)
Four-phase Test form is key to understand the
test code• Setup
• Exercise
• Verify (assertion)
• Teardown
Why? I think for test code:four-phase test form is the key to understand.
When I see one test method, I must need to figure out what’s the setup, exercise and verify.
So, it should be easy to trace.
![Page 75: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/75.jpg)
2.English-like DSL?http://pragdave.blogs.pragprog.com/pragdave/2008/03/
the-language-in.html
Second caveat is english-like DSL.Dave Thomas has a nice article explain this problem.
![Page 76: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/76.jpg)
“The language in a DSL should be the language of the domain, not the natural
language of the developer. “ by DaveThomas
He said...
so... To be english-like is not DSL’s purpose. The easy to use and readable DSL API is the purpose
![Page 77: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/77.jpg)
# Baconit 'should have an object identity' do @ary.should.not.be.same_as Array.newend
the be is just “self”
Let’s see one obvious example. This is a bacon test code. There is a be method inside method chain, but it does nothing and just return self.
It existed only because we want to make it like english. Is it right?
![Page 78: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/78.jpg)
RSpec matcher (1)
target.should be_true # targer.should == truetarget.should be_false # targer.should == falsetarget.should be_nil # targer.should == nil
target.should be_a_kind_of(Array) # target.class.should == Arraytarget.should be_an_instance_of(Array) # target.class.should == Array
On the other hand, RSpec has core feature called matcher for expectation.It also make the test code readable. In some way, It’s also very easy to use.
like * be_true, * be_false * be_a_kind_of
![Page 79: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/79.jpg)
RSpec matcher (2)target.should respond_to(:foo) # target.repond_to?(:foo).should == true
target.should have_key(:foo) # target[:foo].should_not == nil
target.should include(4) # target.include?(4).should == true
target.should have(3).items # target.items.length == 3
There are more example like
* have_key* include
![Page 80: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/80.jpg)
RSpec matcher (3)
target.should be_empty # target.empty?.should == true
target.should be_blank # target.blank?.should == true
target.should be_admin # target.admin?.should == true
any prefix be_ matcher will covert it to that method with question mark.
![Page 81: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/81.jpg)
RSpec matcher is very powerful
# Custom Matcherdescribe 9 do it "should be a multiple of 3" do 9.should be_a_multiple_of(3) # (9 % 3).should == 0 endend
# RSpec supports custom matchers DSLRSpec::Matchers.define :be_a_multiple_of do |expected| match do |actual| actual % expected == 0 endend
RSpec matcher is very powerful since it even provide DSL to define your custom matcher which make the your test readable.
![Page 82: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/82.jpg)
Some matchers I like, but some I dislike.
It becomes personal taste :|
![Page 83: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/83.jpg)
3. Deep nested context?describe Order do before do @order = Order.new end describe "#amount" do before do @order2 = Order.new( :total => 2000) end describe "when user is vip" do before do @user = User.new(:is_vip => true) end it "should discount five percent if total >= 1000" do # ... end end endend
1
1
1
There are three nested context here. For the most inner test method, it has three before block outside.
![Page 84: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/84.jpg)
Deep nested is hatefulIt’s hard to understand what’s setup going on in sub-sub-sub-sub context
Deep nested context is hateful, It hard to trace the setup part when there is sub-sub-sub-sub context
![Page 85: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/85.jpg)
Four-phase Test form is key to understand the
test code• Setup
• Exercise
• Verify (assertion)
• Teardown
Again. Four-phase Test form:
Deep context make setup part hard to trace.
That’s why Rails does not support nested context.
Rails team like flat test structure.
![Page 86: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/86.jpg)
4. Too mockist?
• rspec-rails gem divides tests into model,controller,view,helper and encourages you use mocks.
• should_receive easily make people test implementation rather than result by default. Make your test fragile.
RSpec ship with very nice mock feature,And RSpec-rails divides tests into four layer which encourage you use mocks.
But mocks has many caveats too, It make the test fragile easily.
![Page 87: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/87.jpg)
5.Performance penalty?
• minitest/spec performance is almost equal to test/unit. It’s really fast.
• RSpec performance is slower comparing to test/unit.
minitest/spec performance is pretty well, you need not to worry about it.
RSpec is slower, But when running test, I think the bottleneck is your production code and the testing framework factor is not big problem.
![Page 88: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/88.jpg)
Conclusion
![Page 89: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/89.jpg)
• BDD testing framework is unlike ActiveRecord for SQL, Rails for web development. It does not “simplify” test. It’s not an abstract tool for testing.
• So, yes, It’s not required and some people think it’s unnecessary.
yes, it’s not required...
![Page 90: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/90.jpg)
But it’s very valuable for your team!
![Page 91: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/91.jpg)
• The BDD syntax guides you focus on the expected behavior, and increasing the test coverage.
• Emphasis on Tests as Documentation rather than merely using tests for verification.
It focus on describe the expected behavior and increase the test coverageIt emphasize tests as documentation, not just for verification.
![Page 92: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/92.jpg)
Thanks for listening!Please tweet me if you have questions:
@ihower
![Page 93: BDD style Unit Testing](https://reader033.fdocuments.us/reader033/viewer/2022052503/5412bb858d7f72314e8b46d7/html5/thumbnails/93.jpg)
Reference
• RSpec 讓你愛上寫測試 slides, ihower OSDC.TW 2011
• xUnit Test Patterns
• http://avdi.org/devblog/2011/04/07/rspec-is-for-the-literate/
• http://objectreload.com/articles/2010/09/thoughts-on-testing-part-1.html