Почему последовательные массивы, собранные из 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.
Рефакторинг вашего кода
Вместо того, чтобы искать равенство в вашей спецификации, вы можете рассмотреть:
- Ищете включение с Array # include?
- Поиск конкретных идентификаторов объектов в ObjectSpace.
- Рефакторинг тестируемого класса, а также самого теста. В частности, используйте какой-либо шаблон агрегации, а не полагайтесь на ObjectSpace для хранения ссылок на интересующие вас объекты Project.
Вы можете смешивать и сочетать здесь, но исправление теста - это только часть проблемы. Основная логика приложения нуждается в рефакторинге.
Пройденный тест хорош тем, что выделяет класс, который нуждается в операции. Не просто исправить тест; послушайте, что спецификация пытается рассказать вам о тестируемом классе.