factory_bot 6.0.2 → 6.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +6 -0
- data/GETTING_STARTED.md +311 -73
- data/NEWS.md +21 -0
- data/README.md +4 -10
- data/lib/factory_bot/aliases.rb +1 -1
- data/lib/factory_bot/attribute/dynamic.rb +1 -1
- data/lib/factory_bot/attribute_assigner.rb +3 -2
- data/lib/factory_bot/callback.rb +1 -1
- data/lib/factory_bot/declaration/association.rb +23 -6
- data/lib/factory_bot/decorator/invocation_tracker.rb +1 -0
- data/lib/factory_bot/decorator.rb +18 -6
- data/lib/factory_bot/definition.rb +15 -1
- data/lib/factory_bot/definition_proxy.rb +1 -1
- data/lib/factory_bot/evaluator.rb +4 -3
- data/lib/factory_bot/registry.rb +1 -1
- data/lib/factory_bot/strategy/attributes_for.rb +4 -0
- data/lib/factory_bot/strategy/build.rb +4 -0
- data/lib/factory_bot/strategy/create.rb +4 -0
- data/lib/factory_bot/strategy/null.rb +4 -0
- data/lib/factory_bot/strategy/stub.rb +4 -0
- data/lib/factory_bot/syntax/default.rb +1 -1
- data/lib/factory_bot/trait.rb +1 -1
- data/lib/factory_bot/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3bcbbd8bee01abdb25a5cfdbbbbca29396f587236f0c20f8300adbf6e32f783c
|
4
|
+
data.tar.gz: 94d8e73507ae61edba7fb3129ee74c4fe9998a8fb78614ecba9966199561c8e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e97d9530810f0de00f6e0129df37db52722ee8d430f7b118ffdcebdc0eecc1e1532356b7c8e01cc85265c04f18eb174d3d5ff09fe235c5f30ceb7b5e16432508
|
7
|
+
data.tar.gz: 71b77c30b16c89588d6946505be547374321597e8e16f89673766cda94b22188dc28070a34144ae126dad93042de0173e1d92173237ae1e929a3131e85569a51
|
data/CONTRIBUTING.md
CHANGED
@@ -51,6 +51,12 @@ Here are some ways *you* can contribute:
|
|
51
51
|
asking for help. We love helping!
|
52
52
|
* Please don't update the Gem version.
|
53
53
|
|
54
|
+
## Setting up
|
55
|
+
|
56
|
+
```sh
|
57
|
+
bundle install
|
58
|
+
```
|
59
|
+
|
54
60
|
## Running the test suite
|
55
61
|
|
56
62
|
The default rake task will run the full test suite and [standard]:
|
data/GETTING_STARTED.md
CHANGED
@@ -37,12 +37,15 @@ Getting Started
|
|
37
37
|
* [Associations](#associations)
|
38
38
|
+ [Implicit definition](#implicit-definition)
|
39
39
|
+ [Explicit definition](#explicit-definition)
|
40
|
+
+ [Inline definition](#inline-definition)
|
40
41
|
+ [Specifying the factory](#specifying-the-factory)
|
41
42
|
+ [Overriding attributes](#overriding-attributes)
|
43
|
+
+ [Association overrides](#association-overrides)
|
42
44
|
+ [Build strategies](#build-strategies-1)
|
43
45
|
+ [`has_many` associations](#has_many-associations)
|
44
46
|
+ [`has_and_belongs_to_many` associations](#has_and_belongs_to_many-associations)
|
45
47
|
+ [Polymorphic associations](#polymorphic-associations)
|
48
|
+
+ [Interconnected associations](#interconnected-associations)
|
46
49
|
* [Sequences](#sequences)
|
47
50
|
+ [Global sequences](#global-sequences)
|
48
51
|
+ [With dynamic attributes](#with-dynamic-attributes)
|
@@ -52,6 +55,7 @@ Getting Started
|
|
52
55
|
+ [Without a block](#without-a-block)
|
53
56
|
+ [Aliases](#aliases-1)
|
54
57
|
+ [Rewinding](#rewinding)
|
58
|
+
+ [Uniqueness](#uniqueness)
|
55
59
|
* [Traits](#traits)
|
56
60
|
+ [Defining traits](#defining-traits)
|
57
61
|
+ [As implicit attributes](#as-implicit-attributes-1)
|
@@ -196,17 +200,15 @@ It is also possible to explicitly specify the class:
|
|
196
200
|
|
197
201
|
```ruby
|
198
202
|
# This will use the User class (otherwise Admin would have been guessed)
|
199
|
-
factory :admin, class: User
|
203
|
+
factory :admin, class: "User"
|
200
204
|
```
|
201
205
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
which factory\_bot will constantize later, once you start building objects:
|
206
|
+
You can pass a constant as well, if the constant is available (note that this
|
207
|
+
can cause test performance problems in large Rails applications, since
|
208
|
+
referring to the constant will cause it to be eagerly loaded).
|
206
209
|
|
207
210
|
```ruby
|
208
|
-
|
209
|
-
factory :access_token, class: "Doorkeeper::AccessToken"
|
211
|
+
factory :access_token, class: User
|
210
212
|
```
|
211
213
|
|
212
214
|
### Hash attributes
|
@@ -311,17 +313,17 @@ factory :user, aliases: [:author, :commenter] do
|
|
311
313
|
end
|
312
314
|
|
313
315
|
factory :post do
|
314
|
-
author
|
315
|
-
# instead of
|
316
|
+
# The alias allows us to write author instead of
|
316
317
|
# association :author, factory: :user
|
318
|
+
author
|
317
319
|
title { "How to read a book effectively" }
|
318
320
|
body { "There are five steps involved." }
|
319
321
|
end
|
320
322
|
|
321
323
|
factory :comment do
|
322
|
-
commenter
|
323
|
-
# instead of
|
324
|
+
# The alias allows us to write commenter instead of
|
324
325
|
# association :commenter, factory: :user
|
326
|
+
commenter
|
325
327
|
body { "Great article!" }
|
326
328
|
end
|
327
329
|
```
|
@@ -345,6 +347,7 @@ create(:user, last_name: "Doe").email
|
|
345
347
|
|
346
348
|
Transient Attributes
|
347
349
|
--------------------
|
350
|
+
Transient attributes are attributes only available within the factory definition, and not set on the object being built. This allows for more complex logic inside factories.
|
348
351
|
|
349
352
|
### With other attributes
|
350
353
|
|
@@ -504,6 +507,19 @@ factory :post do
|
|
504
507
|
end
|
505
508
|
```
|
506
509
|
|
510
|
+
### Inline definition
|
511
|
+
|
512
|
+
You can also define associations inline within regular attributes,
|
513
|
+
but note that the value will be `nil`
|
514
|
+
when using the `attributes_for` strategy.
|
515
|
+
|
516
|
+
```ruby
|
517
|
+
factory :post do
|
518
|
+
# ...
|
519
|
+
author { association :author }
|
520
|
+
end
|
521
|
+
```
|
522
|
+
|
507
523
|
### Specifying the factory
|
508
524
|
|
509
525
|
You can specify a different factory (although [Aliases](#aliases) might also
|
@@ -527,6 +543,15 @@ factory :post do
|
|
527
543
|
end
|
528
544
|
```
|
529
545
|
|
546
|
+
Inline:
|
547
|
+
|
548
|
+
```ruby
|
549
|
+
factory :post do
|
550
|
+
# ...
|
551
|
+
author { association :user }
|
552
|
+
end
|
553
|
+
```
|
554
|
+
|
530
555
|
### Overriding attributes
|
531
556
|
|
532
557
|
You can also override attributes.
|
@@ -550,6 +575,35 @@ factory :post do
|
|
550
575
|
end
|
551
576
|
```
|
552
577
|
|
578
|
+
Or inline using attributes from the factory:
|
579
|
+
|
580
|
+
```rb
|
581
|
+
factory :post do
|
582
|
+
# ...
|
583
|
+
author_last_name { "Writely" }
|
584
|
+
author { association :author, last_name: author_last_name }
|
585
|
+
end
|
586
|
+
```
|
587
|
+
|
588
|
+
### Association overrides
|
589
|
+
|
590
|
+
Attribute overrides can be used to link associated objects:
|
591
|
+
|
592
|
+
```ruby
|
593
|
+
FactoryBot.define do
|
594
|
+
factory :author do
|
595
|
+
name { 'Taylor' }
|
596
|
+
end
|
597
|
+
|
598
|
+
factory :post do
|
599
|
+
author
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
eunji = build(:author, name: 'Eunji')
|
604
|
+
post = build(:post, author: eunji)
|
605
|
+
```
|
606
|
+
|
553
607
|
### Build strategies
|
554
608
|
|
555
609
|
In factory\_bot 5, associations default to using the same build strategy as
|
@@ -619,27 +673,51 @@ factory :post do
|
|
619
673
|
|
620
674
|
### `has_many` associations
|
621
675
|
|
622
|
-
|
623
|
-
|
624
|
-
|
676
|
+
There are a few ways to generate data for a `has_many` relationship. The
|
677
|
+
simplest approach is to write a helper method in plain Ruby to tie together the
|
678
|
+
different records:
|
625
679
|
|
626
680
|
```ruby
|
627
681
|
FactoryBot.define do
|
682
|
+
factory :post do
|
683
|
+
title { "Through the Looking Glass" }
|
684
|
+
user
|
685
|
+
end
|
628
686
|
|
629
|
-
|
687
|
+
factory :user do
|
688
|
+
name { "Rachel Sanchez" }
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
def user_with_posts(posts_count: 5)
|
693
|
+
FactoryBot.create(:user) do |user|
|
694
|
+
FactoryBot.create_list(:post, posts_count, user: user)
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
create(:user).posts.length # 0
|
699
|
+
user_with_posts.posts.length # 5
|
700
|
+
user_with_posts(posts_count: 15).posts.length # 15
|
701
|
+
```
|
702
|
+
|
703
|
+
If you prefer to keep the object creation fully within factory\_bot, you can
|
704
|
+
build the posts in an `after(:create)` callback.
|
705
|
+
|
706
|
+
|
707
|
+
```ruby
|
708
|
+
FactoryBot.define do
|
630
709
|
factory :post do
|
631
710
|
title { "Through the Looking Glass" }
|
632
711
|
user
|
633
712
|
end
|
634
713
|
|
635
|
-
# user factory without associated posts
|
636
714
|
factory :user do
|
637
715
|
name { "John Doe" }
|
638
716
|
|
639
717
|
# user_with_posts will create post data after the user has been created
|
640
718
|
factory :user_with_posts do
|
641
|
-
# posts_count is declared as a transient attribute
|
642
|
-
#
|
719
|
+
# posts_count is declared as a transient attribute available in the
|
720
|
+
# callback via the evaluator
|
643
721
|
transient do
|
644
722
|
posts_count { 5 }
|
645
723
|
end
|
@@ -650,71 +728,122 @@ FactoryBot.define do
|
|
650
728
|
# to create and we make sure the user is associated properly to the post
|
651
729
|
after(:create) do |user, evaluator|
|
652
730
|
create_list(:post, evaluator.posts_count, user: user)
|
731
|
+
|
732
|
+
# You may need to reload the record here, depending on your application
|
733
|
+
user.reload
|
653
734
|
end
|
654
735
|
end
|
655
736
|
end
|
656
737
|
end
|
738
|
+
|
739
|
+
create(:user).posts.length # 0
|
740
|
+
create(:user_with_posts).posts.length # 5
|
741
|
+
create(:user_with_posts, posts_count: 15).posts.length # 15
|
657
742
|
```
|
658
743
|
|
659
|
-
|
744
|
+
Or, for a solution that works with `build`, `build_stubbed`, and `create`
|
745
|
+
(although it doesn't work well with `attributes_for`), you can use inline
|
746
|
+
associations:
|
660
747
|
|
661
748
|
```ruby
|
749
|
+
FactoryBot.define do
|
750
|
+
factory :post do
|
751
|
+
title { "Through the Looking Glass" }
|
752
|
+
user
|
753
|
+
end
|
754
|
+
|
755
|
+
factory :user do
|
756
|
+
name { "Taylor Kim" }
|
757
|
+
|
758
|
+
factory :user_with_posts do
|
759
|
+
posts { [association(:post)] }
|
760
|
+
end
|
761
|
+
end
|
762
|
+
end
|
763
|
+
|
662
764
|
create(:user).posts.length # 0
|
765
|
+
create(:user_with_posts).posts.length # 1
|
766
|
+
build(:user_with_posts).posts.length # 1
|
767
|
+
build_stubbed(:user_with_posts).posts.length # 1
|
768
|
+
```
|
769
|
+
|
770
|
+
For more flexibility you can combine this with the `posts_count` transient
|
771
|
+
attribute from the callback example:
|
772
|
+
|
773
|
+
```ruby
|
774
|
+
FactoryBot.define do
|
775
|
+
factory :post do
|
776
|
+
title { "Through the Looking Glass" }
|
777
|
+
user
|
778
|
+
end
|
779
|
+
|
780
|
+
factory :user do
|
781
|
+
name { "Adiza Kumato" }
|
782
|
+
|
783
|
+
factory :user_with_posts do
|
784
|
+
transient do
|
785
|
+
posts_count { 5 }
|
786
|
+
end
|
787
|
+
|
788
|
+
posts do
|
789
|
+
Array.new(posts_count) { association(:post) }
|
790
|
+
end
|
791
|
+
end
|
792
|
+
end
|
793
|
+
end
|
794
|
+
|
663
795
|
create(:user_with_posts).posts.length # 5
|
664
796
|
create(:user_with_posts, posts_count: 15).posts.length # 15
|
797
|
+
build(:user_with_posts, posts_count: 15).posts.length # 15
|
798
|
+
build_stubbed(:user_with_posts, posts_count: 15).posts.length # 15
|
665
799
|
```
|
666
800
|
|
667
801
|
### `has_and_belongs_to_many` associations
|
668
802
|
|
669
803
|
Generating data for a `has_and_belongs_to_many` relationship is very similar
|
670
|
-
to the above `has_many` relationship, with a small change
|
804
|
+
to the above `has_many` relationship, with a small change: you need to pass an
|
671
805
|
array of objects to the model's pluralized attribute name rather than a single
|
672
806
|
object to the singular version of the attribute name.
|
673
807
|
|
674
|
-
Here's an example with two models that are related via
|
675
|
-
`has_and_belongs_to_many`:
|
676
808
|
|
677
809
|
```ruby
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
factory :language do
|
682
|
-
title { "Through the Looking Glass" }
|
683
|
-
profile
|
810
|
+
def profile_with_languages(languages_count: 2)
|
811
|
+
FactoryBot.create(:profile) do |profile|
|
812
|
+
FactoryBot.create_list(:language, languages_count, profiles: [profile])
|
684
813
|
end
|
814
|
+
end
|
815
|
+
```
|
685
816
|
|
686
|
-
|
687
|
-
factory :profile do
|
688
|
-
name { "John Doe" }
|
817
|
+
Or with the callback approach:
|
689
818
|
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
transient do
|
696
|
-
languages_count { 5 }
|
697
|
-
end
|
819
|
+
```ruby
|
820
|
+
factory :profile_with_languages do
|
821
|
+
transient do
|
822
|
+
languages_count { 2 }
|
823
|
+
end
|
698
824
|
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
# records to create and we make sure the profile is associated properly
|
703
|
-
# to the language
|
704
|
-
after(:create) do |profile, evaluator|
|
705
|
-
create_list(:language, evaluator.languages_count, profiles: [profile])
|
706
|
-
end
|
707
|
-
end
|
825
|
+
after(:create) do |profile, evaluator|
|
826
|
+
create_list(:language, evaluator.languages_count, profiles: [profile])
|
827
|
+
profile.reload
|
708
828
|
end
|
709
829
|
end
|
710
830
|
```
|
711
831
|
|
712
|
-
|
832
|
+
Or the inline association approach (note the use of the `instance` method here
|
833
|
+
to refer to the profile being built):
|
713
834
|
|
714
835
|
```ruby
|
715
|
-
|
716
|
-
|
717
|
-
|
836
|
+
factory :profile_with_languages do
|
837
|
+
transient do
|
838
|
+
languages_count { 2 }
|
839
|
+
end
|
840
|
+
|
841
|
+
languages do
|
842
|
+
Array.new(languages_count) do
|
843
|
+
association(:language, profiles: [instance])
|
844
|
+
end
|
845
|
+
end
|
846
|
+
end
|
718
847
|
```
|
719
848
|
|
720
849
|
### Polymorphic associations
|
@@ -748,6 +877,61 @@ create(:comment, :for_video)
|
|
748
877
|
create(:comment, :for_photo)
|
749
878
|
```
|
750
879
|
|
880
|
+
### Interconnected associations
|
881
|
+
|
882
|
+
There are limitless ways objects might be interconnected, and
|
883
|
+
factory\_bot may not always be suited to handle those relationships. In some
|
884
|
+
cases it makes sense to use factory\_bot to build each individual object, and
|
885
|
+
then to write helper methods in plain Ruby to tie those objects together.
|
886
|
+
|
887
|
+
That said, some more complex, interconnected relationships can be built in factory\_bot
|
888
|
+
using inline associations with reference to the `instance` being built.
|
889
|
+
|
890
|
+
Let's say your models look like this, where an associated `Student` and
|
891
|
+
`Profile` should both belong to the same `School`:
|
892
|
+
|
893
|
+
```ruby
|
894
|
+
class Student < ApplicationRecord
|
895
|
+
belongs_to :school
|
896
|
+
has_one :profile
|
897
|
+
end
|
898
|
+
|
899
|
+
class Profile < ApplicationRecord
|
900
|
+
belongs_to :school
|
901
|
+
belongs_to :student
|
902
|
+
end
|
903
|
+
|
904
|
+
class School < ApplicationRecord
|
905
|
+
has_many :students
|
906
|
+
has_many :profiles
|
907
|
+
end
|
908
|
+
```
|
909
|
+
|
910
|
+
We can ensure the student and profile are connected to each other and to the
|
911
|
+
same school with a factory like this:
|
912
|
+
|
913
|
+
```ruby
|
914
|
+
FactoryBot.define do
|
915
|
+
factory :student do
|
916
|
+
school
|
917
|
+
profile { association :profile, student: instance, school: school }
|
918
|
+
end
|
919
|
+
|
920
|
+
factory :profile do
|
921
|
+
school
|
922
|
+
student { association :student, profile: instance, school: school }
|
923
|
+
end
|
924
|
+
|
925
|
+
factory :school
|
926
|
+
end
|
927
|
+
```
|
928
|
+
|
929
|
+
Note that this approach works with `build`, `build_stubbed`, and `create`, but
|
930
|
+
the associations will return `nil` when using `attributes_for`.
|
931
|
+
|
932
|
+
Also, note that if you assign any attributes inside a custom `initialize_with`
|
933
|
+
(e.g. `initialize_with { new(**attributes) }`), those attributes should not refer to `instance`,
|
934
|
+
since it will be `nil`.
|
751
935
|
|
752
936
|
Sequences
|
753
937
|
---------
|
@@ -810,7 +994,7 @@ end
|
|
810
994
|
|
811
995
|
### Initial value
|
812
996
|
|
813
|
-
You can override the initial value. Any value that
|
997
|
+
You can override the initial value. Any value that responds to the `#next`
|
814
998
|
method will work (e.g. 1, 2, 3, 'a', 'b', 'c')
|
815
999
|
|
816
1000
|
```ruby
|
@@ -829,6 +1013,15 @@ factory :post do
|
|
829
1013
|
end
|
830
1014
|
```
|
831
1015
|
|
1016
|
+
Please note, that the value for the sequence could be any Enumerable instance,
|
1017
|
+
as long as it responds to `#next`:
|
1018
|
+
|
1019
|
+
```ruby
|
1020
|
+
factory :task do
|
1021
|
+
sequence :priority, %i[low medium high urgent].cycle
|
1022
|
+
end
|
1023
|
+
```
|
1024
|
+
|
832
1025
|
### Aliases
|
833
1026
|
|
834
1027
|
Sequences can also have aliases. The sequence aliases share the same counter:
|
@@ -878,6 +1071,22 @@ generate(:email) # "person1@example.com"
|
|
878
1071
|
|
879
1072
|
This rewinds all registered sequences.
|
880
1073
|
|
1074
|
+
### Uniqueness
|
1075
|
+
|
1076
|
+
When working with uniqueness constraints, be careful not to pass in override values that will conflict with the generated sequence values.
|
1077
|
+
|
1078
|
+
In this example the email will be the same for both users. If email must be unique, this code will error:
|
1079
|
+
|
1080
|
+
```rb
|
1081
|
+
factory :user do
|
1082
|
+
sequence(:email) { |n| "person#{n}@example.com" }
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
FactoryBot.create(:user, email: "person1@example.com")
|
1086
|
+
FactoryBot.create(:user)
|
1087
|
+
```
|
1088
|
+
|
1089
|
+
|
881
1090
|
Traits
|
882
1091
|
------
|
883
1092
|
|
@@ -943,16 +1152,16 @@ factory :user do
|
|
943
1152
|
name { "Friendly User" }
|
944
1153
|
login { name }
|
945
1154
|
|
946
|
-
trait :
|
1155
|
+
trait :active do
|
947
1156
|
name { "John Doe" }
|
948
|
-
|
949
|
-
login { "#{name} (
|
1157
|
+
status { :active }
|
1158
|
+
login { "#{name} (active)" }
|
950
1159
|
end
|
951
1160
|
|
952
|
-
trait :
|
1161
|
+
trait :inactive do
|
953
1162
|
name { "Jane Doe" }
|
954
|
-
|
955
|
-
login { "#{name} (
|
1163
|
+
status { :inactive }
|
1164
|
+
login { "#{name} (inactive)" }
|
956
1165
|
end
|
957
1166
|
|
958
1167
|
trait :admin do
|
@@ -960,8 +1169,8 @@ factory :user do
|
|
960
1169
|
login { "admin-#{name}" }
|
961
1170
|
end
|
962
1171
|
|
963
|
-
factory :
|
964
|
-
factory :
|
1172
|
+
factory :active_admin, traits: [:active, :admin] # login will be "admin-John Doe"
|
1173
|
+
factory :inactive_admin, traits: [:admin, :inactive] # login will be "Jane Doe (inactive)"
|
965
1174
|
end
|
966
1175
|
```
|
967
1176
|
|
@@ -974,19 +1183,41 @@ factory :user do
|
|
974
1183
|
name { "Friendly User" }
|
975
1184
|
login { name }
|
976
1185
|
|
977
|
-
trait :
|
1186
|
+
trait :active do
|
978
1187
|
name { "John Doe" }
|
979
|
-
|
1188
|
+
status { :active }
|
980
1189
|
login { "#{name} (M)" }
|
981
1190
|
end
|
982
1191
|
|
983
1192
|
factory :brandon do
|
984
|
-
|
1193
|
+
active
|
985
1194
|
name { "Brandon" }
|
986
1195
|
end
|
987
1196
|
end
|
988
1197
|
```
|
989
1198
|
|
1199
|
+
### As mixins
|
1200
|
+
|
1201
|
+
Traits can be defined outside of factories and used as mixins to compose shared attributes
|
1202
|
+
|
1203
|
+
```ruby
|
1204
|
+
FactoryBot.define do
|
1205
|
+
trait :timestamps do
|
1206
|
+
created_at { 8.days.ago }
|
1207
|
+
updated_at { 4.days.ago }
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
factory :user, traits: [:timestamps] do
|
1211
|
+
username { "john_doe" }
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
factory :post do
|
1215
|
+
timestamps
|
1216
|
+
title { "Traits rock" }
|
1217
|
+
end
|
1218
|
+
end
|
1219
|
+
```
|
1220
|
+
|
990
1221
|
### Using traits
|
991
1222
|
|
992
1223
|
Traits can also be passed in as a list of symbols when you construct an instance
|
@@ -996,9 +1227,9 @@ from factory\_bot.
|
|
996
1227
|
factory :user do
|
997
1228
|
name { "Friendly User" }
|
998
1229
|
|
999
|
-
trait :
|
1230
|
+
trait :active do
|
1000
1231
|
name { "John Doe" }
|
1001
|
-
|
1232
|
+
status { :active }
|
1002
1233
|
end
|
1003
1234
|
|
1004
1235
|
trait :admin do
|
@@ -1006,8 +1237,8 @@ factory :user do
|
|
1006
1237
|
end
|
1007
1238
|
end
|
1008
1239
|
|
1009
|
-
# creates an admin user with
|
1010
|
-
create(:user, :admin, :
|
1240
|
+
# creates an admin user with :active status and name "Jon Snow"
|
1241
|
+
create(:user, :admin, :active, name: "Jon Snow")
|
1011
1242
|
```
|
1012
1243
|
|
1013
1244
|
This ability works with `build`, `build_stubbed`, `attributes_for`, and `create`.
|
@@ -1025,8 +1256,8 @@ factory :user do
|
|
1025
1256
|
end
|
1026
1257
|
end
|
1027
1258
|
|
1028
|
-
# creates 3 admin users with
|
1029
|
-
create_list(:user, 3, :admin, :
|
1259
|
+
# creates 3 admin users with :active status and name "Jon Snow"
|
1260
|
+
create_list(:user, 3, :admin, :active, name: "Jon Snow")
|
1030
1261
|
```
|
1031
1262
|
|
1032
1263
|
### With associations
|
@@ -1325,7 +1556,6 @@ FactoryBot.define do
|
|
1325
1556
|
factory :application_user, parent: :user do
|
1326
1557
|
full_name { "Jane Doe" }
|
1327
1558
|
date_of_birth { 21.years.ago }
|
1328
|
-
gender { "Female" }
|
1329
1559
|
health { 90 }
|
1330
1560
|
end
|
1331
1561
|
end
|
@@ -1338,7 +1568,6 @@ FactoryBot.modify do
|
|
1338
1568
|
factory :user do
|
1339
1569
|
full_name { "Jane Doe" }
|
1340
1570
|
date_of_birth { 21.years.ago }
|
1341
|
-
gender { "Female" }
|
1342
1571
|
health { 90 }
|
1343
1572
|
end
|
1344
1573
|
end
|
@@ -1376,6 +1605,15 @@ twenty_somethings = build_list(:user, 10) do |user, i|
|
|
1376
1605
|
end
|
1377
1606
|
```
|
1378
1607
|
|
1608
|
+
`create_list` passes saved instances into the block. If you modify the instance, you must save it again:
|
1609
|
+
|
1610
|
+
```ruby
|
1611
|
+
twenty_somethings = create_list(:user, 10) do |user, i|
|
1612
|
+
user.date_of_birth = (20 + i).years.ago
|
1613
|
+
user.save!
|
1614
|
+
end
|
1615
|
+
```
|
1616
|
+
|
1379
1617
|
`build_stubbed_list` will give you fully stubbed out instances:
|
1380
1618
|
|
1381
1619
|
```ruby
|
@@ -1550,7 +1788,7 @@ factory :user do
|
|
1550
1788
|
|
1551
1789
|
name "John Doe"
|
1552
1790
|
|
1553
|
-
initialize_with { new(attributes) }
|
1791
|
+
initialize_with { new(**attributes) }
|
1554
1792
|
end
|
1555
1793
|
```
|
1556
1794
|
|
data/NEWS.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# News
|
2
2
|
|
3
|
+
## 6.2.1 (March 8, 2022)
|
4
|
+
* Added: CI testing against truffleruby
|
5
|
+
* Changed: Documentation improvements for sequences and traits
|
6
|
+
* Fixed: ActiveSupport::Notifications reporting strategy through associations now report as symbols
|
7
|
+
* Fixed: `add_attribute` with reserved keywords assigns values correctly
|
8
|
+
|
9
|
+
## 6.2.0 (May 7, 2021)
|
10
|
+
* Added: support for Ruby 3.0
|
11
|
+
* Changed: Include factory or trait name in error messages for missing traits. d05a9a3c
|
12
|
+
* Changed: Switched from Travis CI to GitHub Actions
|
13
|
+
* Fixed: More Ruby 2.7 kwarg deprecation warnings
|
14
|
+
|
15
|
+
## 6.1.0 (July 8, 2020)
|
16
|
+
* Added: public reader for the evaluation instance, helpful for building interrelated associations
|
17
|
+
* Changed: raise a more helpful error when passing an invalid argument to an association
|
18
|
+
* Fixed: Ruby 2.7 kwarg deprecation warnings
|
19
|
+
|
3
20
|
## 6.0.2 (June 19, 2020)
|
4
21
|
* Fixed: bug causing traits to consume more memory each time they were used
|
5
22
|
|
@@ -8,10 +25,14 @@
|
|
8
25
|
|
9
26
|
## 6.0.0 (June 18, 2020)
|
10
27
|
* Added: automatic definition of traits for Active Record enum attributes, enabled by default
|
28
|
+
(Note that this required changing where factory_bot constantizes the build
|
29
|
+
class, which may affect applications that were using abstract factories for
|
30
|
+
inheritance. See issue #1409.)
|
11
31
|
* Added: `traits_for_enum` method to define traits for non-Active Record enums
|
12
32
|
* Added: `build_stubbed_starting_id=` option to define the starting id for `build_stubbed`
|
13
33
|
* Removed: deprecated methods on the top-level `FactoryBot` module meant only for internal use
|
14
34
|
* Removed: support for EOL versions of Ruby (2.3, 2.4) and Rails (4.2)
|
35
|
+
* Removed: support for "abstract" factories with no associated class; use traits instead.
|
15
36
|
|
16
37
|
## 5.2.0 (April 24, 2020)
|
17
38
|
* Added: Pass index to block for `*_list` methods
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# factory_bot [![Build Status][ci-image]][ci] [![Code Climate][grade-image]][grade] [![Gem Version][version-image]][version]
|
1
|
+
# factory_bot [![Build Status][ci-image]][ci] [![Code Climate][grade-image]][grade] [![Gem Version][version-image]][version]
|
2
2
|
|
3
3
|
factory_bot is a fixtures replacement with a straightforward definition syntax, support for multiple build strategies (saved instances, unsaved instances, attribute hashes, and stubbed objects), and support for multiple factories for the same class (user, admin_user, and so on), including factory inheritance.
|
4
4
|
|
@@ -43,13 +43,7 @@ gem install factory_bot
|
|
43
43
|
Supported Ruby versions
|
44
44
|
-----------------------
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
The factory_bot 3.x+ series supports MRI Ruby 1.9. Additionally, factory_bot
|
49
|
-
3.6+ supports JRuby 1.6.7.2+ while running in 1.9 mode. See [GETTING_STARTED]
|
50
|
-
for more information on configuring the JRuby environment.
|
51
|
-
|
52
|
-
For versions of Ruby prior to 1.9, please use factory_bot 2.x.
|
46
|
+
Supported Ruby versions are listed in [`.github/workflows/build.yml`](https://github.com/thoughtbot/factory_bot/blob/master/.github/workflows/build.yml)
|
53
47
|
|
54
48
|
More Information
|
55
49
|
----------------
|
@@ -100,8 +94,8 @@ See [our other projects][community] or
|
|
100
94
|
|
101
95
|
[community]: https://thoughtbot.com/community?utm_source=github
|
102
96
|
[hire]: https://thoughtbot.com/hire-us?utm_source=github
|
103
|
-
[ci-image]: https://
|
104
|
-
[ci]: https://
|
97
|
+
[ci-image]: https://github.com/thoughtbot/factory_bot/actions/workflows/build.yml/badge.svg
|
98
|
+
[ci]: https://github.com/thoughtbot/factory_bot/actions?query=workflow%3A.github%2Fworkflows%2Fbuild.yml+branch%3Amaster++
|
105
99
|
[grade-image]: https://codeclimate.com/github/thoughtbot/factory_bot/badges/gpa.svg
|
106
100
|
[grade]: https://codeclimate.com/github/thoughtbot/factory_bot
|
107
101
|
[version-image]: https://badge.fury.io/rb/factory_bot.svg
|
data/lib/factory_bot/aliases.rb
CHANGED
@@ -37,8 +37,9 @@ module FactoryBot
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def decorated_evaluator
|
40
|
-
Decorator::
|
41
|
-
Decorator::
|
40
|
+
Decorator::NewConstructor.new(
|
41
|
+
Decorator::InvocationTracker.new(@evaluator),
|
42
|
+
@build_class
|
42
43
|
)
|
43
44
|
end
|
44
45
|
|
data/lib/factory_bot/callback.rb
CHANGED
@@ -9,7 +9,7 @@ module FactoryBot
|
|
9
9
|
|
10
10
|
def run(instance, evaluator)
|
11
11
|
case block.arity
|
12
|
-
when 1, -1 then syntax_runner.instance_exec(instance, &block)
|
12
|
+
when 1, -1, -2 then syntax_runner.instance_exec(instance, &block)
|
13
13
|
when 2 then syntax_runner.instance_exec(instance, evaluator, &block)
|
14
14
|
else syntax_runner.instance_exec(&block)
|
15
15
|
end
|
@@ -6,6 +6,7 @@ module FactoryBot
|
|
6
6
|
super(name, false)
|
7
7
|
@options = options.dup
|
8
8
|
@overrides = options.extract_options!
|
9
|
+
@factory_name = @overrides.delete(:factory) || name
|
9
10
|
@traits = options
|
10
11
|
end
|
11
12
|
|
@@ -21,20 +22,36 @@ module FactoryBot
|
|
21
22
|
|
22
23
|
private
|
23
24
|
|
25
|
+
attr_reader :factory_name, :overrides, :traits
|
26
|
+
|
24
27
|
def build
|
25
|
-
|
28
|
+
raise_if_arguments_are_declarations!
|
26
29
|
|
27
|
-
|
28
|
-
|
30
|
+
[
|
31
|
+
Attribute::Association.new(
|
32
|
+
name,
|
33
|
+
factory_name,
|
34
|
+
[traits, overrides].flatten
|
35
|
+
)
|
36
|
+
]
|
29
37
|
end
|
30
38
|
|
31
|
-
def
|
32
|
-
if
|
39
|
+
def raise_if_arguments_are_declarations!
|
40
|
+
if factory_name.is_a?(Declaration)
|
33
41
|
raise ArgumentError.new(<<~MSG)
|
34
42
|
Association '#{name}' received an invalid factory argument.
|
35
|
-
Did you mean? 'factory: :#{
|
43
|
+
Did you mean? 'factory: :#{factory_name.name}'
|
36
44
|
MSG
|
37
45
|
end
|
46
|
+
|
47
|
+
overrides.each do |attribute, value|
|
48
|
+
if value.is_a?(Declaration)
|
49
|
+
raise ArgumentError.new(<<~MSG)
|
50
|
+
Association '#{name}' received an invalid attribute override.
|
51
|
+
Did you mean? '#{attribute}: :#{value.name}'
|
52
|
+
MSG
|
53
|
+
end
|
54
|
+
end
|
38
55
|
end
|
39
56
|
end
|
40
57
|
end
|
@@ -6,18 +6,30 @@ module FactoryBot
|
|
6
6
|
@component = component
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
if ::Gem::Version.new(::RUBY_VERSION) >= ::Gem::Version.new("2.7")
|
10
|
+
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
11
|
+
def method_missing(...) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
|
12
|
+
@component.send(...)
|
13
|
+
end
|
14
|
+
|
15
|
+
def send(...)
|
16
|
+
__send__(...)
|
17
|
+
end
|
18
|
+
RUBY
|
19
|
+
else
|
20
|
+
def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing
|
21
|
+
@component.send(name, *args, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def send(symbol, *args, &block)
|
25
|
+
__send__(symbol, *args, &block)
|
26
|
+
end
|
11
27
|
end
|
12
28
|
|
13
29
|
def respond_to_missing?(name, include_private = false)
|
14
30
|
@component.respond_to?(name, true) || super
|
15
31
|
end
|
16
32
|
|
17
|
-
def send(symbol, *args, &block)
|
18
|
-
__send__(symbol, *args, &block)
|
19
|
-
end
|
20
|
-
|
21
33
|
def self.const_missing(name)
|
22
34
|
::Object.const_get(name)
|
23
35
|
end
|
@@ -30,7 +30,7 @@ module FactoryBot
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def to_create(&block)
|
33
|
-
if
|
33
|
+
if block
|
34
34
|
@to_create = block
|
35
35
|
else
|
36
36
|
aggregate_from_traits_and_self(:to_create) { @to_create }.last
|
@@ -111,6 +111,20 @@ module FactoryBot
|
|
111
111
|
|
112
112
|
def base_traits
|
113
113
|
@base_traits.map { |name| trait_by_name(name) }
|
114
|
+
rescue KeyError => error
|
115
|
+
raise error_with_definition_name(error)
|
116
|
+
end
|
117
|
+
|
118
|
+
def error_with_definition_name(error)
|
119
|
+
message = error.message
|
120
|
+
message.insert(
|
121
|
+
message.index("\nDid you mean?") || message.length,
|
122
|
+
" referenced within \"#{name}\" definition"
|
123
|
+
)
|
124
|
+
|
125
|
+
error.class.new(message).tap do |new_error|
|
126
|
+
new_error.set_backtrace(error.backtrace)
|
127
|
+
end
|
114
128
|
end
|
115
129
|
|
116
130
|
def additional_traits
|
@@ -88,7 +88,7 @@ module FactoryBot
|
|
88
88
|
# end
|
89
89
|
#
|
90
90
|
# are equivalent.
|
91
|
-
def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing
|
91
|
+
def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing
|
92
92
|
association_options = args.first
|
93
93
|
|
94
94
|
if association_options.nil?
|
@@ -24,7 +24,7 @@ module FactoryBot
|
|
24
24
|
def association(factory_name, *traits_and_overrides)
|
25
25
|
overrides = traits_and_overrides.extract_options!
|
26
26
|
strategy_override = overrides.fetch(:strategy) {
|
27
|
-
FactoryBot.use_parent_strategy ? @build_strategy.
|
27
|
+
FactoryBot.use_parent_strategy ? @build_strategy.to_sym : :create
|
28
28
|
}
|
29
29
|
|
30
30
|
traits_and_overrides += [overrides.except(:strategy)]
|
@@ -33,15 +33,16 @@ module FactoryBot
|
|
33
33
|
@build_strategy.association(runner)
|
34
34
|
end
|
35
35
|
|
36
|
-
|
36
|
+
attr_accessor :instance
|
37
37
|
|
38
|
-
def method_missing(method_name, *args, &block)
|
38
|
+
def method_missing(method_name, *args, &block)
|
39
39
|
if @instance.respond_to?(method_name)
|
40
40
|
@instance.send(method_name, *args, &block)
|
41
41
|
else
|
42
42
|
SyntaxRunner.new.send(method_name, *args, &block)
|
43
43
|
end
|
44
44
|
end
|
45
|
+
ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
|
45
46
|
|
46
47
|
def respond_to_missing?(method_name, _include_private = false)
|
47
48
|
@instance.respond_to?(method_name) || SyntaxRunner.new.respond_to?(method_name)
|
data/lib/factory_bot/registry.rb
CHANGED
@@ -15,7 +15,7 @@ module FactoryBot
|
|
15
15
|
def factory(name, options = {}, &block)
|
16
16
|
factory = Factory.new(name, options)
|
17
17
|
proxy = FactoryBot::DefinitionProxy.new(factory.definition)
|
18
|
-
proxy.instance_eval(&block) if
|
18
|
+
proxy.instance_eval(&block) if block
|
19
19
|
|
20
20
|
Internal.register_factory(factory)
|
21
21
|
|
data/lib/factory_bot/trait.rb
CHANGED
data/lib/factory_bot/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: factory_bot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Clayton
|
8
8
|
- Joe Ferris
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-03-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -237,7 +237,7 @@ homepage: https://github.com/thoughtbot/factory_bot
|
|
237
237
|
licenses:
|
238
238
|
- MIT
|
239
239
|
metadata: {}
|
240
|
-
post_install_message:
|
240
|
+
post_install_message:
|
241
241
|
rdoc_options: []
|
242
242
|
require_paths:
|
243
243
|
- lib
|
@@ -252,8 +252,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
252
252
|
- !ruby/object:Gem::Version
|
253
253
|
version: '0'
|
254
254
|
requirements: []
|
255
|
-
rubygems_version: 3.1.
|
256
|
-
signing_key:
|
255
|
+
rubygems_version: 3.1.6
|
256
|
+
signing_key:
|
257
257
|
specification_version: 4
|
258
258
|
summary: factory_bot provides a framework and DSL for defining and using model instance
|
259
259
|
factories.
|