RSpec Tutorial: Введение
сентября 18, 2011 | Published in BDD, RSpec, Тестирование | 14 Comments
UPD: Добавлен параграф об around() хуке.
Доброго времени суток ув. читатель RubyDev’а!
В данный момент автор RubyDev, то есть я, очень похож на белку в колесе ибо он занимается устройством на работу. К сожалению или к великому счастью обязательным условием является умение работать с RSpec, которым автор владеет в совершенстве плохо, по сему, решил я бросить все ресурсы на то, чтобы изучить сие твоение Девида Челимски.
Данную статья пишу я не от того, что вдруг появилось свободное время, но потому, что это единственный способ для меня самого хорошо освоить материал, текст статьи я как-бы пишу сам для себя, объясняя себе то, что только что прочитал в том малом, что мне удалось, словно золото из золотой жилы, выбить да вышкребать, в интернетах.
Ибо время — деньги, а платить мне никто за это старание не будет, буду совсем краток.
RSpec — это фреймворк для тестирования написанный на Ruby и предоставляющий специальный DSL (вы уже должны знать, что это) для написания тестов — спецификаций. RSpec — инструмент для BDD. BDD (Bihavior-Driven Development) — разработка с опережающим описанием поведения программы. Другими словами сначала мы описываем в спецификации то, как должна себя вести программа, а затем пишем саму программу. Спецификации при этом используются не просто как описание «Вот эта штука должна напечатать: Hello, World!», но и как тест, проверка того, что программа выведет именно «Hello, World!», а не что-то похабное и нецензурное.
В этой статейке или статеночке мы сконцентрируемся на работе с самим RSpec и совсем не будем затрагивать различные вспомогательные штуки-дрюки типа factory-girl и т.д. ибо я сам еще не сильно во всем разобрался.
Итак, начинаем с установки:
$ gem install rspec
Fetching: rspec-core-2.6.4.gem (100%)
Fetching: diff-lcs-1.1.3.gem (100%)
Fetching: rspec-expectations-2.6.0.gem (100%)
Fetching: rspec-mocks-2.6.0.gem (100%)
Fetching: rspec-2.6.0.gem (100%)
Successfully installed rspec-core-2.6.4
Successfully installed diff-lcs-1.1.3
Successfully installed rspec-expectations-2.6.0
Successfully installed rspec-mocks-2.6.0
Successfully installed rspec-2.6.0
5 gems installed
Кратко о том, что мы установили:
rspec-core — собственно ядро RSpec
rspec-expectations — добавляет expectations(далее по тексту просто «ожидания») и matchers(назовем из «матчеры»).
rspec-mocks — добавляет возможность делать mocking и stubbing (назовем «мокинг» и «стубинг»).
Теперь расскажу кратко о том, что такое метчеры и ожидания.
Ожидания — это методы should и should_not, которые добавляются к некоторому объекту поведение которого мы описываем.
Матчер используется описания и проверки поведения объэкта.
Пример:
Строка «RubyDev» должна содержать 7 символов.
Здесь «RubyDev» — тестируемый объект, должна — ожидание, содержать 7 символов — матчер.
Другой пример:
Строка «RubyDev» не должна содержать 5 символов.
Здесь ожиданием будет «не должна».
А теперь посмотрим на то, как это все будет выглядеть если мы используем RSpec:
string = "RubyDev" string.should have(7).characters string.should_not have(5).characters
Теперь следует увеличить масштам и взглянуть на спецификацию целиком!
Спецификация (spec) — это, как правило, отдельный файл, который содержит описание какой-нибудь части программы, в контексте Rails, это может быть описание целого контроллера, модели, шаблона, партиала, хелпера и т.д. Файлы спецификаций принято хранить в поддиректории spec проекта, а имена файлов должны заканчиваться на _spec.rb.
Сейчас мы не будем заострять внимание на написании спеков для Rails-приложения ибо это уже несколько отдельная тема. Вместо этого мы быстро и стремительно на нашем рубиновом дельтоплане пролетим по азам работы с RSpec.
Давайте создадим простое приложение, которое познакомит нас с ожиданиями и матчерами. Создадим директорию проекта proj1 и два файла user.rb и account.rb.
#user.rb require './account' class User end
#account require './user' class Account end
Теперь мы должны создать директорию spec в нашем проекте, где будем хранить спецификаци (спеки), а в ней две спецификации: user_spec.rb и account_spec.rb.
Наше простое приложение — какая-то система интернет-банкинга. Пользователь может иметь один аккаунт, а аккаунт может принадлежать только одному пользователю. Давайте опишем это в наших спецификациях:
require "./user.rb" require "./account.rb" require 'rspec' describe User do before(:all) do @user = User.new("Vasya", "Ivanov", 18) @account = @user.account end it 'has a fullname' do @user.fullname.should eq "Vasya" + " " + "Ivanov" end it 'is older then 18' do User.new("Ivan","Ivanov", 10).should raise_error() end it 'has an account' do @account.user.should be @user @user.account.should be @account end end
#../spec/account_spec.rb require "./account.rb" require "./user.rb" require 'rspec' describe Account do before(:all) do @user = User.new("Vasya", "Ivanov", 18) @account = @user.account end it 'belongs to the user' do @account.user.should be(@user) end it 'has list of accounts' do Account.all.should include(@account) end it "should has an ID" do @account.id.should_not be_false end it "has a balance in the start" do @account.balance.should be 0 end end
Для запуска спецификаций воспользуемся командой rspec <filepath>:
$ rspec ./spec/user_spec.rb
FFF
Failures:
1) User has a fullname
Failure/Error: @user.has_account
NoMethodError:
undefined method `has_account’ for #<User:0x8b1d458>
# ./spec/user_spec.rb:7:in `block (2 levels) in <top (required)>’
2) User is older then 18
Failure/Error: @user.has_account
NoMethodError:
undefined method `has_account’ for #<User:0x8b1d458>
# ./spec/user_spec.rb:7:in `block (2 levels) in <top (required)>’
3) User has an account
Failure/Error: @user.has_account
NoMethodError:
undefined method `has_account’ for #<User:0x8b1d458>
# ./spec/user_spec.rb:7:in `block (2 levels) in <top (required)>’
Finished in 0.00033 seconds
3 examples, 3 failures
Failed examples:
rspec ./spec/user_spec.rb:11 # User has a fullname
rspec ./spec/user_spec.rb:15 # User is older then 18
rspec ./spec/user_spec.rb:19 # User has an account
Как видите, RSpec сообщает, что код не соответствует спецификации, в нашем случае причиной тому является то, что классы User и Account пусты. Для того, чтобы тестирование прошло успешно, нам необходимо реализовать в классах User и Account то поведение, которое мы описали в спецификациях.
#user.rb require './account' class User attr_reader :name, :lname, :fullname, :age, :account def initialize(name, lname, user_age) @name = name @lname = lname @fullname = name + " " + lname self.age = user_age @account = Account.new(self) end def age=(user_age) if user_age >= 18 @age = user_age else raise "So young!" end rescue RuntimeError => error return error.message end end
#account.rb require './user' class Account attr_reader :user, :id attr_accessor :balance def initialize(user) @user = user @balance = 0 self.class.add_account(self) @id = self.set_id end @@accounts = [] def set_id prev_account_id = self.class.all.last.id @id = prev_account_id ? prev_account + 1 : 1 end def self.all @@accounts end def self.add_account(account) @@accounts << account end end
Теперь, если запустить rspec, то мы увидим, что наше приложние согласно спецификациям работает правильно:
$ rspec ./spec/account_spec.rb
….
Finished in 0.00361 seconds
4 examples, 0 failures
$ rspec ./spec/user_spec.rb
…
Finished in 0.00174 seconds
3 examples, 0 failures
Теперь, когда мы научились писать простые спеки, необходимо разобраться что же мы в них писали =)
describe … do
…
end
или
context … do
…
end
- это синонимы. В этих блоках определяется описание какой-нибудь функциональной части приложения. Например:
describe ArticlesController do
describe «#index» do
…
end
…
end
Как видите, эти блоки могут быть вложенными, глубина вложенности может быть абсолютно любая.
Следующее, что нам следует разобрать — это examples или, если перевести на русский — примеры. Примеры — это те самые блоки it … { … }. Каждый такой пример описывает поведение какой-нибудь функциональной единицы, например метода. Иногда для описания функции метода или функциональности цепочки методов приходится использовать несколько примеров.
Еще нам следует разобраться с тем, что такое pending examples или отложенные примеры (точнее незаконченные, висящие или те, реализацию которых мы ожидаем в будующем). Мы не использовали эту возможность в примере выше, однако о ней вам необходимо знать уже сейчас. Спецификация не всегда проверяет код, иногда программист нужно добавить какой-то пример для функциональности, которая совсем не скоро будет реализовано, но пример должен быть, в такой способ мы как-бы помечаем то, что нам нужно сделать в будующем. Такие пустые примеры и называются pindings. Простой пример pending:
describe "do something" do it "do something" end
При запуске спецификации в отчете будет записано:
Do something
do something (PENDING: Not Yet Implemented)
Pending:
Account Do something do something
# Not Yet Implemented
# ./spec/account_spec.rb:4
Finished in 0.00278 seconds
1 examples, 0 failures, 1 pending
Вы также можете использовать специальный метод pending в контексте блока it для описания причины почему пример временно отложен:
describe "Do something" do it "do something" do pending "Some bugs needs to be fixed." end end
$ rspec spec/account_spec.rb —format documentation —color
Do something
do something (PENDING: Some bugs needs to be fixed.)
Pending:
Account Do something do something
# Some bugs needs to be fixed.
# ./spec/account_spec.rb:4
Finished in 0.00299 seconds
1 examples, 0 failures, 1 pending
Вы также можете использоовать xit вместо it:
describe "Do something" do xit "do something" do (1).should be_true end end
Первый вариант хорош когда вам не нужно указавать причины того, почему пример не реализован. Второй, когда необходимо указать, причину того, почему пример находится в ожидании. А третий подходит когда не нужно показывать причину того, что пример отложен и при этом содержимое блока примера готово, однако, из-за некоторых багов или нереализованности каких-то других частей программы, пример не может сработать успешно. Когда баги будут исправлены и нужный функцинал дописан, то вам достаточно просто убрать «х» из «xit» и пример станет рабочим.
В примере выше (в том, где мы писали преложиние с аккаунтами и пользователями) мы использовали следующие матчеры:
be — (obj.should be) срабатывает если obj true или любое другое значение кроме nil и false. Второй вариант (obj.should be 5) — срабатывает когда obj.equal? 5.
be_false — (obj.should be_false) — срабатывает если obj == nil или obj == false.
include — ([1,2,3].should include 2) — cрабатывает если коллекция содержит элемент, этот матчер похож на метод include?.
raise_error — (obj.do_something.raise_error) — данный матчер срабатывает когда вызывается ошибка, тип ошибки и сообщение можно передать в условиях матчеру.
eq (3.should eq 3) — данный матчер срабатывает, например когда obj1 == obj2.
Но это не все матчеры! Далее мы познакомимся с остальными матчерами, однако прежде мы вспомним различия между .eql? .equal? и ==, это очень важно для того, чтобы вы понимали разницу между некоторыми матчерами и правильно их использовали.
== — проверяет соответствие значений двух объектов:
1.0 == 1 #=> true
.eql? — проверяет соответствие значений двух объектов и их тип:
(1.0).eql? 1 #=> false (1).eql? 1 #=> true
.equal? — проверяет являются ли A и B одним объектом (имеют одинаковый .object_id):
1.equal? 1 #=> true :rubydev.equal? :rubydev #=> true "rubydev".equal? "rubydev" #=> false obj = "rubydev" #=> "rubydev" obj2 = obj obj.equal? obj2 #=> true
Так, с этим разобрались, перейдем к знакомству с остальными матчерами:
Матчеры: equal, eql и == соответствуют методам сравнения описанным выше.
Матчер eq соответствует матчеру ==.
Матчер be(obj) соответствует матчеру equal.
Be-матчеры:
О матчере be и be_false я уже говорил.
be_true — срабатывает если объект конвертируется в true, то есть его значение не nil и не false.
be_nil — срабатывает если значение объекта nil.
be_within(delta).of(value) — срабатывает если значение объекта находится в пределах value +- delta. Пример:
10.should be_within(5).of(11), данный пример сработает так как 10 попадает в диапазон от 7 до 15 включительно.
Матчер be также можно использовать совместно с операторами сравнени, например:
100.should be > 1 100.should be >= 99 100.should be < 101
Матчер expect change очень полезен благодаря тому, что применяется к блоку кода и позволяет оценить изменение состояния объекта. Это может быть полезно, например для проверки правильности работы counter cache при работе с Rails. Пример:
obj = 10 #проверяем, что значение объекта изменилось с 10 на 110 expect{obj += 100}.to change{obj}.from(10).to(110) #проверяем, что значение объекта увеличилось на 100 expect{obj += 100}.to change{obj}.by(100)
Матчер raise_error или raise_exception срабатывает когда возвращается ошибка. Синтаксис: raise_error(Error, Message). Error — тип ошибки, а Message — сообщение. Без этих параметров матчер будет срабатывать для любой ошибки. Пример:
expect{nil + 1}.to raise_error(NoMethodError)
Have-матчеры используются для проверки количества элементов в коллекции. Существуют следующие have-матчеры:
have (have_exactly) — объект должен иметь точно определенное количество элементов.
have_at_least — объект должен имет количество элементов больше или равно, чем указано.
have_at_most — объект должен иметь количество элементов меньше или равное указанному.
#строка "rubydev" должна иметь 7 символов "rubydev".should have(7).characters #строка должна содержать не более 8 каких-то штук "rubydev".should have_at_most(8).items #массив должен содержать не менее 2 слов ["hello","bye"].should have_at_least(2).words #массив должен иметь не менее 1 элемента ["hello","bye"].should have_at_least(1).items
Приставка после have, может быть, почти любой, какую вы пожелаете, только я бы советовал вам добавлять осмысленные приставки (items, words, elements и т.д.).
Матчер include мы уже рассматривали выше, однако следует рассмотреть его более подробно. Ниже приведены примеры объясняющие работу матчера include:
"chicken".should include("c") [1,2,3].should include(1) [1,2,3].should include(1,2,3) hash = {:name => "Vasya", :lname => "Petrov"} hash.should include(:name) hash.should include(:name => "Vasya") hash.should_not include(:name => "Vova")
Матчер match (простите за тафтологию) используется для проверки соответствия значения объекта шаблону регулярных выражений. Пример:
"James Bond".should match "Bond" "James Bond".should match /Bond/ "James Bond".should =~ /Bond/ "James Bond".should match "(Bond)$" "James Bond".should_not match /^(Bond)/
Матчер =~ является синонимом для матчера match, однако может также использоваться для сравнения массивов:
[1,2,3].should =~ [1,2,3] [1,2,3].should =~ [3,2,1] [1,2,3].should_not =~ [1,2]
RSpec автоматически создает матчеры для методов, имена которых заканчиваются символом «?», то есть таких, что возвращают булево значение. Мы можем делать так:
5.zero?.should be_false
Однако этот код не соответстует философии RSpec согластно которой язык описания спецификаций должен быть максимально похож на человеческий. Таким образом RSpec предоставляет нам специальные матчеры для написания читабельных спеков:
5.should_not be_zero [].should be_empty class A def self.good? true end end
A.should be_good
Матчер respond_to используется для проверки того, отвечает ли объект на вызов метода для него, то есть, содержит ли объект определенный метод. Пример:
class A def self.good? true end end
A.should respond_to("good?") "rubydev".should respond_to(:upcase,:downcase)
Матчер satisfy — очень гибок и позволяет проверить практически все, что угодно. satisfy передает объект в блок, где над объектом выполняются определенные действия, если блог возвращает true, то матчер проходит, если false, то не проходит. Примеры:
"rubydev".should satisfy{|word| word.length > 5 } {:q=>10}.should satisfy{|h| h.has_key?(:q)} [1,2,3].should satisfy{|arr| arr.should include 2 }
Матчер throw_symbol используется для проверки правильности работы с ошибками, а точнее с catch и throw, а еще точнее с throw, с тем, какой блок кода исполняется для обработки ошибки (блоки именуются при помощи символов, при определенном условии throw выполняет определенный блок кода, который определен через catch(:symbol)):
expect{throw :foo}.to throw_symbol(:foo)
Матчеры проверяющие тип объекта be_a_kind_of и be_an_instance_of. Примеры:
:symbol.should be_a_kind_of Symbol "string".should be_an_instance_of String
class A ... end
A.new.should be_a_kind_of A
Матчер cover используется для проверки вхождения объекта в диапазон, например:
(1..4).should cover(4) (1..4).should cover(1,2,3,4) (1...4).should_not cover(4, 10) ('a'..'z').should cover("x")
Вот мы и рассмотрели, наверное, все стандартные матчеры.
Остается узнать что такое before(:all) do … end и т.д.
before и after — это так называемые хуки, которые позволяют избегать повторения в коде примеров и, кроме того, реализовывают инициализацию необходимых объектов. В нашем примере спеков мы можем найти следующий пример использования:
before(:all) do @user = User.new("Vasya", "Ivanov", 18) @account = @user.account end
Данный хук выполняется один раз перез запуском всех примеров (параметр :all) и устанавливает объекты @user и @account, которые далее используются в примерах.
Если в параметрах к before передать :each, то блок кода будет выполняться перед каждым примером, то есть столько раз, сколько примеров содержится в спецификации.
Кроме before(:each) или before(:all) имеется также хук after(:each) или after(:all), который выполняется соответственно после каждого примера или после выполнения всех примеров.
Cуществует также around() хук. around принимает блок кода (пример) и позволяют в своем собственном блоке кода определить действий которые будут выполнятся до и после вызова процедуры в которую был преобразован принятый блок кода примера. Пример:
RSpec.configure do |config| config.around(:each) do |example| @var = 100 example.call #or run @var = 0 end end it("is 100") { @var.should be 100 }
Также вам следует знать о том, что describe и it имеют синонимы: context и specify соответственно.
describe … do
it … {…}
context … do
it … {…}
specify…{…}
end
end
Работая с хуками типа before и after вы должны знать, что хуки внешнего описания (контекста) доступны во всех вложенных контекстах не завасимо от глубины вложенности.
Вы также можете оределять фильтры для хуков в конфигурации для спецификации. Фильтры позволяют хукам before и after выполняться только для определенных примеров. Ниже показан пример использования фильтров:
RSpec.configure do |config| config.before(:each) do @var ||= 0 end end config.before(:each, :turn => :second) do @var += 1 end config.before(:each, :turn => :third) do @var += 5 end it "should be 0", :turn => :first do @var.should be 0 end it("should be 1", :turn => :second) do @var.should be 1 end it "should be 5", :turn => :third do @var.should be 5 end it "should be 1", :turn => :second do @var.should be 1 end
Кроме хуков в RSpec присутствуют методы — хэлперы и возможность определять их самостоятельно.
Методы let и let! используются для создания кэшируемых хэлперов в RSpec. let и let! позволяют создать хелперы, которые в контексте каждого примера не изменяются, однако в контексте другого примера могут меняться. Пример:
let(:balance){ @account.balance += 10 } it "should be 10" do balance.should be 10 end it "should be 20" do balance.should be 20 end
Разница между let и let! заключается в том, что хэлпер объявленный через let выполняется только в контексте примеров. Если вам необходимо использовать хэлпер вне примеров, например в before — хуке, то вам следует использовать let!.
Вы также можете создавать методы-хэлперы так же как и обычные методы в Ruby. Пример:
def get_balance "You have " + @account.balance.to_s + " dollars." end it "should return message dollars." do get_balance.should == "You have 20 dollars." end
Такие хэлперы доступны только в контексте своего описания (describe или context) и во всех вложенных контекстах.
Теперь давайте перейдем к объект спецификации. Эта тема, наверное, должна была быть в начале, но я на свой страх и риск перенес ее в конец.
Каждое описание (контекст) или пример имеют объект, который они опысывают (субъект описания). Когда мы пишем:
describe Account do
…
end
Мы не просто даем понять, что мы описываем класс Account, но мы еще и создаем экземпляр этого класса (объект типа Account), доступ к которому можно получить через метод subject. Таким образом, в нашем приложении мы можем описывать поведение аккаунта пользователя следующим образом:
describe Account do it 'is an instance of Account' do subject.should.be_an_instance_of Account subect.should be_a_kind_of Account end end
В нашем случае такая спецификация вызовет ошибку, ибо инициализатор должен получить один очень важный параметр — пользователя аккаунта. Таким образом мы должны переписать спеку в такой способ:
describe Account.new(User.new("Vasya", "Pupkin", 23)) do it 'is an instance of Account' do subject.should be_an_instance_of Account subject.should be_a_kind_of Account end it 'has user with name Vasya' do subject.user.name.should == "Vasya" end end
Теперь все заработает отлично.
Такие субъекты спецификации как приведено выше называются в RSpec неявными (или предопределенными), а теперь мы поговорим о явных субъектах (определяемых вручную пользователем). Эти субъекты определяются при помощи метода subject, пример:
describe Account do subject { Account.new(User.new("Vasya", "Pupkin", 23)) } it 'is an instance of Account' do subject.should be_an_instance_of Account subject.should be_a_kind_of Account end it 'has user with name Vasya' do subject.user.name.should == "Vasya" end end
subject принимает блок кода в котором и определяется субъект спецификации.
Для работы с субъектами специфиации RSpec предоставляет специальный метод its, который является сокражением для использования метода it. its получает в виде стороки или символа имя метода, который следует вызвать для объекта — субъекта тестирования и блок кода в котором находится матчер. Пример:
#перепишим предыдущий пример с использованием its: describe Account subject { Account.new(User.new("Vasya", "Pupkin", 23)) } its(:class) { should equal Account } its("user.name") { should == "Vasya" } end
В случае, когда субъект — массив или хэш, вы можете передавть не только строку или символ, но и массив ключей, например:
describe Account #... describe 'accounts list' do subject { Account.all } #.all возвращает коллекцию аккаунтов, здесь мы проверяем, действительно ли первый элемент коллекции является аккаунтом. its([0]) { should be_an_instance_of Account } end end
Хочу заметить, что в случае с примерами it, should можно вызывать и без объекта-приемника вызова метода, в этом случае объектом-приемником будет служить явно или неявно определенный субъект спецификации. Пример:
describe Account do #... subject { Account.new(User.new("Vasya", "Pupkin", 23)) } it "is an instance of Account" do should be_a_kind_of Account end end
С этим все понятно!
Мы знаем, что описывая класс через describe или context мы получаем ссылку на экземпляр этого класса через метод subject. Что делать, если мы хотим описать поведение самого класса, а не его объекта, использовать константу — имя класса, например Account или User? — Нет, для этого RSpec предоставляет специальный метод described_class. Пример:
describe Account do it "is equal for Account class" do described_class.should equal Account end end
Для доступа к объекту примера и его метаданным в контексте примера вы можете использовать метод example:
describe Account do it "is equal for Account class" do described_class.should equal Account example.description.should == "is equal for Account class" end end
Давайте теперь воспользуемся нашими знаниями и перепишем спецификации в такой способ, чтобы они выглядели красивее и понятнее.
#account_spec.rb require "./account.rb" require "./user.rb" require 'rspec' describe Account do before(:all) do @user = User.new("Vasya", "Ivanov", 18) @account = @user.account end it 'has list of accounts' do described_class.all.should include(@account) end describe "user account" do it 'belongs to the user' do @account.user.should be @user end it "has an ID" do @account.id.should_not be_false end describe 'account balance' do let(:balance){ @account.balance += 100 } it "has a 0 dollars on balance in the start" do @account.balance.should be 0 end it "will be increased when user add money" do balance.should eq 100 end end end end
$ rspec spec/account_spec.rb —format documentation
Account
has list of accounts
user account
belongs to the user
has an ID
account balance
has a 0 dollars on balance in the start
will be increased when user add money
Finished in 0.00288 seconds
5 examples, 0 failures
#user_spec.rb require "./user.rb" require "./account.rb" require 'rspec' describe User do it 'is older then 18' do described_class.new("Ivan","Ivanov", 10).should raise_error() end describe 'user properties and abilities' do subject { described_class.new("Vasya", "Ivanov", 18) } it 'has a fullname' do subject.fullname.should eq "Vasya " + "Ivanov" end it 'has an account' do subject.account.should be end end end
$ rspec spec/user_spec.rb —format documentation
User
is older then 18
user properties and abilities
has a fullname
has an account
Finished in 0.00139 seconds
3 examples, 0 failures
Следующая статья будет более практическая, в ней мы допишем наше приложение и познакомимся с различными вспомогательными инструментами для RSpec типа autotest, и т.д. А также узнаем о том, что такое mocking и stubbing, как настраивать RSpec и т.д.
Лучшая благодарность автору — ваши комментарии!
сентября 18, 2011 at 23:17 (#)
Продолжайте в том же духе, интересно. Правда без описания дополнительных гемов обойтись трудно, думаю в будущем надо затронуть тему factory_girl/fabrication, faker, capybara…
сентября 18, 2011 at 23:50 (#)
Человечище!
сентября 19, 2011 at 07:52 (#)
С удовольствием прочел, некоторые вещи стали понятнее для меня. Вот только ошибки в статье не радуют, а их очень много.
Еще не понятно это:
describe … do
…
end
или
content … do
…
end
Так content или context? Если context, то этот момент объясняется в статье 2 раза (Также вам следует знать о том, что describe и it имеют синонимы: context и specify соответственно.).
Спасибо за статью, очень кстати для меня сейчас (очередной приступ энтузиазма у меня).
сентября 19, 2011 at 08:30 (#)
Да, хорошо бы ещё несколько статей на эту тему…. никак не заставлю себя писать тесты. Где-то внутри понимаю, а не опыт предыдущих лет не понимает и противится (-:
сентября 19, 2011 at 12:35 (#)
int03e, не без того будет. Сейчас как раз разбираюсь с FactoryGirl, но что-то писать еще рано.
Andy, ошибок много ибо сильно спешу и не владею слепым набором. Правильный вариант — context, content — это моя ошибка.
none, статей по RSpec будет несколько, просто я еще сам не во всем разобрался. В следующей расскажу о настройках и конфигурации, рассмотрю несколько вспомогательных инструментов, mock, stub, допишу приложение и спеки к нему, ну чисто ради закрепления понимания работы с RSpec. Через одну статью планирую уже писать о спеках для Rails, там же место для FactoryGirl, фикстур и прочего найдется.
Потом, когда азы опишу буду оттачивать технику, изучать спеки различных проектов и писать уже о том как писать спеки наиболее правильным образом.
сентября 19, 2011 at 14:53 (#)
А про «огурец» ожидать статей? (-:
Если надо, могу подкинуть «The RSpec Book», если ещё нету (-:
сентября 19, 2011 at 17:04 (#)
none, скорее нет, чем да. Cucumber мне кажется бесполезным, я у кого ни спрошу — никто его не использует и все хаят. Если и панишу что-то то совсем поверхностно.
сентября 24, 2011 at 00:17 (#)
Лучше использовать watchr вместо autotest и его аналог autowatchr.
it ‘has an account’ do
@account.user.should be @user
@user.account.should be @account
end
вот так писать не надо, одно ожидание-одна проверка, так как под описание попадает вторая строка, а первая гласит что «акаунт имеет юзера». не стоит перепроверять вещи по два раза. первая проверка будет на стороне акаунта.
имхо проверка таким образом не совсем, то… скорее надо проверить, что класс User имеет асоциацию с классом акаунт. с помощью shoulda это звучить просто should belong_to :account. а вот на чистом rspec наверное сложнее(стоит посмотреть код этого матчера)
p.s. не «стубинг», а «стАбинг» или заглушка
апреля 6, 2012 at 12:16 (#)
have_at_least — объект должен имет количество элементов меньшее, чем указано.
have_at_most — объект должен иметь больше указанного количество элементов.
—
Тут кажется наоборот, причем в примерах у вас все верно.
апреля 9, 2012 at 14:58 (#)
wildDAlex, спасибо, поправил.
июня 18, 2012 at 12:58 (#)
Спасибо за статью! С неё начал изучение RSpec.
февраля 4, 2013 at 10:44 (#)
Разница между let и let!
Основная разница на сколько я понимаю состоит в том, что let! вызывается как before(:each) для каждого ‘it’ — т.е. если у тебя в ‘it’ не используется «переменная» которая определена в let! — она все равно будет инициализирована…
Одним словом — если не строить никаки велосипедов — то лучше использовать let ( а не let! )
ссылочка:
января 22, 2014 at 16:50 (#)
Очень полезная статья. Спасибо большое! Я сам мануальный тестировщик, пытаюсь разобраться с автоматизацией на руби. Что можете посоветовать для начала. Слышал, что весь язык для автоматизации не обязательно осваивать.
января 28, 2014 at 12:48 (#)
Хороша стаття. Дякую за підбір матеріалу і такий аналіз елементів Rspec.