Rails测试《八》实战功能测试functional test2

今天继续我们的功能测试实战。

项目还是:blog,大家可以从github或者gitcafe中获取项目源码。

 

先来介绍一个断言

assert_difference(expression, difference = 1, message = nil, &block)

http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html中比较详细的介绍。

 

 再一次强烈推荐http://api.rubyonrails.org/,还有http://ruby-doc.org/,还有http://apidock.com/,还有http://guides.rubyonrails.org/。这四个工具网站,真是好啊,谁用谁知道。

 

assert_difference的英文解释是

Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.

 

直译的话就是

测试两个数值的区别。

哪两个数值呢,就是expression的数值。怎么变成一个了?不要着急,一个是block执行之前,一个是block执行之后。

 

意译一下就是

比较expression的数值在block执行前后的差,看这个差是否是difference 参数指定的值,这个参数是个可选参数,默认值是1。

也可以理解为,block执行之后的expression值 - block执行之前的expression值,看这个差值是否等于difference 指定的值,默认差值为1。

 

我们先来个简单例子说明一下。

就拿项目中的添加post来举例,对应的controller的代码如下:

  
  
  
  
  1. def create 
  2.     @category = Category.find(params[:post][:category_id]) 
  3.   
  4.     params[:post].delete(:category_id
  5.     @post = @category.posts.build(params[:post]) 
  6.     @post.user = current_user 
  7.     @post.tag_ids = params[:tag_ids
  8.     if @post.save! 
  9.       flash[:notice] = "post was created successfully" 
  10.       redirect_to admin_posts_path 
  11.     else 
  12.       flash.now[:notice] = "create post failed" 
  13.       render :new 
  14.     end 
  15.   end 

添加成功之后会跳转到posts列表,失败的话,就保留在new这个页面。

当然了,测试添加可以有很多方法。比如说添加之后,查询一下,看看是否存在。如果有标题唯一之类的验证,也可以再次添加相同的post,然后看看是否会提示已经存在。或者验证添加之后是否跳转到posts列表,是否输出正确添加的flash信息。

今天我们就是为了讲解assert_difference这个断言,前面说过了,这个断言用来判断block前后的expression的差。再添加一篇post之后,post的个数会增加一,我们就用这个断言来验证添加是否成功。

在test/functional/admin/posts_controller_test.rb文件中敲入下面的代码。

  
  
  
  
  1. require 'test_helper' 
  2.  
  3. class Admin::PostsControllerTest < ActionController::TestCase 
  4.  
  5.   include FactoryGirl::Syntax::Methods 
  6.   def test_should_create_post_successfully 
  7.     user = FactoryGirl.create(:user_valid) 
  8.     category = FactoryGirl.create(:category_valid) 
  9.   
  10.     article = FactoryGirl.build(:post_valid) 
  11.     tag = FactoryGirl.build(:tag_valid) 
  12.      
  13.  
  14.     assert_difference('Post.count') do    
  15.       post :create, {:post=>{:category_id=>category.id, 
  16.                              :title=>article.title, 
  17.                              :slug=>article.slug, 
  18.                              :summary=>article.summary, 
  19.                              :content=>article.content}, 
  20.                      :tag_ids => [tag.id]}, 
  21.                     {:user_id => user.id} 
  22.     end 
  23.  
  24.     assert_redirected_to admin_posts_path(assigns(:posts)) 
  25.  
  26.   end 
  27.  
  28. end 

在代码中我们使用了factory-girl模拟的数据。然后用功能测试提供的post方法想postscontroller的create方法提交了数据,因为我们添加post的过程需要用到当前用户的信息,当前用户的信息又是从session中获取的,所以在post方法中需要给session参数指定信息,使得session[:user_id]有值。

当然了,还有一种办法可以指定session的值。那就是通过@request这个变量,前面介绍functional test的时候讲过,我们有三个变量可以在功能测试中使用。

  • @controller
  • @request
  • @response

@response.session[:user_id]=user.id也可以实现设置session的作用,这样的话,就可以省略掉post方法中的session参数部分。

变成下面的样子。

  
  
  
  
  1. def test_should_create_post_successfully 
  2.     user = FactoryGirl.create(:user_valid
  3.     category = FactoryGirl.create(:category_valid
  4.     @request.session[:user_id] = user.id 
  5.     article = FactoryGirl.build(:post_valid
  6.     tag = FactoryGirl.build(:tag_valid
  7.      
  8.  
  9.     assert_difference('Post.count'do    
  10.       post :create, {:post=>{:category_id=>category.id, 
  11.                              :title=>article.title, 
  12.                              :slug=>article.slug, 
  13.                              :summary=>article.summary, 
  14.                              :content=>article.content}, 
  15.                      :tag_ids => [tag.id]} 
  16.     end 
  17.  
  18.     assert_redirected_to admin_posts_path(assigns(:posts)) 
  19.  
  20.   end 

具体哪个好,这个需要大家在使用的过程中慢慢揣摩吧。其实都不错,我觉得在不同的场景,使用不同的方式。

这个测试有两个断言,一个就是assert_difference,一个是assert_redirected_to。断言是否跳转的posts列表,并且传递了posts变量。

在功能测试中可以访问的四个hash分别是:

  
  
  
  
  1. flash["gordon"]               flash[:gordon
  2. session["shmession"]          session[:shmession
  3. cookies["are_good_for_u"]     cookies[:are_good_for_u
  4.   
  5. # Because you can't use assigns[:something] for historical reasons: 
  6. assigns["something"]          assigns(:something

在下面这个场景,我觉得使用@request.session来设置session,就要比写在post的参数中好。

  
  
  
  
  1. require 'test_helper' 
  2.  
  3. class Admin::CategoriesControllerTest < ActionController::TestCase 
  4.   include FactoryGirl::Syntax::Methods 
  5.  
  6.   def test_should_create_category_successfully 
  7.  
  8.     user = FactoryGirl.create(:user_valid
  9.     @request.session[:user_id] = user.id 
  10.  
  11.     category1 = build(:category_valid
  12.     category1.title = FactoryGirl.generate(:category_title
  13.  
  14.     category2 = build(:category_valid
  15.     category2.title = FactoryGirl.generate(:category_title
  16.  
  17.     assert_difference "Category.count", 2 do 
  18.       post :create, {:category => {:title => category1.title}} 
  19.       post :create, {:category => {:title => category2.title}} 
  20.  
  21.     end 
  22.   end 
  23. end 

好处就是只需要设置一次,如果是写在参数中,那么就需要写两次相同的代码,维护与修改就集中了。

其实还可以用它来测试delete方法,还是测试差值,删除前后的差值,只是这个值是否是-1。

和assert_difference对应的还有一个no_difference。

assert_no_difference(expression, message = nil, &block)

用来测试block前后,expression的数值是否没有发生变化。

比如说测试一下,当post的属性中有违反validates的,这时候添加肯定会失败,block前后的Post.count应该是没有变化的。

 

下面就是测试添加tag失败的例子,tag的title长度要大于3,空的title自然就是无效的tag。

  
  
  
  
  1. def test_should_create_tag_fail 
  2.    user = create(:user_valid
  3.    @request.session[:user_id] = user.id  
  4.  
  5.    assert_no_difference "Tag.count" do  
  6.      post :create, {:tag => { :title => ""}} 
  7.    end 
  8.  end 

 

你可能感兴趣的:(test,Rails,functional,factory-girl)