数据比较小程序(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)来查找符合条件的记录。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值