Skip to content

Commit 9014cb4

Browse files
committed
test: Faker::Loader tests
1 parent 4003e8f commit 9014cb4

5 files changed

Lines changed: 197 additions & 7 deletions

File tree

lib/faker/loader.rb

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,18 @@ def install_on(klass)
3434
def resolve_const(context_name, class_name)
3535
load_path = build_path(context_name, class_name)
3636

37-
require(load_path)
37+
require load_path
3838
rescue LoadError
3939
# try to load default generators
40-
require(load_path.gsub('faker/', 'faker/default/'))
40+
require load_path.gsub('faker/', 'faker/default/')
41+
end
42+
43+
def lazy_loading?
44+
if defined?(@lazy_loading)
45+
@lazy_loading
46+
else
47+
@lazy_loading = @config.lazy_loading?
48+
end
4149
end
4250

4351
private
@@ -47,7 +55,12 @@ def eager_load!
4755

4856
@eager_loaded = true
4957

50-
Dir.glob(["#{@base_dir}/faker/*.rb", "#{@base_dir}/faker/**/*.rb"])
58+
paths = [
59+
"#{@base_dir}/faker/*.rb",
60+
"#{@base_dir}/faker/**/*.rb"
61+
]
62+
63+
Dir.glob(paths)
5164
.each { |f| require f }
5265
end
5366

@@ -62,9 +75,5 @@ def build_path(*constants)
6275
.downcase
6376
end.join('/')
6477
end
65-
66-
def lazy_loading?
67-
@lazy_loading ||= @config.lazy_loading?
68-
end
6978
end
7079
end

test/faker/test_loader.rb

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# frozen_string_literal: true
2+
3+
require_relative '../test_helper'
4+
5+
class TestLoader < Test::Unit::TestCase
6+
FIXTURES_DIR = File.expand_path('../fixtures', __dir__)
7+
8+
FakeConfig = Struct.new(:lazy_loading?)
9+
10+
def eager_loader = Faker::Loader.new(FIXTURES_DIR, FakeConfig.new(false))
11+
def lazy_loader = Faker::Loader.new(FIXTURES_DIR, FakeConfig.new(true))
12+
13+
def with_require_spy(fail_if: nil)
14+
original = Kernel.instance_method(:require)
15+
loaded_files = []
16+
mutex = Mutex.new
17+
18+
Kernel.define_method(:require) do |f|
19+
if fail_if&.call(f)
20+
raise LoadError
21+
end
22+
23+
mutex.synchronize { loaded_files << f }
24+
end
25+
26+
yield loaded_files
27+
ensure
28+
Kernel.define_method(:require, original)
29+
end
30+
31+
def test_strategy_does_not_change_after_first_use
32+
config = FakeConfig.new(false)
33+
loader = Faker::Loader.new(FIXTURES_DIR, config)
34+
35+
with_require_spy do
36+
loader.load_const('Faker', :Gadget)
37+
38+
config[:lazy_loading?] = true
39+
40+
refute_predicate loader, :lazy_loading?
41+
end
42+
end
43+
44+
def test_falls_back_to_default_path_on_load_error
45+
loader = lazy_loader
46+
47+
fail_when_requiring_non_default_path = ->(f) { f.end_with?('faker/gadget') }
48+
49+
with_require_spy(fail_if: fail_when_requiring_non_default_path) do |loaded_files|
50+
loader.load_const('Faker', :Gadget)
51+
52+
assert loaded_files.any? { |f| f.include?('faker/default/gadget') }
53+
end
54+
end
55+
56+
def test_inflection_resolves_correctly
57+
loader = lazy_loader
58+
59+
with_require_spy do |loaded_files|
60+
loader.load_const('Faker::Games', :DnD)
61+
62+
assert_includes loaded_files.first, 'faker/games/dnd'
63+
refute_includes loaded_files.first, 'dn_d'
64+
end
65+
end
66+
67+
def test_install_on_installs_const_missing
68+
loader = lazy_loader
69+
klass = Class.new
70+
71+
loader.install_on(klass)
72+
73+
assert_equal klass.singleton_class, klass.method(:const_missing).owner
74+
end
75+
76+
def test_eager_loads_all_files_on_first_const_access
77+
loader = eager_loader
78+
79+
with_require_spy do |loaded_files|
80+
loader.load_const('Faker', :Gadget)
81+
82+
actual_files = loaded_files.map do |loaded|
83+
loaded.match(/fixtures\/(?<path>.*)/)[:path]
84+
end.uniq
85+
86+
expected_files = %w[
87+
faker/gadget.rb
88+
faker/default/widget.rb
89+
faker/games/dnd.rb
90+
]
91+
92+
expected_files.each do |file|
93+
assert_includes actual_files, file, "expected #{file} to be loaded"
94+
end
95+
end
96+
end
97+
98+
def test_eager_loads_only_once
99+
loader = eager_loader
100+
101+
with_require_spy do |loaded_files|
102+
loader.load_const('Faker', :Gadget)
103+
104+
count = loaded_files.size
105+
106+
loader.load_const('Faker', :Gadget)
107+
108+
assert_equal count, loaded_files.size
109+
end
110+
end
111+
112+
def test_lazy_loads_single_file_on_const_access
113+
loader = lazy_loader
114+
115+
with_require_spy do |loaded_files|
116+
loader.load_const('Faker', :Gadget)
117+
118+
assert_equal 1, loaded_files.size
119+
assert_includes loaded_files.first, 'faker/gadget'
120+
end
121+
end
122+
123+
def test_eager_loads_only_once_across_threads
124+
loader = eager_loader
125+
126+
with_require_spy do |loaded_files|
127+
threads = 10.times.map do
128+
Thread.new { loader.load_const('Faker', :Gadget) }
129+
end
130+
131+
threads.each(&:join)
132+
133+
expected_files = %w[
134+
faker/gadget.rb
135+
faker/gadget.rb
136+
faker/default/widget.rb
137+
faker/games/dnd.rb
138+
]
139+
140+
actual_files = loaded_files.map do |loaded|
141+
loaded.match(/fixtures\/(?<path>.*)/)[:path]
142+
end.compact
143+
144+
assert_equal expected_files.sort, actual_files.sort
145+
end
146+
end
147+
148+
def test_raises_on_unknown_const
149+
loader = lazy_loader
150+
151+
assert_raises(LoadError) { loader.load_const('Faker', :NonExistent) }
152+
end
153+
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
3+
module Faker
4+
class Default
5+
# rubocop:disable Lint/EmptyClass
6+
class Widget
7+
end
8+
# rubocop:enable Lint/EmptyClass
9+
end
10+
end

test/fixtures/faker/gadget.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# frozen_string_literal: true
2+
3+
module Faker
4+
# rubocop:disable Lint/EmptyClass
5+
class Gadget
6+
end
7+
# rubocop:enable Lint/EmptyClass
8+
end

test/fixtures/faker/games/dnd.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
3+
module Faker
4+
class Games
5+
# rubocop:disable Lint/EmptyClass
6+
class DnD
7+
end
8+
# rubocop:enable Lint/EmptyClass
9+
end
10+
end

0 commit comments

Comments
 (0)