testing with mock object
DESCRIPTION
shanghaionrails first event presentation by zhanyuanyiTRANSCRIPT
使 用 Mock object进 行 测 试
张 元 一
什么是Mock object?
Mock object 是面向对象编程中,对真实对象的行为以可控的方式进行模拟的一种虚拟对象
wikipedia
汽车碰撞测试中的模型人
为什么需要Mock object?
1.降低代码耦合
一个改动导致大量测试失败
def test_createpost :create, :user =>
{:name => 'test'}end
def test_updateput :update, :user =>
{:name => 'new'}end
add_column :first_nameadd_column :last_nameremove_column :name
挂了!
2. 人生短暂,珍惜时间
不要重复测试
A测试例中已经测试过的代码没必要再在B测试例中进行测试,尤其是这些代码很耗时
# post_test.rbdef test_should_create_postpost = Post.new(...) assert post.valid?
end
class postvalidates_presence_of :xxx
end
# posts_controller_test.rbdef test_should_create_postpost :create,
:post => {...}...
end
重复了!
# posts_controller.rbdef create
post = Post.new(params[:post]) if post.save
...end
3.让自己更轻松
等待是人世间最痛苦的事情之一,尤其是你苦苦等待的结果居然是:
Failure!
如何使用Mock object?
Mocha, Flex Mock or RSpec
Mocha
@post = mock(“post”)
@post = Post.new
@post = mock(“post”) @post.digg
#<Mock:post>.digg -expected calls: 0, actual calls: 1
@post.expects(:digg)
@post.instance_eval {def digg...
end}
@post.expects(:digg).once
at_least(min) at_least_onceat_most(max) at_most_oncenevertimes(num)
if @post.digg # nil...
else...
end
@post.expects(:digg) .returns(true)
@post.expects(:digg) .raises(exception)
@post.digg(@blocked) # [email protected](@unblocked) # true
@post.expects(:digg) .with(any_of(User.blocked)) .returns(false)
@post.expects(:digg) .with(any_of(User.unblocked))
.returns(true)
all_ofany_ofanythinghas_entry(key, value) has_key(key) has_value(value) includes(item) instance_of(klass) kind_of(klass) regexp_matches(regexp)
@post.stubs(:method) @post.stubs(:method =>
:result)
@post.expects(:method) .at_least(0)
@post.expects(:method) .at_least(0) .returns(:result)
@post = stub_everything('post' :method => :result)
@post.method1 # [email protected] # [email protected] # :result
Mocha on Rails
def test_createpost :create, :user =>
{:name => 'test'}end
add_column :first_nameadd_column :last_nameremove_column :name
def test_should_create_userPost.expects(:new)
.returns(@post) @post.expects(:save)
.returns(true)
post :create, :user => {}assert_redirect_to
user_path(@user) end
告别Fixture!
# teachers_students.ymlone: teacher_id: 1student_id: 1
two:teacher_id: 1student_id: 2
three:teacher_id: 2student_id: 3
不够直观
浪费时间
def test_should_show_postUser.expects(:find)
.returns(@user) @user.posts.expects(:find)
.returns(@post1)
get :show, :id=>1, :user_id=>1assert_response :success
end
def setup@user = User.new(:name=>'test') @post1 =
Post.new(:title=>'post1') end
RSpec
it “should create a new user” doUser.should_receive(:new)
.and_return(@user) User.stub!(:save)
.and_return(true)
post :create, :user => {}response.should
redirect_to(user_path(@user)) end
同样的思想,不同的实现!
Flex Mock?
问题?
谢谢!