Ruby中扩展模块及测试类(或实例)方法的技术问题
Hey there! Let's break down what's going wrong with your code and fix it step by step. I see you're trying to build a where method similar to ActiveRecord's, but hitting issues when extending the module into your test class.
问题根源
When you extend Where in your WhereTest class, you're turning the module's where method into a class method of WhereTest. But inside the where method, you call self.select — and WhereTest (a plain class) doesn't have a select method! The select method comes from the Enumerable module, which is included by objects like arrays, hashes, and other collection types, not regular classes.
That's why you're getting a NoMethodError when trying to run your tests. Let's fix this with two common approaches depending on what you want to achieve.
方案1:让所有可枚举对象都能用where(推荐)
If you want your where method to work with any collection (like arrays of hashes), modify your Where module to extend Enumerable so every enumerable object gets the method:
Where.rb
module Where def where(hash = {}) select do |fixture| hash.all? do |key, value| value === fixture[key] end end end end # Make the where method available to all Enumerable objects Enumerable.include(Where)
Then update your test class to use an array of fixtures (since arrays are enumerable) and test the method on that array:
test.rb
require 'minitest/autorun' require './where.rb' class WhereTest < Minitest::Test def setup @fixtures = [ {:name => 'Boris The Blade', :quote => "Heavy is good. Heavy is reliable."}, {:name => 'Franky Four Fingers', :quote => "Don't be a cunt, Harry."}, {:name => 'Bullet Tooth Tony', :quote => "You should never underestimate the predictability of stupidity."} ] end def test_where_matches_exact_name result = @fixtures.where(name: 'Boris The Blade') assert_equal 1, result.size assert_equal "Heavy is good. Heavy is reliable.", result.first[:quote] end def test_where_matches_with_regex result = @fixtures.where(name: /Tony/) assert_equal 1, result.size assert_equal "Bullet Tooth Tony", result.first[:name] end end
方案2:让测试类本身拥有where方法
If you specifically want the WhereTest class to have a class-level where method that operates on class-specific data, you'll need to define a data source in the class and adjust the module to use it:
Where.rb
module Where def where(hash = {}) # Assume the extending class has a @@fixtures class variable (an enumerable collection) @@fixtures.select do |fixture| hash.all? do |key, value| value === fixture[key] end end end end
test.rb
require 'minitest/autorun' require './where.rb' class WhereTest < Minitest::Test extend Where # Define class-level fixtures for the where method to operate on @@fixtures = [ {:name => 'Boris The Blade', :quote => "Heavy is good. Heavy is reliable."}, {:name => 'Franky Four Fingers', :quote => "Don't be a cunt, Harry."} ] def test_class_level_where result = WhereTest.where(name: 'Franky Four Fingers') assert_equal 1, result.size assert_equal "Don't be a cunt, Harry.", result.first[:quote] end end
额外小提示
- Remember the difference between
includeandextend:includeadds module methods as instance methods to a class/object.extendadds module methods as class methods to a class.
- Your use of
value === fixture[key]is smart! This lets you match not just exact values, but also regex patterns, ranges, and even classes (e.g.,value: Stringwould match any string value for that key).
内容的提问来源于stack exchange,提问作者Warren Mui

