Strategy to create related objects without creating main object
See original GitHub issueThe problem
I introduced Factory Boy to the team I work on and we love using it, but there is one scenario that comes up frequently for us where we find Factory Boy does not have a good solution.
When testing deserialization of an object that has one or more related objects associated with it, we often want to create all of the dependent objects and build (without creating) the object we want to deserialize. How I would typically deal with this situation is to use the dependent factory to create dependent object before using the other factory.
A typical test of a serializer might look something like this:
class AuthorFactory(factory.DjangoModelFactory):
class Meta:
model = Author
first_name = factory.Faker("first_name")
last_name = factory.Faker("last_name")
class BookFactory(factory.DjangoModelFactory):
class Meta:
model = Book
author = factory.SubFactory(AuthorFactory)
title = factory.Sequence(lambda n: f"Book {n}")
class TestBookSerializer(TestCase):
def test_deserialize_book(self):
author = AuthorFactory()
book_data = BookFactory.build(author=author)
data = {
"author": book_data.author_id,
"title": book_data.title,
}
serializer = BookSerializer(data=data)
self.assertTrue(serializer.is_valid())
book = serializer.save()
self.assertEqual(book.author, author)
self.assertEqual(book.title, book_data.title)
What I’d like is to be able to write the same test without having to create the author before building the book. It would be ideal if there was a strategy I could use that would build the book, but create the dependent author.
In this simple example, it’s easy to say that there’s nothing wrong with just having that additional line where the author is created first, but the problem really comes into play when a model has many related objects, and all of these have to be created for every test.
Proposed solution
In addition to the strategies build
, create
, and stub
, it would be great if there was another strategy that would build the given factory but create the related objects from the corresponding subfactories, maybe something like create_related_and_build
. Alternatively if there was a way to plug in custom strategies into factory boy, that could possibly work too.
Extra notes
I apologize if there actually already is a nice way to solve this problem, but I wasn’t able to find anything from some searches on StackOverflow or through the documentation. If that is the case, please consider this issue a request to add an example to the documentation.
Also feel free to tell me that I’m completely thinking about this the wrong way. If that’s the case, I’d be open to suggestions on good patterns for writing tests as I have in my example above.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:6
Top GitHub Comments
I am facing the same issue, and I tried solving it by subclassing SubFactory and overriding the generate method, however, I stumbled upon:
ValueError: save() prohibited to prevent data loss due to unsaved related object
.I haven’t given it much thought, but basically here’s what I tried:
Has anybody been close to solving it? Or has any ideas on how to approach it?
@RevolutionTech Ah! Sorry for misunderstanding from the first place, indeed I missed the point. Now it is clear what you are trying to achieve and this totally makes sense.
For some reason we did not face this problem in our project, probably because of different architecture. I hope one day the proper solution could be implemented in Factory Boy but as a quick and really dirty workaround I would do something like
This way you will leave all related objects in DB, delete the Book from DB but leave it’s instance in memory for further use in data deserialization. Hope this works!
NB: Not sure you need the
keep_parents
parameter but I mentioned it just in case you will encounter cascade delete issues. I don’t have Django at hands right now to check this myself but I hope you’ve got the idea and might find it helpful.