Test Driven
Development
● когда надо
● когда не надо
Павел Калашников, SimbirSoft
● 5 лет опыта коммерческой
разработки
● все 5 лет использую TDD
● применял (или не применял) TDD к
примерно 30 проектам
TL;DR
too long; didn't read
TDD-обещания
● ускорение скорости разработки
● показ тестов заказчику до реализации
● высокое покрытие функционала тестами
● меньше отладки
● проработанная архитектура приложения
TDD-разочарование
● только продуктовым компаниям
● не в нашем стеке технологий
● вообще не работает
● только на backend
● не в этой предметной области
TDD нужен НЕ всегда
Применять или не применять TDD зависит от:
● человека, который ставит и принимает задачи (“заказчика”)
● ценностей команды разработчиков
● ответственности менеджмента
Критерии использования TDD
Многие тестовые фреймворки поддерживают возможность создания схемы,
которая будет актуальна для всех интерфейсов
it "returns valid json matching the candidate types schema" do
get candidates_path, :params => {}, :headers => headers
expect(response).to match_response_schema("candidates", :strict => true)
end
Описанная структура интерфейсов
Один подход к реализации интерфейсов
def show
candidate = Candidate.find(params[:id])
render(
:json => candidate,
:serializer => CandidateSerializer,
:status => :ok
)
end
Один подход к реализации интерфейсов
def show
user = User.find(params[:id])
render(
:json => user,
:serializer => UserSerializer,
:status => :ok
)
end
В задаче описана реализация
● GET /bandings -> Lists the bandings for the current account
● POST /bandings -> Creates a banding for the current account
● GET /bandings/:id -> Gets a banding for the current account
● PATCH /bandings/:id -> Updates a banding for the current account
Критерии
● в проекте чётко описана структура интерфейсов (JSON-API, REST-API и
т.д.)
● в проекте однотипный подход к реализации интерфейсов
● “заказчик” ставит задачу, описывая в ней интерфейс взаимодействия
● задачи ставятся в стиле: одна подзадача - один тест
● в стеке технологий есть высокоуровневный тестовый фреймворк
(необязательное условие)
● нет отдельной команды QA
TDD & QA
Методология предполагает,
что тесты пишут
программисты
Когда TDD не нужен
Задачи без должной проработки
● Написание тестов
● Реализация функционала
● Рефакторинг
● Переписывание тестов
● Переделывание функционала
● Снова рефакторинг
Простой пример:
форма регистрации
Сложные вычисления
Сложные вычисления
-> Сложно предсказать результат
-> Сложно написать тест до реализации
-> Много времени на написание теста
Простой пример:
BI
Безопасность
Обеспечение безопасности сегодня требует участия человека
-> Автоматическое тестирование не очень полезно
-> Тесты до реализации ещё менее полезно
TDD не нужен!
● “заказчик” ставит задачи без должной проработки
● реализуется назаурядный функционал
● реализуется функционал, связанный со сложными вычислениями
● реализуется функционал, связанный с большими нагрузками
● реализуется функционал, связанный с безопасностью
Copy Paste Driven Development + TDD
Условия: описанные интерфейсы + описанные принципы
реализации интерфейсов
Порядок действий:
● Копируем тесты
● Переименовываем endpoints и названия сущностей (2
команды в VIM)
● Прогоняем тесты
● Копируем реализацию
● Переименовываем названия сущностей (2
команды в VIM)
● Прогоняем тесты
● “Работаем напильником”
UsersController
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
end
def create
@user = User.new(params[:user])
if @user.save
redirect_to user_path
else
render :new
end
end
end
UsersControllerTest
test "should get show" do
@user = User.find_by_email(“test@email.com”)
get user_path(@user)
assert_response :success
end
test "should post create" do
attributes = attributes_for(:user)
post :create, user: attribute
assert_response :success
end
CandidateController
class CandidatesController < ApplicationController
def show
@candidate = Candidate.find(params[:id])
end
def create
@candidate = Candidate.new(params[:candidate])
if @candidate.save
redirect_to candidate_path
else
render :new
end
end
end
CandidateControllerTest
test "should get show" do
@candidate = Candidate.find_by_email(“test@email.com”)
get candidate_path(@candidate)
assert_response :success
end
test "should post create" do
attributes = attributes_for(:candidate)
post :create, candidate: attribute
assert_response :success
end
RSpec or Minitest + FactoryGirl + webdriver
Фреймворки для интеграционного тестирования с
декларативно-императивным синтаксисоМ
● RSpec or Minitest or Cucumber
● FactoryGirl or another
● Selenium or Poltergeist or PhantomJS
RSpec
it "returns a candidate with the correct ID" do
candidate = create :candidate
get candidate_path candidate
expect(response.body).to include_json(
"data" => {
"id" => candidate.id.to_s,
"type" => "candidates"
}
)
end
RSpec
it "returns a user with the correct ID" do
user = create :user
get user_path user
expect(response.body).to include_json(
"data" => {
"id" => user.id.to_s,
"type" => "users"
}
)
end
RSpec
[ :user, :candidate ] do |model_name|
it "returns a #{model_name} with the correct ID" do
object = create model_name
get send "#{model_name}_path", object
expect(response.body).to include_json(
"data" => {
"id" => object.id.to_s,
"type" => model_name.pluralize(:en)
}
)
end
end
scenario 'Create new open opportunity and add teachers to it' do
visit opportunities_path
click_on "New opportunity"
fill_in "Name", with: "New opportunity"
fill_in "Amount in $", with: 100
click_on 'Create Opportunity'
fill_in "opportunity_teacher[email]", with: "test@example.com"
click_on "Add teacher"
expect(page).to have_content("test@example.com")
end
RSpec + Capybara
@kalashnikovisme
http://pavelkalashnikov.tumblr.com

