From 7ade3a7dca446bfcfe41ed57376f016c45e04b9c Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Fri, 28 Jan 2011 15:03:09 -0800 Subject: [PATCH] Add specs It's certainly not a best practice to wait 3 years before adding specs for a library you are using in production, but better late than never. This adds specs for both ActiveRecord and Sequel, which should have close to 100% line coverage. --- .gitignore | 6 +- README | 15 ++-- Rakefile | 27 +++++++ spec/ar_spec_helper.rb | 21 ++++++ spec/fd_spec.rb | 124 ++++++++++++++++++++++++++++++++ spec/fixtures/albums.yml | 12 ++++ spec/fixtures/artists.yml | 7 ++ spec/fixtures/bars.yml | 2 + spec/fixtures/self_refs.yml.erb | 14 ++++ spec/fixtures/tags.yml | 7 ++ spec/migrate/001_tables.rb | 10 +++ spec/sequel_spec_helper.rb | 31 ++++++++ spec/spec_helper.rb | 13 ++++ 13 files changed, 283 insertions(+), 6 deletions(-) create mode 100644 spec/ar_spec_helper.rb create mode 100644 spec/fd_spec.rb create mode 100644 spec/fixtures/albums.yml create mode 100644 spec/fixtures/artists.yml create mode 100644 spec/fixtures/bars.yml create mode 100644 spec/fixtures/self_refs.yml.erb create mode 100644 spec/fixtures/tags.yml create mode 100644 spec/migrate/001_tables.rb create mode 100644 spec/sequel_spec_helper.rb create mode 100644 spec/spec_helper.rb diff --git a/.gitignore b/.gitignore index 83fb5c1..d4972e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ -rdoc/ -*.gem +/rdoc/ +/*.gem +/spec/db +/coverage diff --git a/README b/README index baa75e3..cc2c6f1 100644 --- a/README +++ b/README @@ -7,7 +7,7 @@ the following features: - Fixtures specify association names instead of foreign keys - Support both Sequel and ActiveRecord - Supports many_to_one/belongs_to, one_to_many/has_many, - many_to_many/has_and_belongs_to_many, and has_one associations + many_to_many/has_and_belongs_to_many, and has_one/one_to_one associations - Loads a fixture's dependency graph in such a manner that foreign key constraints aren't violated - Has a very simple API (FixtureDependencies.load(:model__fixture)) @@ -128,7 +128,7 @@ using the following syntax: This would load just the jeremy fixture and its dependencies. I find this is much better than loading all fixtures in most of my test suites. Even better -is loading just the fixtures you want instead every test method (see below). +is loading just the fixtures you want inside every test method (see below). This leads to the most robust testing. == Loading fixtures inside test methods @@ -244,6 +244,13 @@ test/test_helper.rb: This will give a verbose description of the loading and saving of fixtures for every test, including the recursive loading of the dependency graph. +== Specs + +The specs for fixture dependencies and be run with Rake. They require +the sequel, activerecord, and sqlite3 gems installed. The default rake task +runs the specs. You should run the spec_migrate task first to create the +spec database. + == Similar Ideas Rails now supports something similar by default. Honestly, I'm not sure what @@ -255,8 +262,8 @@ it doesn't support has_* associations. == License -fixture_dependencies is released under the MIT License. See the LICENSE file -for details. +fixture_dependencies is released under the MIT License. See the MIT-LICENSE +file for details. == Author diff --git a/Rakefile b/Rakefile index 3784257..1ac38f4 100644 --- a/Rakefile +++ b/Rakefile @@ -20,3 +20,30 @@ desc "Package fixture_dependencies" task :package do sh %{gem build fixture_dependencies.gemspec} end + +begin + require 'spec/rake/spectask' + + desc "Run Sequel specs" + Spec::Rake::SpecTask.new(:spec_sequel) do |t| + t.spec_files = Dir['spec/*_spec.rb'] + #t.rcov = true + end + + desc "Run ActiveRecord specs" + Spec::Rake::SpecTask.new(:spec_ar) do |t| + ENV['FD_AR'] = '1' + t.spec_files = Dir['spec/*_spec.rb'] + #t.rcov = true + end + + desc "Run Sequel and ActiveRecord specs" + task :default=>[:spec_sequel, :spec_ar] +rescue LoadError +end + +desc "Create spec database" +task :spec_migrate do + sh %{mkdir -p spec/db} + sh %{sequel -m spec/migrate -E sqlite://spec/db/fd_spec.sqlite3} +end diff --git a/spec/ar_spec_helper.rb b/spec/ar_spec_helper.rb new file mode 100644 index 0000000..a705c42 --- /dev/null +++ b/spec/ar_spec_helper.rb @@ -0,0 +1,21 @@ +require 'active_record' + +ActiveRecord::Base.establish_connection(:adapter=>'sqlite3', :database=>File.join(File.dirname(File.expand_path(__FILE__)), 'db', 'fd_spec.sqlite3')) + +class Artist < ActiveRecord::Base + has_many :albums +end + +class Album < ActiveRecord::Base + belongs_to :artist + has_and_belongs_to_many :tags +end + +class Tag < ActiveRecord::Base + has_and_belongs_to_many :albums +end + +class SelfRef < ActiveRecord::Base + belongs_to :self_ref + has_many :self_refs +end diff --git a/spec/fd_spec.rb b/spec/fd_spec.rb new file mode 100644 index 0000000..0a0c84f --- /dev/null +++ b/spec/fd_spec.rb @@ -0,0 +1,124 @@ +require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper') + +describe FixtureDependencies do + def load(*a) FixtureDependencies.load(*a) end + def verbose(i=3) + v = FixtureDependencies.verbose + FixtureDependencies.verbose = i + yield + ensure + FixtureDependencies.verbose = v + end + + after do + # Clear tables and fixture_dependencies caches + [:self_refs, :albums_tags, :tags, :albums, :artists].each{|x| DB[x].delete} + FixtureDependencies.loaded.clear + FixtureDependencies.fixtures.clear + end + + it "should load single records with underscore syntax" do + ym = load(:artist__ym) + ym.id.should == 1 + ym.name.should == 'YM' + end + + it "should load multiple records with underscore syntax and multiple arguments" do + rf, mo = load(:album__rf, :album__mo) + rf.id.should == 1 + rf.name.should == 'RF' + mo.id.should == 2 + mo.name.should == 'MO' + end + + it "should load multiple records with hash syntax" do + rf, mo = load(:albums=>[:rf, :mo]) + rf.id.should == 1 + rf.name.should == 'RF' + mo.id.should == 2 + mo.name.should == 'MO' + end + + it "should load multiple records with a mix a hashes and symbols" do + rf, mo = load(:album__rf, :albums=>[:mo]) + rf.id.should == 1 + rf.name.should == 'RF' + mo.id.should == 2 + mo.name.should == 'MO' + end + + it "should load whole tables at once with single symbol" do + Artist.count.should == 0 + load(:artists) + Artist.count.should == 2 + end + + it "should load whole tables at once with single symbol" do + Artist.count.should == 0 + Album.count.should == 0 + load(:artists, :albums) + Artist.count.should == 2 + Album.count.should == 3 + end + + it "should load associated many_to_one records" do + rf = load(:album__rf) + rf.artist.id.should == 1 + end + + it "should load associated one_to_many records" do + nu = load(:artist__nu) + nu.albums.length.should == 1 + nu.albums.first.id.should == 3 + nu.albums.first.name.should == 'P' + end + + it "should load associated many_to_many records and handle cycles (I->P->NU->P)" do + i = load(:tag__i) + i.albums.length.should == 1 + p = i.albums.first + p.id.should == 3 + p.name.should == 'P' + nu = p.artist + nu.id.should == 2 + nu.name.should == 'NU' + nu.albums.length.should == 1 + nu.albums.first.should == p + end + + it "should not load records that were not asked for" do + ym = load(:artist__ym) + Artist.count.should == 1 + Artist.first.should == ym + load(:artist__nu) + Artist.count.should == 2 + end + + it "should handle self referential records" do + i = load(:self_ref__i) + i.id.should == 1 + i.self_ref.id.should == 1 + i.self_refs.length.should == 1 + i.self_refs.first.id.should == 1 + end + + it "should handle association cycles" do + a = load(:self_ref__a) + a.id.should == 2 + b = a.self_ref + b.id.should == 3 + c = b.self_ref + c.id.should == 4 + c.self_ref.should == a + end + + it "should raise error nonexistent fixture files" do + class Foo < Sequel::Model; end + proc{load(:foos)}.should raise_error(ArgumentError) + end + + it "should raise error for unsupported model classes" do + class Bar; def self.table_name() :bars end end + proc{load(:bars)}.should raise_error(TypeError) + end +end diff --git a/spec/fixtures/albums.yml b/spec/fixtures/albums.yml new file mode 100644 index 0000000..1c795b9 --- /dev/null +++ b/spec/fixtures/albums.yml @@ -0,0 +1,12 @@ +rf: + id: 1 + name: RF + artist: ym +mo: + id: 2 + name: MO + artist: ym +p: + id: 3 + name: P + artist: nu diff --git a/spec/fixtures/artists.yml b/spec/fixtures/artists.yml new file mode 100644 index 0000000..befeaf1 --- /dev/null +++ b/spec/fixtures/artists.yml @@ -0,0 +1,7 @@ +ym: + id: 1 + name: YM +nu: + id: 2 + name: NU + albums: p diff --git a/spec/fixtures/bars.yml b/spec/fixtures/bars.yml new file mode 100644 index 0000000..fa4e559 --- /dev/null +++ b/spec/fixtures/bars.yml @@ -0,0 +1,2 @@ +i: + id: 1 diff --git a/spec/fixtures/self_refs.yml.erb b/spec/fixtures/self_refs.yml.erb new file mode 100644 index 0000000..23260c9 --- /dev/null +++ b/spec/fixtures/self_refs.yml.erb @@ -0,0 +1,14 @@ +i: + id: 1 + self_ref: i + self_refs: [i] +a: + id: 2 + self_ref: b +b: + id: 3 + self_ref: c +c: + id: 4 + self_ref: a + diff --git a/spec/fixtures/tags.yml b/spec/fixtures/tags.yml new file mode 100644 index 0000000..3d6aa6f --- /dev/null +++ b/spec/fixtures/tags.yml @@ -0,0 +1,7 @@ +m: + id: 1 + name: M +i: + id: 2 + name: I + albums: p diff --git a/spec/migrate/001_tables.rb b/spec/migrate/001_tables.rb new file mode 100644 index 0000000..4696f3e --- /dev/null +++ b/spec/migrate/001_tables.rb @@ -0,0 +1,10 @@ +Sequel.migration do + change do + create_table(:artists){primary_key :id; String :name} + create_table(:albums){primary_key :id; String :name; foreign_key :artist_id, :artists} + create_table(:tags){primary_key :id; String :name} + create_table(:albums_tags){foreign_key :album_id, :albums; foreign_key :tag_id, :tags; primary_key [:album_id, :tag_id]} + + create_table(:self_refs){primary_key :id; foreign_key :self_ref_id, :self_refs} + end +end diff --git a/spec/sequel_spec_helper.rb b/spec/sequel_spec_helper.rb new file mode 100644 index 0000000..fdc7337 --- /dev/null +++ b/spec/sequel_spec_helper.rb @@ -0,0 +1,31 @@ +class Artist < Sequel::Model + one_to_many :albums +end + +class Album < Sequel::Model + many_to_one :artist + many_to_many :tags +end + +class Tag < Sequel::Model + many_to_many :albums +end + +class SelfRef < Sequel::Model + many_to_one :self_ref + one_to_many :self_refs +end + +class ComArtist < Sequel::Model + one_to_many :com_albums, :key=>[:artist_id1, :artist_id2] +end + +class ComAlbum < Sequel::Model + many_to_one :com_artist, :key=>[:artist_id1, :artist_id2] + many_to_many :com_tags, :left_key=>[:album_id1, :album_id2], :right_key=>[:tag_id1, :tag_id2] +end + +class ComTag < Sequel::Model + many_to_many :com_albums, :right_key=>[:album_id1, :album_id2], :left_key=>[:tag_id1, :tag_id2] +end + diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..7f65811 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,13 @@ +require 'rubygems' +require 'sequel' + +DB = Sequel.sqlite(File.join(File.dirname(File.expand_path(__FILE__)), 'db', 'fd_spec.sqlite3')) + +require File.join(File.dirname(File.expand_path(__FILE__)),"#{ENV['FD_AR'] ? 'ar' : 'sequel'}_spec_helper") + +$:.unshift(File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'lib')) +require 'fixture_dependencies' +FixtureDependencies.fixture_path = File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures') +#FixtureDependencies.verbose = 3 + +