• Ruby on rails开发从头来(windows)(二十七) 测试驱动开发


    在敏捷开发的实践中,测试驱动是少不了的。这篇来看看在rails中的一个测试驱动开发的例子。

    在前面我们编写并进行了一些单元测试和功能测试,现在,我们的客户突然要求添加一个功能:系统的每个用户都可以对商品进行查询。

     我们先初步的画了一些草图,来整理我们的思路和设计,然后开始写代码。对于具体的实现,我们已经有了大致的思路,但是如果有更多的反馈信息的话会有助于我们走在正确的道路上。我们会在深入到代码之前,编写测试代码。考虑我们的代码将怎样工作,确定一些规约,当测试通过,你的代码就OK了。

    现在,我们来考虑一下查询功能的测试,应该由哪个controller来控制查询操作呢?用户和管理员都可以进行查询操作,我们可以在store_controller.rb或者admin_controller.rb中添加一个search()Action,但是这里我们添加一个SearchController,并且含有一个方法search。在rails命令行执行命令:

    depot>ruby script/generate controller Search search

     

    我们看到,在app/controllerstest/functional目录下已经生成了对应的文件。但是现在我们并不关心SearchControllersearch方法的实现,我们关心的是在测试时我们所期望看到的结果。现在添加测试代码,在test/functional/search_controller_test.rb中添加test_search方法:

    我们首先想到的是调用searchAction,然后判断是否得到了响应:

    get :search, :title => "Pragmatic Version Control"

    assert_response :success

     

    根据之前的草图,我们应该在页面上显示一个flash信息,所以我们要判断flash信息的文本,以及是否显示了正确的页面:

    assert_equal "Found 1 product(s).", flash[:notice]

    assert_template "search/results"

     

    然后我们想要判断查询所得到的商品信息:

    products = assigns(:products)

    assert_not_nil products

    assert_equal 1, products.size

    assert_equal "Pragmatic Version Control", products[0].title

     

    我们还想判断用来显示查询结果的页面的一些内容,我们查询到的商品会作为列表在页面上显示,我们使用catelog视图相同的css样式:

    assert_tag :tag => "div",

          :attributes => { :class => "results" },

          :children => { :count => 1,

          :only => { :tag => "div",

          :attributes => { :class => "catalogentry" }}}

     

    下面是完整的测试方法:

    def test_search

        get :search, :title => "Pragmatic Version Control"

        assert_response :success

        assert_equal "Found 1 product(s).", flash[:notice]

        assert_template "search/results"

        products = assigns(:products)

        assert_not_nil products

        assert_equal 1, products.size

        assert_equal "Pragmatic Version Control", products[0].title

        assert_tag :tag => "div",

          :attributes => { :class => "results" },

          :children => { :count => 1,

          :only => { :tag => "div",

          :attributes => { :class => "catalogentry" }}}

      end 

     

    现在我们来运行测试:ruby test/functional/search_controller_test.rb

    不出意外,会得到下面的结果:

    test_search(SearchControllerTest) [test/functional/search_controller_test.rb:17]:

    <"Found 1 product(s)."> expected but was

    <nil>.

    1 tests, 2 assertions, 1 failures, 0 errors

     

    因为我们还没有设置flash的内容,进一步说,我们还没有实现search这个Action。怎样实现,书上给留了个作业。OK,那我们就自己来一步步实现searchAction

    1.       search方法添加内容:

    @products = Product.find(:all,:conditions=>['title=?',params[:title]])

        if not @products.nil?

          flash[:notice] = sprintf('Found %d product(s).',@products.size)

        end

       

        render(:action=>'results')现在运行测试,结果如下:

    ----------------------------------------------------------------------------

      1) Failure:

    test_search(SearchControllerTest)

        [Z:/study/ruby/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack……

         Z:/study/ruby/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack……

         test/functional/search_controller_test.rb:19:in `test_search']:

    expecting <"search/results"> but rendering with <"search/search">

    1 tests, 3 assertions, 1 failures, 0 errors

    ----------------------------------------------------------------------------

     

    2.       这次轮到assert_template "search/results"断言出错了,是因为我们还没有results这个View,我们在view目录下添加一个results.rhmtl文件,在search_controller.rb文件中添加一个results的空方法:

    def results                               

    end

    还要在search方法中添加一句:render("search/results"),然后再运行测试,结果如下:

    ----------------------------------------------------------------------------

    Finished in 0.125 seconds.

      1) Failure:

    test_search(SearchControllerTest)

        [Z:/study/ruby/InstantRails/ruby/lib/ruby/gems/1.8/gems/…… Z:/study/ruby/InstantRails/ruby/lib/ruby/gems/1.8/gems/……

    expected tag, but no tag found matching {:attributes=>{:class=>"results"}, :tag=>"div",

    "<h1>Search#results</h1>\n<p>Find me in app/views/search/results.rhtml</p>\n".

    <nil> is not true.

    ----------------------------------------------------------------------------

     

    3.       现在可以看到,就只有最后一个断言assert_tag没有通过了,这个断言是对页面上的元素进行判断的,所以我们来实现results页面。仔细看看断言的内容,我们就知道只要在results.rhtml里添加两个div就可以了,下面是results.rhtml的完整内容:

    <h1>Search#results</h1>

    <p>Find me in app/views/search/results.rhtml</p>

    <div class="results">

        <div class = "catalogentry">

        </div>

    </div>

    保存,然后再运行测试,激动人心的时刻来临了,所有的断言都通过了!测试OK了,下面是结果:

    ----------------------------------------------------------

    DEPRECATION WARNING: You called render('search/result……

    t Z:/study/ruby/InstantRails/ruby/lib/ruby/gems/1.8/g……

    Finished in 0.094 seconds.

    1 tests, 7 assertions, 0 failures, 0 errors

    ----------------------------------------------------------

    4.       在实现search.rhtmlresults.rhtml的时候,我碰到了一些问题,用测试用例都可以选出数据来,但是通过页面就怎么也不行了,把log里的sql贴出来到phpMyAdmin里执行,也能选出数据,真不知道是怎么回事,自己对rails的理解还不深,自己胡乱写了这些代码,先把代码都帖出来,等自己对rails有更深入的理解的时候看能不能找到问题。同时也请高人指点

    search_controller_test.rb:

    require File.dirname(__FILE__) + '/../test_helper'

    require 'search_controller'

     

    # Re-raise errors caught by the controller.

    class SearchController; def rescue_action(e) raise e end; end

     

    class SearchControllerTest < Test::Unit::TestCase

      fixtures :products

      def setup

        @controller = SearchController.new

        @request    = ActionController::TestRequest.new

        @response   = ActionController::TestResponse.new

      end

     

      def test_search

        get :search, :title => "Pragmatic Version Control"

        assert_response :success

        assert_equal "Found 1 product(s).", flash[:notice]

        assert_template "search/results"

        products = assigns(:products)

        assert_not_nil products

        assert_equal 1, products.size

        assert_equal "Pragmatic Version Control", products[0].title

        assert_tag :tag => "div",

          :attributes => { :class => "results" },

          :children => { :count => 1,

          :only => { :tag => "div",

          :attributes => { :class => "catalogentry" }}}

      end 

    end

     

    search_controller.rb

    class SearchController < ApplicationController

     

      def search

        print(params[:title])

        @products = Product.find(:all,:conditions=>['title=?',params[:title]])

       

        if not @products.nil?

          flash[:notice] = sprintf('Found %d product(s).',@products.size)

        end

        print(flash[:notice])

        #redirect_to(:action=>'results')

        render(:action=>'results')

      end

     

      def results

       

      end

      def index

       

      end

    end

     

    Views下有三个文件:index.rhtmlresults.rhtmlsearch.rhtml

    index.rhtml

    <html>

    <%= form_tag(:action=>'search',:id=>@products) %>

        <table>

                <tr>

                        <td>Book title:</td>

                        <td><%=text_field("title","")%></td>

                </tr>

               

                <tr>

                        <td><input type="submit" value="SEARCH" /></td>

                </tr>

        </table>

    <%= end_form_tag %>

    </html>

     

    results.rhtml

    <h1>Search#results</h1>

    <p>Find me in app/views/search/results.rhtml</p>

    <div class="results">

        <div class = "catalogentry">

                <table cellpadding="10" cellspacing="0">

                <tr class="carttitle">

                        <td >title</td>

                        <td >description</td>

                        <td >price</td>

                </tr>

               

                <%

                printf("result:%d",@products.size)

                for product in @products

                -%>

                        <tr>

                                <td><%= product.title %></td>

                                <td><%= product.description%></td>

                                <td align="right"><%= fmt_dollars(product.price) %></td>

                        </tr>

                <% end %>

     

        </table>

        </div>

    </div>

     

    search.rhtml

     <html></html>

  • 相关阅读:
    wepy框架入门
    认识WebStorm-小程序框架wepy
    列表
    透明盒子
    wepy框架滑动组件使用
    底部导航栏效果
    安装less/sass
    wepy快速入门
    实现返回顶部-wepy小程序-前端梳理
    音乐播放器-图片切换-轮播图效果
  • 原文地址:https://www.cnblogs.com/dahuzizyd/p/ruby_rails_dotneter_study_27.html
Copyright © 2020-2023  润新知