Spec RSpec модель атрибута setter - PullRequest
5 голосов
/ 09 апреля 2011

Я использую Sinatra (1.2) и RSpec (2.5) и хотел бы создать новый объект с атрибутом стиля TDD.Вот как должен выглядеть конечный результат:

class User
  def initialize(name)
    @name = name
  end
end

Я знаю, что должен написать пример до реализации, но я пытаюсь объяснить свой вопрос здесь.:) Вот неработающая спецификация, которую я имею до сих пор:

describe User
  it "creates a new user object" do
    name = mock("A name")
    user = mock(User) # shouldn't do this, see the reply's
    user.should_receive(:name=).with(name)
    User.new(name)
  end
end

Когда я запускаю RSpec, я получаю ошибку «Ожидается: 1 раз, получено 0 раз».Любая идея, как я могу объяснить RSpec, я хотел бы назначить атрибут имени?

Примечание: я не использую Rails, не использую ActiveRecord или что-нибудь, только Ruby.

Ответы [ 3 ]

12 голосов
/ 09 апреля 2011

Прежде всего, позвольте мне объяснить, почему написанная вами спецификация не работает:

Вы устанавливаете ожидание, что фиктивный объект, возвращаемый mock(User), должен получить name=. Есть две проблемы с этим. Прежде всего макет ничего не получит, потому что он никогда не называется. mock(User) возвращает фиктивный объект, и его нельзя использовать для установки ожиданий того, что получит объект класса User (для этого просто выполните User.should_receive(...)). Во-вторых, даже если вы установили ожидание для объекта класса User, этот объект никогда не получит name=. Для этого тоже есть две причины: во-первых, потому что name= (если бы он существовал) был бы методом экземпляра, и как таковой он не вызывался бы для объекта класса, и во-вторых, вы не объявляли метод экземпляра name=. Ваш код делает то, что он устанавливает переменную экземпляра.

Теперь, как вы должны написать тест для этого? Ты не должен. Тесты должны определять и утверждать поведение, а не реализацию. Установка переменной экземпляра является чистой реализацией. В вашем примере кода нет способа получить значение переменной экземпляра @name извне класса, поэтому нет смысла писать для него тест.

Очевидно, что ваш код - всего лишь пример, все что угодно может сделать что-то с переменной @name, и это то, что вы должны проверить. Начните с написания теста, для которого будет использоваться объект User, затем запишите всю реализацию, необходимую для выполнения этого теста (но не более). Напишите тест, который отражает, как объект будет использоваться в реальном производственном коде.

5 голосов
/ 09 апреля 2011

Я бы порекомендовал вам не подходить к этому с помощью насмешек.Это не то, для чего они.На самом деле, указание таких методов получения / установки не совсем то, для чего предназначен TDD.Идея состоит в том, чтобы позволить требованию приводить в действие установщики / получатели.Например, может существовать требование, чтобы имя пользователя отображалось в приветственном сообщении при входе в систему. Затем вы можете сделать что-то вроде этого:

describe 'login process' do
  it "displays user's name after successful login" do
    user = User.new("Cimm", "cimm@somewhere.com", "secret")
    post "/login", :email => "cimm@somewhere.com", :password => "secret"
    last_response.body.should =~ /Welcome Cimm/m
  end
end

Это определяет поведение и вынуждает вас реализоватьсредство установки атрибута имени (в данном случае через конструктор) и средство доступа к нему.Нет необходимости указывать конструктор напрямую.

1 голос
/ 09 апреля 2011

Вы действительно хотите издеваться над самим объектом, который вы разрабатываете?

require 'rspec'

class User
  attr_accessor :name

  def initialize(name)
    @name = name
  end 
end

describe User do
  subject {User.new "other name"}

  it "creates a new user object" do
    subject.should respond_to :name=
  end 
end
...