test context arrangement recipebook

75
5 years know-how of RSpec driven Rails app. development MOROHASHI Kyosuke (@moro)

Upload: kyosuke-morohashi

Post on 19-Jan-2015

6.492 views

Category:

Education


0 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Test Context Arrangement Recipebook

5 years know-how of

RSpec drivenRails app. development

MOROHASHI Kyosuke(@moro)

Page 2: Test Context Arrangement Recipebook

Kyosuke MOROHASHI

✓ http://twitter.com/moro

✓ http://github.com/moro

✓ Eiwa System Management, Inc.

Page 3: Test Context Arrangement Recipebook
Page 4: Test Context Arrangement Recipebook

http://gihyo.jp/news/report/01/rubykaigi2011/0001

Page 5: Test Context Arrangement Recipebook

http://tatsu-zine.com/books/cuke

RubyKaigi

special price

40% off

Page 6: Test Context Arrangement Recipebook

http://amzn.to/rails3recipebook

Available

@Junku-Do

RubyKaigi

Page 7: Test Context Arrangement Recipebook

http://www.junkudo.co.jp/tenpo/evtalk.html#20110721ikebukuro

Page 8: Test Context Arrangement Recipebook

5 years know-how of

RSpec drivenRails app. development

MOROHASHI Kyosuke(@moro)

Page 9: Test Context Arrangement Recipebook

諸橋 恭介

3の技

レシピブック

Test Context Arrangement

Page 10: Test Context Arrangement Recipebook

✓What test should I write first?

✓ Write model unit test.✓I began model test,but it’s difficult to setup data!!

✓ Use multi strategies for multi types of data.

✓It seems good but less DRY. Is there any idea?

✓ Share your testing context.

I’ll talk about.

Page 11: Test Context Arrangement Recipebook

Probl

em

What test should

I write first?

Page 12: Test Context Arrangement Recipebook

Solu

tion

Write Modelunit test.

Page 13: Test Context Arrangement Recipebook

Skinny ControllersFat Models

People say

Page 14: Test Context Arrangement Recipebook

It’s clear what happening.

http://flic.kr/p/5tk4by

Page 15: Test Context Arrangement Recipebook

#64 2011-07-18 12:00@ねりぶん集会室

http://twitter.com/railstokyo

Page 16: Test Context Arrangement Recipebook

RubyKaigi

special price

40% off

http://tatsu-zine.com/books/cuke

Use Cucumber(or other end-to-end testing)

to cover controller, views and more.

Page 17: Test Context Arrangement Recipebook

Model unit test

Page 18: Test Context Arrangement Recipebook

Probl

em

Began model test, but it’s difficult to setup data!!

Page 19: Test Context Arrangement Recipebook

events

courses

rooms periods

registrations

students

lessons

Page 20: Test Context Arrangement Recipebook

Course-1 Course-2

Period-1

Period-2

Period-3

Period-4

Period-5

Lesson-1-1 Lesson-2-1

Lesson-1-2 Lesson-2-2

Lesson-1-3 Lesson-2-3

Lesson-1-4 Lesson-2-4

Lesson-1-5 Lesson-2-5

registration

Page 21: Test Context Arrangement Recipebook

Course-1 Course-2

Period-1

Period-2

Period-3

Period-4

Period-5

Lesson-1-1 Lesson-2-1

Lesson-1-2 Lesson-2-2

Lesson-1-3 Lesson-2-3

Lesson-1-4 Lesson-2-4

Lesson-1-5 Lesson-2-5

registration

Student#subscribe(course_1)

Page 22: Test Context Arrangement Recipebook

Course-1 Course-2

Period-1

Period-2

Period-3

Period-4

Period-5

Lesson-1-1 Lesson-2-1

Lesson-1-2 Lesson-2-2

Lesson-1-3 Lesson-2-3

Lesson-1-4 Lesson-2-4

Lesson-1-5 Lesson-2-5

registration

Student#register!(lesson_2_4)

Page 23: Test Context Arrangement Recipebook

student.subscribe(course)

student.register!( @another_course.lessons[3])

Page 24: Test Context Arrangement Recipebook

describe Student do let(:student) { Student.create(name: 'Alice') } before do event = Event.create(name: 'Camp') course = event.courses.create(name: 'Course-1') 5.times {¦i¦ course.lessons.create(name: "Lessson-#{i}") } student.subscribe(course)

@another_course = event.courses.create(name: 'Course-2') 5.times {¦i¦ @another_course.lessons.create(name: "NewLessson-#{i}") } student.register!(@another_course.lessons[3]) end its(:registering_lessons) do should include(@another_course.lessons[3]) endend

Page 25: Test Context Arrangement Recipebook

