test context arrangement recipebook

Post on 19-Jan-2015

6.492 Views

Category:

Education

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

 

TRANSCRIPT

5 years know-how of

RSpec drivenRails app. development

MOROHASHI Kyosuke(@moro)

Kyosuke MOROHASHI

✓ http://twitter.com/moro

✓ http://github.com/moro

✓ Eiwa System Management, Inc.

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

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

RubyKaigi

special price

40% off

http://amzn.to/rails3recipebook

Available

@Junku-Do

RubyKaigi

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

5 years know-how of

RSpec drivenRails app. development

MOROHASHI Kyosuke(@moro)

諸橋 恭介

3の技

レシピブック

Test Context Arrangement

✓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.

Probl

em

What test should

I write first?

Solu

tion

Write Modelunit test.

Skinny ControllersFat Models

People say

It’s clear what happening.

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

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

http://twitter.com/railstokyo

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.

Model unit test

Probl

em

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

events

courses

rooms periods

registrations

students

lessons

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

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)

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)

student.subscribe(course)

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

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

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

Solu

tion

Use multi strategies for multi types of data.

✓ 3 types of data✓ 3 strategies

✓ 3 types of data✓ 3 strategies

✓Master data✓ Resource data✓ Event data

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

Mastar data

✓Mainly created/updated data.

✓ Users themselves maintains this type of data.

Resource data

✓ Represent relationship between resources.

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

Event data

✓ 3 types of data✓ 3 strategies

✓ Fixture

✓ Fixture replacement

✓ before/setup block

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

✓ good speed to be loaded.

✓ less maintainability.✓ less flexibility.

http://fabricationgem.org/

FixtureReplacement

✓ flexible.✓ powerful but

difficult to use correctly.

✓ slow to be loaded.

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

Which should we use rails-fixture or fixture replacement?

Use them both which suitable for the data.

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

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

✓ edit each of them to maintain.

speed flexibilityin test

maintain-ability good for

fixture best wrong wrongMaster

data

fixturereplacements wrong good best

Resourcedata

before() good best

goodfor eachwrongfor all

Eventdata

events

courses lessons

rooms periods

registrations

students

rooms periods

Master

events

courses lessons

studentsResource

registrations

Event

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

Solu

tion

Use multi strategies for multi types of data.

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

✓ fixture✓ fixture replacement✓ before/setup block

Use them all which suitable for the data.

Probl

em

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

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

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

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

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

Solu

tion

Share your testing context.

https://github.com/rspec

✓naming each✓naming them

✓naming each✓naming them

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

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

✓ name each focusing object.

✓ Build strategy doesn’t matter.

✓naming each✓naming them

✓ RSpec’s shared_context()

✓let()

✓before()

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

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

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

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

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

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

shared_context()

Solu

tion

Share your testing context.

Conclusion

✓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.

✓ fixture✓ fixture replacement✓ before/setup block

Use them all which suitable for the data.

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

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

shared_context()

May a testbe with you.

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

May a testbe with you.

top related