`
piecehealth
  • 浏览: 47936 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

数据比较小程序(Ruby)

 
阅读更多
做测试的时候经常会遇到数据比较的情况,大部分情况数据的格式都可视为二维表,每次比较内容相同,但是数据每次都不同,所以写了一个小程序来自动化这些比较过程。

思路是通过ruby元编程的技巧根据二维表格式自动创建相关类,然后可以通过编程的方式灵活对其进行比较,而且比较部分的代码是完全可以复用的。

比如说数据如下

name class math grade chinese grade english grade total grade
One 1 34 55 66 155
Two 1 45 67 24 136
Three 2 56 55 33 144
Four 2 53 53 24 129
Five 2 77 99 33 209

第一步:根据数据结构创建出类,并且把每条数据映射成类的实例。
我会动态创建一个类(假定类名是Demo),类的属性分别是name, class, math grade等等,同时我会把每条记录动态填充进Demo的类变量中,这样可以通过类似于Demo.all的方法得到全部数据,每条数据对应一个Demo的实例。

第二步:添加测试方法。
数据比较我目前归纳为3种:
第一种:数据内的比较:比如上面的math grade, chinese grade, english grade加起来是否等于total grade.
第二种:数据间的比较:比如class 1跟class 2数据aggregate之后的种种比较(如sql里的group by)
第三种:不同数据源之间的比较:如另外的一个表跟上面的表比较。

第一步完成后,第二步显得非常简单……而且比较的内容都是一致,所以每次比较只需要更换数据源(文件名或者sql或者调用服务得到的结果)就可以,可以说实现数据比较自动化。

第三步:运行测试方法,生成报告。
在ruby强大的反射机制下运行测试方法非常简单,生成报告成了锦上添花的东西(但是直接影响老板们感觉的因素)。


根据上述描述,使用时我期望是这样的:

# step 1
creat_class "Demo", data_source
# step 2
class Demo
    def compare_total_grade #比较总分是否等于三项相加
      expect = @math_grade.to_i + @chinese_grade.to_i + @english_grade.to_i
      actual =  @total_grade.to_i
      name = "Name: #{@name}\t";
      puts((expect == actual)? name + "Pass": name + "Fail: Expect Result:#{expect}, Actual Result:#{actual}")
    end
  
    def compare_math_greater_than_chinese #比较数学成绩是否大于语文成绩
      name = "Name: #{@name}\t"
      puts((@math_grade.to_i > @chinese_grade.to_i)? name + "Pass": name + "Fail: Math:#{@math_grade}, Chinese:#{@chinese_grade}")      
    end
    
    def self.compare_xxxxx #定义第二种第三种比较的方法。
    end

end
# step 3
Demo.run_compares


具体实现code如下
module TestingTool

  # Include this module, you must specify @@outer_attributes and @@outer_original_records
  # @@outer_attributes: An array to store all attributes of each record.
  # @@outer_original_records: orignial records from source file. It's an array too.
  
  class CompareTool
    
    @@outer_attributes ||= []
    @@outer_original_records ||= []
    
    def self.creat_class klass_name

      process_outer_data
      klass = Object.const_set(klass_name, Class.new)
      inner_attributes = @@outer_attributes.clone
      inner_original_records = @@outer_original_records.clone
      
      klass.class_eval do

        attr_accessor *inner_attributes
        @@attributes = inner_attributes.clone
        @@original_records = inner_original_records.clone
        @@records = {}
        
        # Define method: initialize
        define_method(:initialize) do |*values|
          inner_attributes.each_with_index {|attribute, i| instance_variable_set("@" + attribute, values[i])}
        end
        
        define_method(:inspect) do
          str = "\t#{self.to_s}\n"
          instance_variables.each {|variable| str = str + "#{variable}\t: #{instance_variable_get(variable)}\n"}
          str = str + "-------------------------"
        end
        
        # Open meta class
        class << self
          # dump: dumps all record into @@records.
          define_method(:dump) do |primary_key|
            raise 'Invalid Primary Key' unless @@attributes.include?(primary_key)
            pk_index = @@attributes.index(primary_key)
            @@original_records.each do |record|
              @@records[record[pk_index].to_sym] ||= []
              @@records[record[pk_index].to_sym] << self.new(*record)
            end
          end
          
          # Execute the compare methods...
          def run_compares
            @@records.each_value do |records|
              records.each do |record|
                self.new.methods.each do |method|  
                  if method.to_s =~ /^compare_/
                    record.method(method).call
                  end
                end
              end
            end
            
            self.methods.each do |method|  
              if method.to_s =~ /^compare_/
                self.method(method).call
              end
            end
          end
          
          def find key
            @@records[key]
          end
          
          def all
            @@records
          end
          
          def find_all condition_hash
            index_hash = {}
            condition_hash.keys.each do |key|
              raise "Invalid Key: #{key}." unless @@attributes.include? key.to_s
              index_hash[key] = @@attributes.index(key.to_s)
            end
            result = []
            @@records.each_value do |records|
              records.each do |record|
                flag = true
                condition_hash.each {|key, value| if record.method(key).call != value; flag = false; break; end;}
                result << record if flag
              end
            end
            result
          end
          
        
        end # class << self
        
      end
      
    end

  
    private
    def self.process_outer_data
      nil_indexs = []
      @@outer_attributes.each_with_index {|a, i| nil_indexs << i if(a.nil? || a.empty?)}
      nil_indexs.each {|i| @@outer_attributes[i] = nil unless @@outer_attributes[i].nil?}
      @@outer_original_records.collect! do |record|
        nil_indexs.each {|i| record[i] = nil}
        record.compact
      end
      @@outer_attributes.compact!
      @@outer_original_records.compact!
    end

  end
  
end


以上是基础的类,因为不同数据源读取数据方式不一样,但是只需要读取title跟具体数据之后的操作都是一样的,所以把通用操作的放到class CompareTool中,子类只要填充@@outer_attributes(属性名字) 和 @@outer_original_records(存放记录的数组)
下面是一个读excel的文件的类
require 'CompareTool'
require 'win32ole'

module TestingTool

  class ExcelCompareTool < CompareTool
    
    def self.creat_class klass_name, excel_name, worksheet_name, title_start_pos, title_end_pos 
      title_start_pos =~ /^([[:alpha:]]+)(\d+)$/
      start_pos = $1
      row_number1 = $2
      title_end_pos =~  /^([[:alpha:]]+)(\d+)$/
      end_pos = $1
      row_number2 = $2
      raise "Invaild title area: #{title_start_pos}:#{title_end_pos}" unless row_number1 == row_number2
      begin
        excel = WIN32OLE::new('excel.Application')
        workbook = excel.Workbooks.Open(excel_name)
        worksheet = workbook.Worksheets(worksheet_name)
        @@outer_attributes = worksheet.Range("#{title_start_pos}:#{title_end_pos}")['Value'][0].collect {|i| i.to_s.downcase.gsub(/[ \/\\\.\t\r\n]/, '_')}
        row_number = row_number1.to_i
        while true
          row_number = row_number + 1
          break if worksheet.Range("#{start_pos}#{row_number.to_s}:#{end_pos}#{row_number.to_s}")['Value'][0].compact.empty?
          @@outer_original_records << worksheet.Range("#{start_pos}#{row_number.to_s}:#{end_pos}#{row_number.to_s}")['Value'][0]
        end
        #worksheet.Select
        #data = worksheet.Range(data_range)['Value']
      rescue => e
        raise e
      ensure
        excel.quit unless excel.nil?
      end

      
      super klass_name
    end
    
  end

end

使用代码(测试文件我会放到附件中)
require 'ExcelCompareTool'

include TestingTool

require 'pathname'
path = Pathname.new(File.dirname(__FILE__)).realpath.to_s.gsub('/', '\\')
excel_path = "#{path}\\book1.xlsx"
ExcelCompareTool.creat_class 'Demo', excel_path, 'sheet1', 'e13', 'j13'
Demo.dump 'name'

class Demo
    
    def compare_total_grade
      expect = @math_grade.to_i + @chinese_grade.to_i + @english_grade.to_i
      actual =  @total_grade.to_i
      name = "Name: #{@name}\t"
      puts((expect == actual)? name + "Pass": name + "Fail: Expect Result:#{expect}, Actual Result:#{actual}")
    end
  
    def compare_math_greater_than_chinese
      name = "Name: #{@name}\t"
      puts((@math_grade.to_i > @chinese_grade.to_i)? name + "Pass": name + "Fail: Math:#{@math_grade}, Chinese:#{@chinese_grade}")      
    end
    
end

Demo.run_compares

同时创建的类还可以通过classname.all返回所有记录,通过classname.find_all(:attribute_name1 => value1,:attribute_name2 => value2)来查找符合条件的记录。
分享到:
评论
2 楼 piecehealth 2013-09-13  
liyu212 写道
第三种:不同数据源之间的比较:如另外的一个表跟上面的表比较。
楼主在吗?急用这个小程序啊
上面的第三种比较怎么实现呢?
还有这个程序再ruby2.0上无法运行,能否帮忙改进一下


加我QQ 81774487
1 楼 liyu212 2013-09-11  
第三种:不同数据源之间的比较:如另外的一个表跟上面的表比较。
楼主在吗?急用这个小程序啊
上面的第三种比较怎么实现呢?
还有这个程序再ruby2.0上无法运行,能否帮忙改进一下

相关推荐

    ruby小程序

    Ruby小程序,即用Ruby语言编写的小规模程序或工具,通常是为了解决特定问题或实现特定功能而设计的。 Ruby的核心特性包括: 1. 面向对象:Ruby是一种纯面向对象的语言,每一个数据类型都是一个对象,包括基本类型...

    Ruby版微信小程序对称加密数据解密算法wechat_aes_sample_ruby-master.zip

    在Ruby编程环境中,微信小程序的对称加密数据解密算法主要涉及到的是AES(Advanced Encryption Standard)加密技术。AES是一种广泛使用的块密码,以其安全性、效率和标准化而闻名。本项目"wechat_aes_sample_ruby-...

    小程序茶叶商城源码(带后台 ruby)

    【小程序茶叶商城源码(带后台 Ruby)】是一款基于微信小程序开发的在线茶叶销售平台,其后台管理系统采用了 Ruby 作为主要编程语言。这个项目旨在提供一个完整的电子商务解决方案,结合前端的小程序界面和后端的...

    ruby小程序2.rar

    在"ruby小程序2.rar"这个压缩包中,包含了两个文件:关羽.lps和诸葛亮.lps,推测它们可能是使用Ruby编写的小程序或者脚本。下面我们将深入探讨Ruby编程语言的关键知识点以及如何解读这两个文件名。 1. **Ruby基础**...

    ruby tcp/ip 简单小程序实例

    下面我们将详细探讨如何使用Ruby编写TCP/IP的小程序。 首先,我们需要了解TCP/IP通信的基本步骤: 1. 建立连接:客户端(Client)向服务器端(Server)发送一个连接请求,服务器响应并建立连接。 2. 数据传输:...

    Ruby-RubyGraphVizGraphViz绘图工具的Ruby接口

    RubyGraphViz是Ruby编程语言与GraphViz图形渲染库之间的接口,允许开发者在Ruby程序中生成复杂的图形和图表。GraphViz是一个强大的开源图形绘制软件,它能够自动生成有向无环图(DAG)、树状图、网络图等多种类型的...

    Ruby-rabl普通的ruby模板包含jsonbsonxmlplist和msgpack支持

    Ruby是一种动态、面向对象的编程语言,而RABL(Ruby API Builder Language)是Ruby社区中一个流行的模板库,专门用于生成结构化的数据输出,如JSON、BSON、XML、Plist和MsgPack。这些格式在Web开发中尤其重要,因为...

    Ruby-RubySQLite中的语言学习工具

    一旦安装了`sqlite3`库,你就可以在Ruby程序中创建、连接、查询和操作SQLite数据库。以下是一些基本的示例: 1. **创建数据库连接**: ```ruby require 'sqlite3' db = SQLite3::Database.new "test.db" # 创建名...

    ruby 移动图片小工具

    标题中的“ruby移动图片小工具”指的是一个使用Ruby编程语言编写的实用程序,它的主要功能是整理和管理图片。这个小工具将帮助用户按照图片的拍摄日期自动将图片分类并移动到相应的目录下,这对于组织大量照片库或者...

    Ruby 基础语法 视频教程1

    在Ruby中,变量用于存储数据,并在程序的不同位置使用。Ruby有五种不同类型的变量: - **局部变量**(Local Variables):以小写字母或下划线开头,如`x = 10`。 - **实例变量**(Instance Variables):以`@`...

    Ruby 应用安装程序制作工具 Ocra.zip

    Ocra,全称为"One-Click Ruby Application",是专门为Ruby开发者设计的一个工具,它允许用户将Ruby源代码打包成独立的可执行文件,使得在没有Ruby环境的Windows系统上也能运行Ruby应用程序。这一特性对于分发和部署...

    Ruby-SQLite3Ruby绑定SQLite3嵌入式数据库

    总之,Ruby-SQLite3是一个强大且实用的工具,它允许Ruby开发者在没有额外服务器进程的情况下轻松处理数据存储。无论是在学习基础的数据库操作,还是在构建小型应用,甚至是进行原型开发,Ruby-SQLite3都是一个理想的...

    Ruby编程,Ruby编程,

    3. **动态类型系统**:Ruby采用动态类型系统,这意味着变量不需要显式声明类型,程序运行时会根据实际值自动确定类型。 4. **元编程能力**:Ruby允许开发者在运行时修改类的行为,这种灵活性使得Ruby非常适合于快速...

    Ruby-Configatron一个超级酷简单功能丰富的Ruby应用程序的配置系统

    Configatron就是一个为解决这一问题而设计的库,它提供了一个简单易用且功能丰富的框架,使得Ruby应用程序的配置工作变得轻松高效。 Configatron的核心理念是提供一个简洁的API,让开发者能够快速地定义和访问应用...

    Ruby-Money一个Ruby库来处理货币和货币转换

    这个库被广泛用于需要精确管理货币数据的应用程序,比如电子商务、财务系统或者任何涉及金融交易的项目。在Ruby社区中,它被视为一个非常实用且功能丰富的库,能够帮助开发者以一种更规范和安全的方式处理货币。 ...

    Ruby-mongorubydriverMongoDBRuby驱动程序

    Ruby-mongorubydriver是MongoDB官方支持的Ruby语言客户端驱动程序,用于连接和操作MongoDB数据库。这个驱动程序提供了一套丰富的API,允许开发者在Ruby应用中方便地执行各种数据库操作,包括文档的创建、读取、更新...

    2000个小程序精选源码(包含49个行业)

    │ │ 小契约(交友互动小程序).zip │ │ │ ├─企业展示(3个) │ │ 信息科技公司展示小程序.zip │ │ 华云智慧园区.zip │ │ 房地产公司展示.zip │ │ │ ├─企业应用(1个) │ │ 企业OA系统...

Global site tag (gtag.js) - Google Analytics