factory_boy: testing like a pro with Camila Maia
Published November 16, 2022
This video features Camila Maia at DjangoCon Europe 2022 in Porto, Portugal.
factory_boy: testing like a pro by Camila Maia
How to test complex objects using the library factory_boy. The lessons I’ve learned using the tool in a Django monolith containing 230+ tables and 75k+ relevant lines of code for over 3 years.
Factory Boy replaces static fixtures with customizable factories for creating complex test objects and their related data. Camila Maia explains how poorly designed factories create hidden assumptions, tight coupling, duplicated hacks, and tests that are written to satisfy the factory rather than the behavior under test. She recommends mirroring database models accurately, making test setup explicit, keeping optional fields optional through traits, preferring `build` when database persistence is unnecessary, choosing `SubFactory` or `RelatedFactory` according to relationship direction, and migrating shared factories gradually rather than changing them all at once.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
I hope you can hear me well. So let's talk about Factory Buy today. First things first, uh this slides deck are already in the speakerdeck. com slash cmycd. So if you can see any code or anything, you can follow the presentation uh online. So Um yep, let's go. So first of all, who am I? As I said already, so I'm a back-end developer. I work for SoundCloud. Um I'm a Brazilian. Any Brazilians here? Wow, Jesus Christ. I thought we had more, right? Okay, sorry. Uh I live in Berlin. Anyone living in Berlin here
Thank you. So I just relocated to Berlin uh like uh six months or so so if you want to share change any experience like Python stuff Please uh send a message. I'm queer any queer here? From Berlin, maybe no, okay. I do live with my wife and a dog. Anyone with a wife and no okay stop stop just okay uh so I'm coding since 2010, a long time. Uh mostly with Python and Ruby Um I love to work with communities, so conference, open source and stuff. I help to organize a lot of different conferences, Python Brasil, I helped on EuroPython to 2020, yes.
And I hope to I also help to create uh PyJermas and help organizing. Um I love to work with open source as I already said Uh one of the main projects that I work at uh that I work is uh Scan API that uh this project I created. and I help to maintain. Basically it's like a framework to create uh automated documentation for Rest NPIs and also integrate uh to create integration tests So if you are interested to about this uh this this library uh we are going to have like a workshop tomorrow uh here in Django Kong so just join me. Also yeah change the order of the slides but anyways
like have more than 1. 5k downloads per month so it's like super nice and super super proud and it's like really can't feel this number so and more like a lot of stars and okay whatever. So anyway this is me, hello. So let's start with Factory Boy. What is it? Uh I'm super glad that we have already a lot of talks about testing before. So I think you are probably already in the vibe of testing and TDD and So yeah, but let's talk uh specifically about Factory Boy. Uh it's a fixture replacement, so it's a library and um it's basically the idea is to stop using fixtures and starting using factories um with it. So Uh it's based on FactoryBot that is a gem for Ruby
and uh basically it's like the Python version for it. Uh it 's uh the Factorybot is uh from uh Fotbot so quite nice company also and uh the version the first version for uh Factory Boy they were only uh that it was only for jungle It was only working for Django, but nowadays it's nowadays it's framework independent, so it works with a lot of different frameworks. And also like work with unit tests, PyTests, and so on. The idea here is that we are talking about specifically in this uh this talk uh when we are uh dealing with complex objects. So when you have objects models that have a lot of relationships
and they are like complex and depending a lot of each other So in this case, especially in this case, like fixtures can be like a really not that good solution because they are static, they are hard to maintain. And on the other hand, like factories, they are easy to use, they are easy to customize and you can pass only the fields and you can already create your object. So just a small example of how we can do this. This is like from the factory bullet um documentation basically is like uh one let's say here that we have an orders object and this orders object is super complex uh so it depends on address and depends on customer and depends a lot of other stuff. And with Factory Boy you can create it only like in in instantiating an object, uh
like a factory, uh a factory for that. And uh without you have to like to create the address, create the customer, and do all this setup every time for every test that you want. So it's like pretty handful. Um And also one thing that I like from Faculty Boy is that we there is a lot of different tools that comes with it. So we can have sequence, for example, let's say that you have wanted to create uh many many users and then but the email field is required for the user so you cannot create all the emails with the same uh with with the same string. So let's say that once you create like user one, user two, user three dynamically. So you can use sequence, you can use flaker, which is quite um quite powerful that is basically to create uh
random random information. So let's say that you want to create an address like a US address so you can create a use fake for it and it it will generate like a random fake uh random data for you or like create a faker sentence, a faker name, and then you can like have proper values uh but also random Uh and many others that I'm just listing here so you can check later, but still like you have a lot of uh different tools for it So my experience in working with Factory Boy was like mainly like working three years, like in the previous job I was working for. working with a really huge uh monolith and were written in Django and that and there we were using Factory Boy and uh basically it was pretty huge because like it was more than 230
models, tables uh more than 2200 relevant files and more than 75k relevant lines so it was like a monster really And uh it was like pretty pretty hard to deal with it because uh if you change something it breaks everything. So you really need to know where to touch there. So a monster And uh and then like starting working with it, I I started like figure out some some things that were happening, like The first thing was that if you design s a factory that's not well defined or like it's not following some principles, um it starts affecting many many tests because everything starts depending on it and then you you start having a chain of problems. Also, you might generate implicit errors
because if if someone understands the model in a one way and then you create a factor that doesn't match with the model, probably the person that will use it is Even if it it's you in the future you would assume that it works like the model. But if not, then probably you're going to create like different errors that you you wouldn't expect Also uh we started like to notice that um a lot of times we were like creating the tests to fit the factory. So the factory was like not well designed And then we were trying like to do all the all the the fixes to make the the test pass based on the f in the factory, not the way not the way around that Usually we you would like write a test and just use the factory as a helper so you can use and have other
functionalities. So this is what I call as factory-oriented tests. So we start like doing the testing just to pass, like just to match the factory. So not a good approach. Um also the factories can get two ties. So you when you have one factory and this factory depends on another in a not really good way, then you cannot start changing one because then it affects the other and the other and then you just can't really Change anything because otherwise you start breaking everything else. Um and another thing is that when you start to like to work in with a not so good factory and You see a problem, you try to fix it, you do a hack, because if you change it, it will break everything, so you do a hack to make it
pass. But then you are going to work again in another test that use the same factory. And then you have to do the same hack. And then in that scenario we were doing the same hack. many times really copy and paste everywhere and we are just already like copying all the the the blocks already because we are super used to copy and paste the same hack over and over again So and then I started like to notice, okay, we have some patterns here. We are having we are having we are facing some problems and uh let's let's try to understand what are the root cause and how we how we can fix then. So while we were analyzing this part, like I started saying maybe we should start taking notice of this and uh take notes of this and start like really writing some best practices so
we can follow them then in the future So before I start talking about the best practice itself, I'm going to introduce the demo app. This app is just like to use as an example Uh because of course as I said in the beginning, uh this makes sense for complex objects. But if I showed here a complex app, nobody would understand anything because we would take like five minutes only to understand the relationships So I'm going to show like a really simple example just to illustrate the idea of having like complex objects and factories. Nice. So This is the application basically a Django application. Django application super basic. This is the post
page. And uh where we have a list of polls and basically like you can vote on a poll. So let's say that we want to vote if we prefer like Coke or Pepsi or whatever. So if we vote here, then we go to the to this page to vote, and then we can click on vote, and if we click on vote we see the results and that's it basically and just taking a look in the in the the first screen uh there are some some details here that we we should notice the first one is this label that is new that basically says that every time that there a poll is was created like in this last twenty-four hours then we have this label new Another thing is that if the post premium, then it has like a star.
So just show it differentiated. Also, a poll can have or not an alter. So for example in this first two we have an alter , but in the last two we don't And also we can have polls in two different languages. One uh is English or Portuguese. And if it's uh English, we are going to use the preposition uh for the author as by, but if it's uh Portuguese we are going to use poor. So basically this is the logic of the of the application. This is all the logic that we have. Talking about the relationships between them, we have poll that basically have the published date and if it's premium or not. um related with
the question that has the question itself so it's text and also we have the language that is a chart field saying if it's English or Portuguese Um and the question can have none or uh uh many choices. So a choice have a text saying like what's the option for the person to vote, and also the number of uh votes, so the total votes. Cool. Uh here is the model. So this is the pole model. As I said already in the beginning, uh in the last slide, we have the attributes. So basically question it's 101 publish date author and premium and also we have a dunder uh method here uh dunder string that basically is responsible to do all this logic about showing the text of the question
uh of the poll. So gets the information from that question and puts star, no star and uh author or not author. Also we have a method here for the poll that is uh for was published basically to uh handle the logic of the new label. And uh we have questions with all the attributes and also some uh auxiliary methods to say if it's in English, it's in Portuguese and also to get a string For the choice is the simplest one that basically uh the attributes and the under string to get the the value. Okay, so best practice, let's go The first one is factories should represent their models. Actually, this is the most important one.
So basically the idea is that The factory should represent exactly what you have in the database. So if you are you if you have a relationship in the database you have you should have in the in the in the factory as well. If the value can be new in the database, so in the factory also. So you have to map this the same way so this way you can avoid implicit errors. So uh um and then you in the future and also your your co-workers will know how this factory works because they already know how the model works. So they wouldn't need to guess how the factory works So one example of how we shouldn't do this. The first one is uh so we have the pole factory here and the question factory. So we are saying here that uh in the question factory we are pointing to the pole
So but if you see here in the model we have the question but there is no motion to pole at all here because the the relationship is in the other way So this is this is not good because uh in the model we are we don't have the relationship but in the factory we have. So the good way of doing this would be Putting the the relationship in the proper place that is in the poll. So if you go back here and you see that poll does have the question in the relationship. So here we map the same relationship Second one is do not rely on the phones from factories. Basically, uh this one means that if you have a value in the factory And that you create by default and then you start using and in other
tests relying that thinking that this value will be always the same then this might uh generate some problems. So for example if someone goes and change the value in the factory it will break your test that uh it shouldn't Also, um we should like create the test having the mode uh all the setup already there. So if anyone changed anything Doesn't matter because on your test you are setting everything that you want. So you are being explicit. You are being explicit what you want, saying exactly what the value that you want. So let's see some example. We have poll question factory again, and then we have a test. The test is just to check if the text of the poll is uh
is valid, is right. So here a better example would be to have a default value in the question factory and saying it explicitly as setting it explicitly as WhatsApp and then in the test in the test you are asserting it directly so you're getting this hard code value and checking if this is like uh uh valid or not So the idea here would be instead of first putting the value as hard-coded, we could use faker. So uh it would be always a new and different value there. And also We should properly say explicitly what we want when creating a factory. So here when we are creating a factory, we are saying that we want that premium is false, we want without uh
author, and we want with this text So we are like this is what I was trying to talk uh talk about, saying that we should be explicit in saying everything that you want inside the test. So we should do all the setup inside the test The third one is factors should contain only the required data. This is a uh this is also um related to um representing their model. So basically the rule is if if the value in the database uh in the in the in the model says that could be null, so null equals true Then the attribute should be if you are going to explicitly say that you want this attribute, you should put it under a trait So then the user can have an option to use it or not, but by default it will be new.
In exactly the same way as it is in the database So let's see an example of how not to do it. So we have here question and then we have author. So if you go back here and we see question model, then there is um There there is the author uh what is that uh that question? Oh, it's Paul, that's why. Okay Pole, we have author, but then uh it's null equals true. So it means that in the database we can have a pole without an auto. And then here we are saying explicitly that every time that you create a factory, it will come already with an author, even though you didn't explicitly say that So the idea would be to put it under a trade.
So every time that you want an author, then you have to explicitly say that So basically if you want an auto you you would use the factory on this way pole factory with author equals true so you're saying that you want an outer Otherwise, I think it's pretty hard to remember that we should also test the case where the author is known. Because this is like we we wouldn't probably remember about passing known as author. So in this way you you always remember to test all the cases. And um and basically because we should not assume that we have an outer in the in the factory, since in the database we have the possibility of not having an outer So we are like missing one case here. Uh the fourth one is
build over create. This is super related with performance. Uh basically let's see the difference between them When you do like myfactory. build, it creates an object in memory. But when we do myfactory. create, it creates in memory but also stores in the database. So Basically, uh the idea here is not saying that you should never use create. It's just to keep in mind that first, if you are using create, you are not doing a unit test anymore. You are hitting the database. So you're really uh testing more than just your unit. And uh the second one it will take more. So if you are okay with this then it then it's all good. But I would say that especially for like huge
uh test suites, if you use more build probably it will be way faster. So if you're using like in the bad way here, like bad way, let's say that if you use a create then you have to pass the notation. Okay, so you should not use create So how would we do this? Uh so basically we would use build instead and then automatically would be done. Good. So just an example of how this can impact your performance. We have a file with 14 tests. really small file 14 tests and using only create it was passing in 3. 26 seconds When we change everything to start using build, it was less than two
seconds. So imagine this for like more than 10,000 uh tests. This can really impact So the fifth one, this this one uh took me a while to understand, but once I understood the relationship between these helped a lot to make the factories uh not so tight so this helped a lot. Basically the rule is if there is a Farrank key in the table then you represent this relationship with a subfactory If the Ferng key is in the other table, then you use a related factor under a trait. So let's see First, the difference between sub factory and related factory. Sub factory when you are creating or building a sub factory, it creates at the same time of the main factory.
So it uh it occurs at the same time So m if they are dependent they will uh be they will be done in the same time. Uh but in the related factor it creates first the main factory and then the related factory. So maybe these are the difference So seeing an example here of how we should do this. So we have the choices and the question factory. So the first one if you see here choice Um here choice. We can see that we do have the Farrang key. So if we do have the Farrang key, see this means that the we really depend. It's a dependence. So we should use a subfactor in this. So uh we use uh subfactory directly. If uh
in the case of uh question, if you go here we see that we don't have uh the the relationship here uh to the choice so there is no mention of the choice so the Farangi key is in the other table and this case so what means is that we can have for this model we can have a question without a choice. So for that we would create then a related factor because uh the factor will be created afterwards and then there are trades. So you can also create a factor with choice and without choice because in the database you can create without choice. So we should simulate the same for the factory. So basically this would be the way that we will create um using the relationships. Um the sixth uh
best practice is we can use fixtures because I was saying in the first uh in the first slides that we uh it should like factories being used instead of fixtures, but we can keep using fixtures like for simple objects, for example, like things that are not complex, but also to avoid uh duplication. So This is this best practice is a way of how to use picture in a pretty good way. So basically let's say that we have uh this example here we have two tests And if you see we are creating these two tests, uh two factories, uh two objects from factories, but they are passing exactly the same attributes. So we are repeating ourselves a lot and imagine if you have a lot of tests this would be a ro a lot of repetition.
So one good way to do this is like uh encapsulate this uh uh using a fixture and then reuse this fixture in the test. So you have a pretty clear fixture that is in this case is like I want a poll that is uh English and it's no premium and there is an author and then you can reuse it. So this is a good way of using the fixture. But it's uh this leads us to our last best factor is that we should avoid sharing factors or our fixtures independently among different files. Why So basically if we start in sharing like having common fixtures, common factories in a file and have a lot of them we starting creating dependencies a lot.
So we if we try to change, improve, or uh if we try to do anything with this um uh with this these factors of fixtures it will touch in a lot of different place. So also it tends to inflate because Um we have different scenarios where you're using the same fixture or the same factory and then we are trying to accommodate. So we start putting things together uh things in the same fixture And then it's gett it getting getting bigger and bigger and bigger because you are trying to accommodate all the different scenarios where you shouldn't. So Also it's hard to maintain, especially because if you change a factor of fixture, you have tons of tests and breaking at the same time, so you cannot uh decorplate uh
everything Um and also one more time that if you start doing this you will probably will uh be in a scenario where you are trying to Make your test pass only to fit the fixture to match the fixture and not like really thinking about what your test should be doing Okay, then then we like I started noticing the patterns, I I saw all the best practices, I I noticed uh I took note of everything nice so Let's go and let's try to fix the first factory, right? So I got one of the main factories there and then I saw like yeah why not? I would change it. I would do like user related factory when it should sub factory nice
Cool, and then I broke more than fifteen hundred vests. I really it was painful. So what I say is, first of all, babe steps So if it's already too too tight, if it's already in a really bad situation probably we are going to have a lot of hard work but Let's let's try it with the first thing. So the new ones, the new factors, try to use the best practice because this probably will help And then trying to um go factory by factory, changing step by step because of course this could like be pretty hard, but I'm 100% sure that this can help a lot once you have the models and the factories matching and then everything will happen as you would expect.
All these examples and the code of this app is in the GitHub repository Camila Maya slash factory boys best practice. The official docs of Factory Boy is here. Also the common recipes, they have a page where they they give some tips like this. So you can merge like some of these ones and some of the Factory Boy. uh official ones and also the code of the the library itself is here. I would like to thank the whole organization because I know how hard it is to organize like a super huge event like this and it's been awesome And then I want to thank you everyone. And if you want to talk with me, I these are my uh social and please let's keep in touch. If you have any doubts or any questions or any suggestions about Factory Boy, just
Talk with me, thank you very much.
Factory Boy is a framework-independent Python library for creating test objects, intended as a replacement for static fixtures. It is especially useful for complex, related models because factories are easier to customize and maintain.
Discussed at 2:17A factory should mirror the model and database exactly: relationships should appear in the corresponding place, and nullable fields should remain nullable by default. This makes the factory predictable and avoids implicit errors.
Discussed at 13:06Set the values needed by each test explicitly instead of relying on defaults in the factory. Using generated values such as Faker data can also prevent tests from accidentally depending on one fixed default.
Discussed at 14:42Fields that can be null in the database should not be populated by default in the factory. Put the optional value behind a trait so tests explicitly opt into it and are more likely to cover both the present and absent cases.
Discussed at 16:03Use `build` when you only need an in-memory object, particularly in unit tests; `create` also saves the object to the database, is slower, and means the test is no longer testing only the unit. In the example, replacing `create` with `build` reduced runtime from 3.26 seconds to under two seconds for 14 tests.
Discussed at 18:35Yes. Fixtures remain useful for simple objects and for avoiding repeated setup when several tests need the same explicitly defined combination of factory values.
Discussed at 22:25Apply the practices first to new factories, then change existing factories incrementally, one factory at a time. A large refactor can break many tests at once, so gradual changes are safer even though matching factories to models may require substantial work.
Discussed at 25:26Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.
Published June 13, 2025
Published June 13, 2025
Published June 13, 2025
Published June 13, 2025
Published June 13, 2025
Published June 13, 2025