Skip to content

Commit dd9026d

Browse files
committed
test: Faker::Loader tests
1 parent 7def9ec commit dd9026d

5 files changed

Lines changed: 197 additions & 11 deletions

File tree

lib/faker/loader.rb

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def initialize(base_dir, config)
1313

1414
def load_const(context_name, class_name)
1515
@mutex.synchronize do
16-
if lazy_loading?
16+
if loading_strategy == :lazy
1717
resolve_const(context_name, class_name)
1818
else
1919
eager_load!
@@ -31,24 +31,40 @@ def install_on(klass)
3131
end
3232
end
3333

34+
def loading_strategy
35+
if defined?(@loading_strategy)
36+
@loading_strategy
37+
else
38+
@loading_strategy = if @config.lazy_loading?
39+
:lazy
40+
else
41+
:eager
42+
end
43+
end
44+
end
45+
46+
private
47+
3448
def resolve_const(context_name, class_name)
3549
load_path = build_path(context_name, class_name)
3650

37-
require(load_path)
51+
require load_path
3852
rescue LoadError
3953
# try to load default generators
40-
require(load_path.gsub('faker/', 'faker/default/'))
54+
require load_path.gsub('faker/', 'faker/default/')
4155
end
4256

43-
private
44-
4557
def eager_load!
4658
return if @eager_loaded
4759

4860
@eager_loaded = true
4961

50-
Dir.glob(["#{@base_dir}/faker/*.rb", "#{@base_dir}/faker/**/*.rb"])
51-
.each { |f| require f }
62+
paths = [
63+
"#{@base_dir}/faker/*.rb",
64+
"#{@base_dir}/faker/**/*.rb"
65+
]
66+
67+
Dir.glob(paths).uniq.each { |f| require f }
5268
end
5369

5470
def build_path(*constants)
@@ -62,9 +78,5 @@ def build_path(*constants)
6278
.downcase
6379
end.join('/')
6480
end
65-
66-
def lazy_loading?
67-
@lazy_loading ||= @config.lazy_loading?
68-
end
6981
end
7082
end

test/faker/test_loader.rb

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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+
assert_equal :eager, loader.loading_strategy
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+
actual_files = loaded_files.map do |loaded|
134+
loaded.match(/fixtures\/(?<path>.*)/)[:path]
135+
end.compact
136+
137+
assert_equal actual_files.uniq, actual_files
138+
end
139+
end
140+
141+
def test_raises_on_unknown_const
142+
loader = lazy_loader
143+
144+
assert_raises(LoadError) { loader.load_const('Faker', :NonExistent) }
145+
end
146+
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)