2009-12-15 2 views

ответ

3

посмотреть на http://blog.ardes.com/2006/12/11/testing-singletons-with-ruby: "? Глобальные переменные плохо, m'kay"

require 'singleton' 

class <<Singleton 
    def included_with_reset(klass) 
    included_without_reset(klass) 
    class <<klass 
     def reset_instance 
     Singleton.send :__init__, self 
     self 
     end 
    end 
    end 
    alias_method :included_without_reset, :included 
    alias_method :included, :included_with_reset 
end 
+0

Проблема с этим - глобальное состояние изменяется при выполнении ваших тестов. См. Мой ответ ниже для более чистого способа. –

0

Допускают ли RSpec вы выполнить предтестовое действие? Итак, вы можете добавить к вам еще один метод статического класса, который очистил бы все, что было сделано во время конструктора. Затем просто вызовите это до каждого теста.

+0

RSpec имеет блоки «до» и «после». http://rspec.info/documentation/before_and_after.html –

+1

Но конструктор не будет вызываться дважды из-за шаблона Singleton. – brainfck

1

Рефакторинг в класс, который можно построить несколько раз. У этого есть побочный эффект (кто-то сказал бы выгоду) об удалении природы Singleton из класса.

Посмотрите на это по-другому: вы обнаружили необходимость вызова конструктора более одного раза. Почему должен класс только построить один экземпляр? Какая польза от предоставления Singleton?

+0

Конструктор не требует вызова более одного раза, но я хочу, чтобы мои спецификации были ясными - поэтому я тестирую только одну часть конструктора в каждой спецификации. – brainfck

-1

Вы можете просто создать новый it и блокировать для каждой спецификации. Разделите свою спецификацию на тестируемый блок. Используйте «до» и «после», чтобы настроить и очистить все, что вы сделали.

before(:each) и after(:each) выполнены для каждого блока it.

2

Одна из причин, люди используют одиночка, потому что Синглтон - это глобальная переменная, секвестрированная в пространстве имен и с ленивым экземпляром. Подумайте, может ли истинная глобальная переменная упростить вещи, особенно если вам не нужна ленивая инстанция.

3

Один узор, который я видел, имеет одноэлементный подкласс класса. Вы используете версию Singleton в производственном коде, но базовый (не одиночный) класс для тестирования.

Пример:

class MyClass 
    attr_accessor :some_state 

    def initialize 
    @some_state = {} 
    end 
end 

class MySingletonClass < MyClass 
    include Singleton 
end 

... но я ищу лучший способ себя.

Часть моей проблемы заключается в том, что я использую JRuby и подключаюсь к настройкам Java-системы, которые являются глобальными. Остальное, я думаю, я могу реорганизовать.

7
# singleton_spec.rb 
require "singleton" 

class Global 
    include Singleton 

    def initialize 
    puts "Initializing" 
    end 
end 

describe Global do 
    before do 
    Singleton.__init__(Global) 
    end 

    it "test1" do 
    Global.instance 
    end 

    it "test2" do 
    Global.instance 
    end 
end 


% rspec singleton_spec.rb -fd 
Global 
Initializing 
    test1 
Initializing 
    test2 
+0

Ключ «Singleton .__ init __ (Global)». Это прибило его для меня. Спасибо. – iHiD

+0

Проблема в том, что это сбрасывает синглтон. Если какие-либо другие части вашего приложения опираются на глобальное состояние, которое было установлено ранее, это сломается. Например, если вы используете шаблон реестра, то как вы можете протестировать реестр, не нарушая тесты для компонентов, использующих реестр? –

14

классы Singleton в основном это сделать

def self.instance 
    @instance ||= new 
end 

private_class_method :new 

Таким образом, вы можете обойти запоминанием вообще, вызвав частный метод новый, используя отправить

let(:instance) { GlobalClass.send(:new) } 

Хорошая выгода этого способа является то, что нет глобальное состояние изменяется в результате выполнения ваших тестов.


Вероятно, лучший путь, от this answer:

let(:instance) { Class.new(GlobalClass).instance } 

Это создает анонимный класс, который наследует от GlobalClass, что все переменные экземпляра класса уровня хранятся в этом затем выбрасываются после каждого. тест, оставив GlobalClass нетронутым.

+0

Это простое, прямое решение. –

+0

Спасибо, это было очень полезно. – chrisco

Смежные вопросы