Почему последовательные массивы, собранные из ObjectSpace, не равны в моей спецификации?

У меня есть класс Project с двумя методами, связанными с ObjectSpace:

def self.all
  ObjectSpace.each_object(self).to_a
end

def self.count
  ObjectSpace.each_object(self).count
end

Эта спецификация не работает:

it "can print all projects" do
  Project.all.should eq([@project1, @project2])
end

со следующей ошибкой:

Failure/Error: Project.all.should eq([@project1, @project2])

       expected: [#<Project:0x007fd76a815508 @name="Building house", @tasks=[]>, #<Project:0x007fd76a815198 @name="Getting a loan from the Bank", @tasks=[]>]
            got: [#<Project:0x007fd7688336b8 @name="Building house", @tasks=[]>, #<Project:0x007fd7688dae40 @name="Building house", @tasks=[]>, #<Project:0x007fd768af4de8 @name="Getting a loan from the Bank", @tasks=[]>, #<Project:0x007fd768af5090 @name="Building house", @tasks=[]>, #<Project:0x007fd76a815198 @name="Getting a loan from the Bank", @tasks=[]>, #<Project:0x007fd76a815508 @name="Building house", @tasks=[]>]

Как видите, это дает мне удвоенные объекты в массиве, но сам код работает нормально. Так почему мой тест не пройден?

3 ответа

Потому что ранее существовавший Projectвсе еще существуют как объекты.

Это означает, что они все еще будут найдены в ObjectSpaceи у вас будет больше объектов, чем вы ожидаете.

project_spec.rb

describe Project do

 let(:p1) { Project.new }
 let(:p2) { Project.new }

 describe ".all" do

  it "should keep track of all pr" do
   Project.all.should == [p1, p2]  
  end

 end

 describe ".count" do

  it "should count all the projects" do
   Project.count.should == 2
  end

 end

end

project.rb

class Project

  @@all_projects = []

  def initialize(options=nil)
    @@all_projects << self
  end

  def self.all
    @@all_projects
  end

  def self.count
    @@all_projects.count
  end

end


Finished in 0.00079 seconds
2 examples, 0 failures

Я оставлю это на ваше усмотрение, чтобы проработать другие детали, которые могут быть специфическими и запутанными для вашего проекта. Надеюсь, это сработает для вас.

ObjectSpace может содержать следы орехов

Ну не совсем. Но ObjectSpace часто содержит объекты, принадлежащие другим областям, объекты, которые были помечены для сборки мусора, но еще не удалены, или (в случае тестов RSpec, в частности) копии объектов из нескольких вызовов блока before.

Ruby 2.0 может отличаться, но более ранние интерпретаторы MRI не дают гарантий относительно сборки мусора, поэтому вы не можете действительно рассчитывать на то, что содержимое ObjectSpace будет действительным для теста на равенство, даже если вы вручную запустите GC.start.

Рефакторинг вашего кода

Вместо того, чтобы искать равенство в вашей спецификации, вы можете рассмотреть:

  1. Ищете включение с Array # include?
  2. Поиск конкретных идентификаторов объектов в ObjectSpace.
  3. Рефакторинг тестируемого класса, а также самого теста. В частности, используйте какой-либо шаблон агрегации, а не полагайтесь на ObjectSpace для хранения ссылок на интересующие вас объекты Project.

Вы можете смешивать и сочетать здесь, но исправление теста - это только часть проблемы. Основная логика приложения нуждается в рефакторинге.

Пройденный тест хорош тем, что выделяет класс, который нуждается в операции. Не просто исправить тест; послушайте, что спецификация пытается рассказать вам о тестируемом классе.

Другие вопросы по тегам