describe Student do let(:student) { Student.create(name: 'Alice') } before do event = Event.create(name: 'Camp') course = event.courses.create(name: 'Course-1') 5.times {¦i¦ course.lessons.create(name: "Lessson-#{i}") } student.subscribe(course)

@another_course = event.courses.create(name: 'Course-2') 5.times {¦i¦ @another_course.lessons.create(name: "NewLessson-#{i}") } student.register!(@another_course.lessons[3]) end its(:registering_lessons) do should include(@another_course.lessons[3]) endend

Page 26: Test Context Arrangement Recipebook

Solu

tion

Use multi strategies for multi types of data.

Page 27: Test Context Arrangement Recipebook

✓ 3 types of data✓ 3 strategies

Page 28: Test Context Arrangement Recipebook

✓ 3 types of data✓ 3 strategies

Page 29: Test Context Arrangement Recipebook

✓Master data✓ Resource data✓ Event data

Page 30: Test Context Arrangement Recipebook

✓ Created/updated rarely.✓e.g. rooms / periods.✓ Less dependencies to others.✓ Maintained by scaffold or so.

Mastar data

Page 31: Test Context Arrangement Recipebook

✓Mainly created/updated data.

✓ Users themselves maintains this type of data.

Resource data

Page 32: Test Context Arrangement Recipebook

✓ Represent relationship between resources.

✓ Used for has_many :through ‘s through table in Rails.

Event data

Page 33: Test Context Arrangement Recipebook

✓ 3 types of data✓ 3 strategies

Page 34: Test Context Arrangement Recipebook

✓ Fixture

✓ Fixture replacement

✓ before/setup block

Page 35: Test Context Arrangement Recipebook

Fixturehttp://flic.kr/p/5NfjKM

Page 36: Test Context Arrangement Recipebook

✓ good speed to be loaded.

✓ less maintainability.✓ less flexibility.

Page 37: Test Context Arrangement Recipebook

http://fabricationgem.org/

FixtureReplacement

Page 38: Test Context Arrangement Recipebook

✓ flexible.✓ powerful but

difficult to use correctly.

✓ slow to be loaded.

Page 39: Test Context Arrangement Recipebook

Fabricator(:event) do name 'SummerCamp' courses!(count: 2) {¦e, i¦ Fabricate(:course, event: e) }end

Fabricator(:course) do name { sequence(:course_name) {¦i¦ "Course-#{i+1}" } } lessons!(count: 5) {¦course, i¦ Fabricate(:lesson, course: course, period_id: i) }end

Fabricator(:lesson) do name { sequence(:lesson_name) {¦i¦ "Lesson-#{i+1}" } } period_id 1 room_id 1end

Page 40: Test Context Arrangement Recipebook

Which should we use rails-fixture or fixture replacement?

Use them both which suitable for the data.

Page 41: Test Context Arrangement Recipebook

describe Student do let(:student) { Student.create(name: 'Alice') }

describe "#register" do

subject { student }

before do student.subscribe(course) student.register(another_course.lessons[3]) end

its(:registering_lessons) do

before/setup block

Page 42: Test Context Arrangement Recipebook

✓ each run per test.✓ same speed with real data persistence.

✓ edit each of them to maintain.

Page 43: Test Context Arrangement Recipebook

speed flexibilityin test

maintain-ability good for

fixture best wrong wrongMaster

data

fixturereplacements wrong good best

Resourcedata

before() good best

goodfor eachwrongfor all

Eventdata

Page 44: Test Context Arrangement Recipebook

events

courses lessons

rooms periods

registrations

students

rooms periods

Master

events

courses lessons

studentsResource

registrations

Event

Page 45: Test Context Arrangement Recipebook

describe Student do # fixture :all let(:student) { Fabricate(:student) }

describe "#register" do let(:event) { Fabricate(:event) } let(:course) { event.courses.first } let(:another_course) { event.courses.last }

subject { student }

before do student.subscribe(course) student.register!(another_course.lessons[3]) end

its(:registering_lessons) do should include(another_course.lessons[3]) end

Page 46: Test Context Arrangement Recipebook

Solu

tion

Use multi strategies for multi types of data.

Page 47: Test Context Arrangement Recipebook

type, strategy = if user.rarely_maitain?(data) [MastarData, :fixture] elsif data.through_table? [EventData, :before] else [ResourceData, :fixture_replacement] end

Page 48: Test Context Arrangement Recipebook

✓ fixture✓ fixture replacement✓ before/setup block

Use them all which suitable for the data.

Page 49: Test Context Arrangement Recipebook

Probl

em

It seems good but less DRY.Is there any idea?

Page 50: Test Context Arrangement Recipebook

Course Students Count

Period-1

Period-2

Period-3

Period-4

Period-5

Lesson-1-1 2

