10. RSpec. Require

10. RSpec. Require

10. RSpec. Require

14 ноември 2016

Днес

План за 19-20 ноември

традиционната планина

Трета задача

Четвърта задача

Първи тест

RSpec

малко преговор

Пример от предната лекция

class Game
  def initialize(name, genre)
    @name  = name
    @genre = genre
  end

  def recommend
    case genre
    when :mmorpg then "Hey! Did you hear about #{name}? It's better than WoW!"
    when :fps    then "Yo! You must try this new shooter - #{name}!"
    else              "Have you tried #{name}? It's awesome!"
    end
  end
end

RSpec.describe Game do
  it 'compares MMORPGs to WoW' do
    game = Game.new('Guild Wars 2', :mmorpg)
    expect(game.recommend).to eq 'Hey! Did you hear about Guild Wars 2? It\'s better than WoW!'
  end

  it 'calls FPS games shooters' do
    game = Game.new('FarCry 4', :fps)
    expect(game.recommend).to eq 'Yo! You must try this new shooter - FarCry 4!'
  end

  it 'calls games from unknown genres awesome' do
    game = Game.new('The Witcher 3', :rpg)
    expect(game.recommend).to eq 'Have you tried The Witcher 3? It\'s awesome!'
  end
end

Конвенции

Още

Често използвани matcher-и

expect(object).to be value          #=> object.equal? value
expect(object).to eq value          #=> object == value
expect(object).to be < value        #=> object < value
expect(object).to be > value        #=> object > value
expect(object).to match_array value #=> сравнява два масива, без да гледа реда на елементите
expect(object).to be_something      #=> object.something?
expect(object).to have_something    #=> object.has_something?
expect(object).to include(value)    #=> object.include? value
expect { code }.to raise_error(Error, 'message')

Често използвани matcher-и

негативна версия

expect(object).to_not be value          #=> object.equal? value
expect(object).to_not eq value          #=> object == value
expect(object).to_not be < value        #=> object < value
expect(object).to_not be > value        #=> object > value
expect(object).to_not match_array value #=> сравнява два масива, без да гледа реда на елементите
expect(object).to_not be_something      #=> object.something?
expect(object).to_not have_something    #=> object.has_something?
expect(object).to_not include(value)    #=> object.include? value
expect { code }.to_not raise_error

before и after

RSpec.describe '#launch_nukes' do
  before { puts 'prepare nukes' }
  after  { puts 'destroy nukes' }

  it 'can launch the nukes' do
    puts 'launching nukes'
  end
end

it 'does not have access to nukes' do
  puts 'cannot launch nukes'
end

#=> prepare nukes
#=> launching nukes
#=> destroy nukes
#=> cannot launch nukes

before и after

before и after

RSpec.describe '#launch_nukes' do
  before do
    @silo = NukeSilo.new
    @silo.nukes = 10
  end

  it 'can launch the nukes' do
    @silo.launch_at('Moon')
  end
end

it 'does not have access to @silo' do
  expect(@silo).to eq nil
end

Често използвани обекти

let

let(:user) { User.new('Georgi', :admin)      }
let(:game) { Game.new('The Witcher 3', :rpg) }

it 'can like a game' do
  user.like(game)

  expect(game.likes).to eq 1
  expect(user.favourite_games).to match_array [game]
end

it 'can play a game' do
  user.play(game, 2.hours)

  expect(game.played_hours).to eq 2
  expect(user.gameplay_hours).to eq 2
end

Често използвани обекти

let

let(:user) { puts 'user initialized'; User.new('Georgi', :admin) }

it 'does something' do
  expect(user.admin?).to be true
  expect(user.admin?).to be true
  expect(user.admin?).to be true
end

it 'does something else' do
  expect(user.superadmin?).to be true
end

it 'does not test user' do
  expect(true).to be true
end

#=> user initialized
#=> user initialized

Често използвани обекти

let!

let!(:user) { puts 'user initialized'; User.new('Georgi', :admin) }

it 'does something' do
  expect(user.admin?).to be true
  expect(user.admin?).to be true
  expect(user.admin?).to be true
end

it 'does something else' do
  expect(user.superadmin?).to be true
end

it 'does not test user' do
  expect(true).to be true
end

#=> user initialized
#=> user initialized
#=> user initialized

let и let!

Въпроси дотук?

???

Test Doubles

преговор

To mock or not to mock

class Game
  attr_accessor :likes

  def initialize(name, genre)
    # ...
    @likes = 0
  end
end

class User
  def like(game)
    game.likes += 1
  end
end

Без mock-ване

it 'increases the like counter of the game' do
  game = Game.new('The Witcher 3', :rpg)
  user = User.new('Georgi', :admin)

  user.like(game)

  expect(game.likes).to eq 1
end

С mock-ване

it 'increases the like counter of the game' do
  game = double likes: 0
  user = User.new('Georgi', :admin)

  expect(game).to receive(:likes=).with(1)

  user.like(game)
end

Stub-ване

class Game
  def popular?() likes >= 100 end
end

let(:game) { Game.new('The Witcher 3', :rpg) }

describe '#popular?' do
  it 'returns true if the game is liked by at least 100 users' do
    allow(game).to receive(:likes).and_return(123)

    expect(game.popular?).to be true
  end
end

Друг пример

class NukeSilo
  def launch_nukes_at(city)
    # [CLASSIFIED]

    "Nukes sent at #{city}"
  end
end

class User
  def launch_nukes(silo)
    raise "No permissions" unless rank == :president

    silo.launch_nukes_at '[CLASSIFIED]'
  end
end

Друг пример

context 'when the user is a president' do
  let(:silo) { NukeSilo.new }

  it 'launches the nukes and reports what happened' do
    user = User.new('Trump', :president)

    expect(silo).to receive(:launch_nukes_at).and_return('World peace achieved')

    expect(user.launch_nukes(silo)).to eq 'World peace achieved'
  end
end

Двете страни на една и съща монета

ЗА мокване

Двете страни на една и съща монета

ПРОТИВ мокване

Как решаваме?

Мокваме/стъбваме само ако...

Въпроси?

Задайте ги сега!

Импортиране на файлове

В Ruby, код от други файлове се импортира с require.

Например:

require 'bigdecimal'
require 'bigdecimal/util'

Какво търси require?

Текуща директория на процес

лирическо отклонение

Всеки процес си има "текуща директория".

Load path

където Ruby търси файлове за require

Зареждане на файлове от текущата директория

Зареждане на файлове от текущата директория

require_relative

Как работи require?

Библиотеките в Ruby

Типичната структура на един gem

skeptic опростен

.
├── README.rdoc
├── Rakefile
├── bin
│   └── skeptic
├── features
├── lib
│   ├── skeptic
│   │   ├── rules.rb
│   │   └── scope.rb
│   └── skeptic.rb
├── skeptic.gemspec
└── spec 

Особеностите

Останалите неща

Kernel#load

Въпроси