File: lint_spec.rb

package info (click to toggle)
ruby-factory-bot 6.5.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,372 kB
  • sloc: ruby: 7,827; makefile: 6
file content (218 lines) | stat: -rw-r--r-- 4,985 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
describe "FactoryBot.lint" do
  it "raises when a factory is invalid" do
    define_model "User", name: :string do
      validates :name, presence: true
    end

    define_model "AlwaysValid"

    FactoryBot.define do
      factory :user do
        factory :admin_user
      end

      factory :always_valid
    end

    error_message = <<~ERROR_MESSAGE.strip
      The following factories are invalid:

      * user - Validation failed: Name can't be blank (ActiveRecord::RecordInvalid)
      * admin_user - Validation failed: Name can't be blank (ActiveRecord::RecordInvalid)
    ERROR_MESSAGE

    expect {
      FactoryBot.lint
    }.to raise_error FactoryBot::InvalidFactoryError, error_message
  end

  it "executes linting in an ActiveRecord::Base transaction" do
    define_model "User", name: :string do
      validates :name, uniqueness: true
    end

    define_model "AlwaysValid"

    FactoryBot.define do
      factory :user do
        factory :admin_user
      end

      factory :always_valid
    end

    expect { FactoryBot.lint }.to_not raise_error
  end

  it "does not raise when all factories are valid" do
    define_model "User", name: :string do
      validates :name, presence: true
    end

    FactoryBot.define do
      factory :user do
        name { "assigned" }
      end
    end

    expect { FactoryBot.lint }.not_to raise_error
  end

  it "allows for selective linting" do
    define_model "InvalidThing", name: :string do
      validates :name, presence: true
    end

    define_model "ValidThing", name: :string

    FactoryBot.define do
      factory :valid_thing
      factory :invalid_thing
    end

    expect {
      only_valid_factories = FactoryBot.factories.reject { |factory|
        factory.name =~ /invalid/
      }

      FactoryBot.lint only_valid_factories
    }.not_to raise_error
  end

  describe "trait validation" do
    context "enabled" do
      it "raises if a trait produces an invalid object" do
        define_model "User", name: :string do
          validates :name, presence: true
        end

        FactoryBot.define do
          factory :user do
            name { "Yep" }
            trait :unnamed do
              name { nil }
            end
          end
        end

        error_message = <<~ERROR_MESSAGE.strip
          The following factories are invalid:

          * user+unnamed - Validation failed: Name can't be blank (ActiveRecord::RecordInvalid)
        ERROR_MESSAGE

        expect {
          FactoryBot.lint traits: true
        }.to raise_error FactoryBot::InvalidFactoryError, error_message
      end

      it "does not raise if a trait produces a valid object" do
        define_model "User", name: :string do
          validates :name, presence: true
        end

        FactoryBot.define do
          factory :user do
            name { "Yep" }
            trait :renamed do
              name { "Yessir" }
            end
          end
        end

        expect {
          FactoryBot.lint traits: true
        }.not_to raise_error
      end
    end

    context "disabled" do
      it "does not raises if a trait produces an invalid object" do
        define_model "User", name: :string do
          validates :name, presence: true
        end

        FactoryBot.define do
          factory :user do
            name { "Yep" }
            trait :unnamed do
              name { nil }
            end
          end
        end

        expect {
          FactoryBot.lint traits: false
          FactoryBot.lint
        }.not_to raise_error
      end
    end
  end

  describe "factory strategy for linting" do
    it "uses the requested strategy" do
      define_class "User" do
        attr_accessor :name

        def save!
          raise "expected :build strategy, #save! shouldn't be invoked"
        end
      end

      FactoryBot.define do
        factory :user do
          name { "Barbara" }
        end
      end

      expect {
        FactoryBot.lint strategy: :build
      }.not_to raise_error
    end

    it "uses the requested strategy during trait validation" do
      define_class "User" do
        attr_accessor :name

        def save!
          raise "expected :build strategy, #save! shouldn't be invoked"
        end
      end

      FactoryBot.define do
        factory :user do
          name { "Barbara" }

          trait :male do
            name { "Bob" }
          end
        end
      end

      expect {
        FactoryBot.lint traits: true, strategy: :build
      }.not_to raise_error
    end
  end

  describe "verbose linting" do
    it "prints the backtrace for each factory error" do
      define_class("InvalidThing") do
        def save!
          raise "invalid"
        end
      end

      FactoryBot.define do
        factory :invalid_thing
      end

      expect {
        FactoryBot.lint(verbose: true)
      }.to raise_error(
        FactoryBot::InvalidFactoryError,
        %r{#{__FILE__}:\d*:in `save!'}
      )
    end
  end
end