Lesson-1-2 3

Lesson-1-3 3

Lesson-1-4 3

Lesson-1-5 2

Course#attendee_count

Page 51: Test Context Arrangement Recipebook

describe Course do describe '#attendee_per_lesson' do let(:student) { Fabricate(:student) } let(:event) { Fabricate(:event) } let(:course) { event.courses.first }

subject { course }

before do student.subscribe(course) Student.create(name: 'Bob').subscribe(course) end

its('attendee_count.first') do should == [course.lessons[0], 2] end

Page 52: Test Context Arrangement Recipebook

describe Course do describe '#attendee_per_lesson' do let(:student) { Fabricate(:student) } let(:event) { Fabricate(:event) } let(:course) { event.courses.first }

subject { course }

before do student.subscribe(course) Student.create(name: 'Bob').subscribe(course) end

its('attendee_count.first') do should == [course.lessons[0], 2] end

Page 53: Test Context Arrangement Recipebook

describe Student do # fixture :all let(:student) { Fabricate(:student) }

describe "#register!" do let(:event) { Fabricate(:event) } let(:course) { event.courses.first } let(:another_course) { event.courses.last }

subject { student }

before do student.subscribe(course) student.register!(another_course.lessons[3]) end

its(:registering_lessons) do should include(another_course.lessons[3]) end

Page 54: Test Context Arrangement Recipebook

Solu

tion

Share your testing context.

Page 55: Test Context Arrangement Recipebook

https://github.com/rspec

Page 56: Test Context Arrangement Recipebook

✓naming each✓naming them

Page 57: Test Context Arrangement Recipebook

✓naming each✓naming them

Page 58: Test Context Arrangement Recipebook

“Generates a method whose return value is memoized after the first call.

describe 'TheAnswer' do let(:answer) { 42 } specify { answer.should == 42 }end

Page 59: Test Context Arrangement Recipebook

✓ name each focusing object.

✓ Build strategy doesn’t matter.

Page 60: Test Context Arrangement Recipebook

✓naming each✓naming them

Page 61: Test Context Arrangement Recipebook

✓ RSpec’s shared_context()

✓let()

✓before()

Page 62: Test Context Arrangement Recipebook

shared_context "A student subscribes a course" do let(:student) { Fabricate(:student) } let(:event) { Fabricate(:event) } let(:course) { event.courses.first }

before do student.subscribe(course) endend

Page 63: Test Context Arrangement Recipebook

event

course-1

course-2

Alice<student>

registrationsregistrationsregistrationsregistrations

room-1 period-1room-1room-1room-1room-1period-1period-1period-1period-1

lesson-1-1lesson-1-1lesson-1-1lesson-1-1lesson-1-1

lesson-2-1lesson-2-1lesson-2-1lesson-2-1lesson-2-1

A student subscribes a course

Page 64: Test Context Arrangement Recipebook

require 'spec_helper'require 'student_subscribe_a_course'

describe Student do include_context "A student subscribes a course"

describe "#register!" do let(:another_course) { event.courses.last } subject { student }

before do student.register!(another_course.lessons[3]) end

its(:registering_lessons) do should include(another_course.lessons[3]) end endend

Page 65: Test Context Arrangement Recipebook

require 'spec_helper'require 'student_subscribe_a_course'

describe Course do include_context "A student subscribes a course"

describe '#attendee_per_lesson' do

subject { course }

before do Student.create(name: 'Bob').subscribe(course) end

its('attendee_count.first') do should == [course.lessons[0], 2] end endend

Page 66: Test Context Arrangement Recipebook

Express your intention clearly and reduce tiredness to add new test.

✓ naming each w/ let()✓ naming them w/

shared_context()

Page 67: Test Context Arrangement Recipebook

Solu

tion

Share your testing context.

Page 68: Test Context Arrangement Recipebook

Conclusion

Page 69: Test Context Arrangement Recipebook

✓What test should I write first?

✓ Write model unit test.✓I began model test,but it’s difficult to setup data!!

✓ Use multi strategies for multi types of data.

✓It seems good but less DRY. Is there any idea?

✓ Share your testing context.

I’ve talked about.

Page 70: Test Context Arrangement Recipebook

✓ fixture✓ fixture replacement✓ before/setup block

Use them all which suitable for the data.

Page 71: Test Context Arrangement Recipebook

Express your intention clearly and reduce tiredness to add new test.

✓ naming each w/ let()✓ naming them w/

shared_context()

Page 72: Test Context Arrangement Recipebook

May a testbe with you.

Page 73: Test Context Arrangement Recipebook
Page 74: Test Context Arrangement Recipebook

http://www.rubyworld-conf.org/

Page 75: Test Context Arrangement Recipebook

May a testbe with you.