TDD: когда нужно и, самое главное, когда не нужно / Павел Калашников (SimbirSoft)

  • 1.
    Test Driven Development ● когданадо ● когда не надо
  • 2.
    Павел Калашников, SimbirSoft ●5 лет опыта коммерческой разработки ● все 5 лет использую TDD ● применял (или не применял) TDD к примерно 30 проектам
  • 3.
  • 5.
    TDD-обещания ● ускорение скоростиразработки ● показ тестов заказчику до реализации ● высокое покрытие функционала тестами ● меньше отладки ● проработанная архитектура приложения
  • 9.
    TDD-разочарование ● только продуктовымкомпаниям ● не в нашем стеке технологий ● вообще не работает ● только на backend ● не в этой предметной области
  • 10.
    TDD нужен НЕвсегда Применять или не применять TDD зависит от: ● человека, который ставит и принимает задачи (“заказчика”) ● ценностей команды разработчиков ● ответственности менеджмента
  • 11.
  • 12.
    Многие тестовые фреймворкиподдерживают возможность создания схемы, которая будет актуальна для всех интерфейсов it "returns valid json matching the candidate types schema" do get candidates_path, :params => {}, :headers => headers expect(response).to match_response_schema("candidates", :strict => true) end Описанная структура интерфейсов
  • 13.
    Один подход креализации интерфейсов def show candidate = Candidate.find(params[:id]) render( :json => candidate, :serializer => CandidateSerializer, :status => :ok ) end
  • 14.
    Один подход креализации интерфейсов def show user = User.find(params[:id]) render( :json => user, :serializer => UserSerializer, :status => :ok ) end
  • 15.
    В задаче описанареализация ● GET /bandings -> Lists the bandings for the current account ● POST /bandings -> Creates a banding for the current account ● GET /bandings/:id -> Gets a banding for the current account ● PATCH /bandings/:id -> Updates a banding for the current account
  • 16.
    Критерии ● в проектечётко описана структура интерфейсов (JSON-API, REST-API и т.д.) ● в проекте однотипный подход к реализации интерфейсов ● “заказчик” ставит задачу, описывая в ней интерфейс взаимодействия ● задачи ставятся в стиле: одна подзадача - один тест ● в стеке технологий есть высокоуровневный тестовый фреймворк (необязательное условие) ● нет отдельной команды QA
  • 17.
    TDD & QA Методологияпредполагает, что тесты пишут программисты
  • 18.
  • 19.
    Задачи без должнойпроработки ● Написание тестов ● Реализация функционала ● Рефакторинг ● Переписывание тестов ● Переделывание функционала ● Снова рефакторинг
  • 20.
  • 21.
    Сложные вычисления Сложные вычисления ->Сложно предсказать результат -> Сложно написать тест до реализации -> Много времени на написание теста
  • 22.
  • 23.
    Безопасность Обеспечение безопасности сегоднятребует участия человека -> Автоматическое тестирование не очень полезно -> Тесты до реализации ещё менее полезно
  • 24.
    TDD не нужен! ●“заказчик” ставит задачи без должной проработки ● реализуется назаурядный функционал ● реализуется функционал, связанный со сложными вычислениями ● реализуется функционал, связанный с большими нагрузками ● реализуется функционал, связанный с безопасностью
  • 25.
    Copy Paste DrivenDevelopment + TDD Условия: описанные интерфейсы + описанные принципы реализации интерфейсов Порядок действий: ● Копируем тесты ● Переименовываем endpoints и названия сущностей (2 команды в VIM) ● Прогоняем тесты ● Копируем реализацию ● Переименовываем названия сущностей (2 команды в VIM) ● Прогоняем тесты ● “Работаем напильником”
  • 26.
    UsersController class UsersController <ApplicationController def show @user = User.find(params[:id]) end def create @user = User.new(params[:user]) if @user.save redirect_to user_path else render :new end end end
  • 27.
    UsersControllerTest test "should getshow" do @user = User.find_by_email(“test@email.com”) get user_path(@user) assert_response :success end test "should post create" do attributes = attributes_for(:user) post :create, user: attribute assert_response :success end
  • 28.
    CandidateController class CandidatesController <ApplicationController def show @candidate = Candidate.find(params[:id]) end def create @candidate = Candidate.new(params[:candidate]) if @candidate.save redirect_to candidate_path else render :new end end end
  • 29.
    CandidateControllerTest test "should getshow" do @candidate = Candidate.find_by_email(“test@email.com”) get candidate_path(@candidate) assert_response :success end test "should post create" do attributes = attributes_for(:candidate) post :create, candidate: attribute assert_response :success end
  • 30.
    RSpec or Minitest+ FactoryGirl + webdriver Фреймворки для интеграционного тестирования с декларативно-императивным синтаксисоМ ● RSpec or Minitest or Cucumber ● FactoryGirl or another ● Selenium or Poltergeist or PhantomJS
  • 31.
    RSpec it "returns acandidate with the correct ID" do candidate = create :candidate get candidate_path candidate expect(response.body).to include_json( "data" => { "id" => candidate.id.to_s, "type" => "candidates" } ) end
  • 32.
    RSpec it "returns auser with the correct ID" do user = create :user get user_path user expect(response.body).to include_json( "data" => { "id" => user.id.to_s, "type" => "users" } ) end
  • 33.
    RSpec [ :user, :candidate] do |model_name| it "returns a #{model_name} with the correct ID" do object = create model_name get send "#{model_name}_path", object expect(response.body).to include_json( "data" => { "id" => object.id.to_s, "type" => model_name.pluralize(:en) } ) end end
  • 34.
    scenario 'Create newopen opportunity and add teachers to it' do visit opportunities_path click_on "New opportunity" fill_in "Name", with: "New opportunity" fill_in "Amount in $", with: 100 click_on 'Create Opportunity' fill_in "opportunity_teacher[email]", with: "test@example.com" click_on "Add teacher" expect(page).to have_content("test@example.com") end RSpec + Capybara
  • 35.