<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rootstrap</title>
    <description>The latest articles on DEV Community by Rootstrap (@rootstrap).</description>
    <link>https://dev.to/rootstrap</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2858%2Ff617c7ea-4500-42ca-83dd-e42d460bfb05.png</url>
      <title>DEV Community: Rootstrap</title>
      <link>https://dev.to/rootstrap</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rootstrap"/>
    <language>en</language>
    <item>
      <title>RIP XML (Welcome Compose)</title>
      <dc:creator>Amaury</dc:creator>
      <pubDate>Wed, 02 Aug 2023 14:57:51 +0000</pubDate>
      <link>https://dev.to/rootstrap/rip-xml-welcome-compose-1aii</link>
      <guid>https://dev.to/rootstrap/rip-xml-welcome-compose-1aii</guid>
      <description>&lt;p&gt;&lt;strong&gt;RIP XML "They killed an innocent" (welcome Compose)&lt;/strong&gt;&lt;br&gt;
"They killed an innocent," says a song. That's what is happening to XML in Android, Google and the community are slowly killing XML.&lt;/p&gt;

&lt;p&gt;Since the announcement of Android Compose back in 2021 at the Google IO developers conference, developers have been testing and working with this new Declarative pattern for Android, and slowly Compose has been winning space in the Android community.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Big steps for a baby:&lt;/strong&gt;&lt;br&gt;
As with all, new things generate a lot of uncertainty, and usually, when you are adopting a new tech you have to be very careful with it, at the beginning we don't use to count on enough documentation or a community that supports it, so we strongly suggest considering this points before adopting new tech in general:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learning curve: We should evaluate the learning curve of the new technology and determine if it is feasible to allocate enough time and resources to learn and become proficient in it.&lt;/li&gt;
&lt;li&gt;Technical requirements: Analyze the technical requirements and determine if it is compatible with the existing infrastructure and software, and the company vision.&lt;/li&gt;
&lt;li&gt;Availability of resources: Assess the availability of resources, such as documentation, tutorials, and support forums, to aid in the learning and development process.&lt;/li&gt;
&lt;li&gt;Company goals: Determine if the new technology aligns with the company goals and if it can help achieve them more efficiently or effectively than the current technology.&lt;/li&gt;
&lt;li&gt;Cost: Consider the cost of adopting the new technology, including licensing, training, and impact on productivity during the transition.&lt;/li&gt;
&lt;li&gt;Risks and challenges: Identify risks and challenges associated with the new technology, such as security, adaptability, compatibility issues, or a lack of community support and updates.&lt;/li&gt;
&lt;li&gt;User experience: Consider how the new technology will impact the user experience and whether it will improve or hinder it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The transition&lt;/strong&gt;&lt;br&gt;
But the idea of doing the transition to Compose was always over the table, so we started to learn it, testing, documenting, fighting poor online documentation and tutorials, fighting with bugs and undocumented issues (🥲), new design approaches, and studying new architectures.&lt;/p&gt;

&lt;p&gt;So trust me when I say this transition can take a while, when you have a big team, aligning all the team members in the same direction is a big challenge, so my advice is don't try to jump into a bottomless pool and start a project with a new tech, or the consequence could be catastrophic.&lt;br&gt;
Right now we can say that Compose is mature enough to start taking it seriously and start the transition to it, so here it's the big question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is XML so bad that we need to move to Compose?&lt;/strong&gt;&lt;br&gt;
No really, in general, over other advantages, XML offers a structured, efficient, and maintainable way to define UI elements, promoting a good development workflow and a consistent user experience. But Compose provide several advantages over XML:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Declarative UI: is cleaner, more readable, and more performant than Imperative UI.&lt;/li&gt;
&lt;li&gt;XML depends too much on the application's live cycle.&lt;/li&gt;
&lt;li&gt;Compose allows you to do more with less code compared to XML, redefining all the views and simplifying their use, no more recycler views yea!!!.&lt;/li&gt;
&lt;li&gt;Compose is more intuitive: as declarative UI you just need to tell the UI what to show changing the data state and not setting up the UI like in XML.&lt;/li&gt;
&lt;li&gt;State management: data state is not attached to the app live-cycle, we don't need to take care anymore of that.&lt;/li&gt;
&lt;li&gt;Memory: all the compositions are painted "on demand" so the user just sees what needs to see and doesn't have hidden views or unused views in the background, improving the app performance.&lt;/li&gt;
&lt;li&gt;Design: the design system is more robust and intuitive.&lt;/li&gt;
&lt;li&gt;Learn more about Compose&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why now?&lt;/strong&gt;&lt;br&gt;
Compose is in a stage where we can use it to create complex solutions, saving time and effort for the team. The Android Development community is adopting compose very fast, and XML will be obsolete in the future, the same thing that happens with Java for Android when we move to Kotlin.&lt;/p&gt;

&lt;p&gt;On the other hand with Compose, you can create UI components more intuitively and concisely, using Kotlin code instead of XML. This makes it easier to understand and modify your UI code and reduces the amount of boilerplate code you need to write, improving the readability and the maintenance cost. &lt;/p&gt;

&lt;p&gt;Also rovides a faster way to develop your app's user interface. You can use Compose to quickly prototype your UI design and see the results in real-time, without having to compile and run your app. This can save you a lot of time and effort, especially during the early stages of UI development.&lt;/p&gt;

&lt;p&gt;And finally Compose provides a more flexible and customizable way to create UI components. You can easily create custom UI components and modify existing ones to fit your specific needs. With Compose, you can also easily reuse UI components across different parts of your app, saving you time and effort in the long run. Compose is designed to perform better than XML-based UI development. Uses a more efficient rendering engine that can reduce the amount of CPU and memory usage, resulting in a smoother and more responsive user interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
The transition to a new tech could be a challenge depending on the context, as individuals you may take a risk, but as a team, we have to consider a lot of things. &lt;/p&gt;

&lt;p&gt;As a team, this is the moment to start developing products with Compose, very soon we will bring you a base project with an initial setup for an Android project with Compose including Theming, Navigation, Testing, DI, and more. We encourage you to start learning Compose to be more productive and bring to the end user a better experience.&lt;/p&gt;

&lt;p&gt;Keep calm and code clean. 😜&lt;/p&gt;

&lt;p&gt;keywords: #android #compose #xml #google&lt;/p&gt;

</description>
      <category>compose</category>
      <category>android</category>
      <category>google</category>
    </item>
    <item>
      <title>Imperative and Declarative UI Pattern - Is Declarative Programming the future?</title>
      <dc:creator>Amaury</dc:creator>
      <pubDate>Thu, 16 Mar 2023 13:42:00 +0000</pubDate>
      <link>https://dev.to/rootstrap/imperative-and-declarative-ui-pattern-is-declarative-programming-the-future-2f6b</link>
      <guid>https://dev.to/rootstrap/imperative-and-declarative-ui-pattern-is-declarative-programming-the-future-2f6b</guid>
      <description>&lt;p&gt;Check out the &lt;a href="https://www.rootstrap.com/blog/imperative-v-declarative-ui-design-is-declarative-programming-the-future"&gt;Original post&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Behind every mobile application, a UI framework determines how developers create the application and how well it performs. Today, UI frameworks fit into two categories: declarative and imperative. The majority of apps use the imperative approach. &lt;/p&gt;

&lt;p&gt;However, as the newer, declarative frameworks develop and reveal their many advantages, several major tech companies have switched to developing their apps with declarative UI designs.&lt;/p&gt;

&lt;p&gt;As declarative programming becomes more popular, we want to evaluate its advantages and disadvantages and consider how it improves upon imperative designs. &lt;/p&gt;

&lt;p&gt;In this article, we will define and compare imperative and declarative UI design and explore the benefits and drawbacks of designing apps with declarative programming.&lt;/p&gt;

&lt;p&gt;Understanding Imperative and Declarative UI Designs&lt;br&gt;
Imperative and declarative UI frameworks refer to frameworks that use imperative and declarative programming styles, respectively. Below, we define these terms as they relate to UI design and describe the differences between the two paradigms or models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Imperative Programming&lt;/strong&gt;&lt;br&gt;
Currently the most common paradigm, imperative programming requires the developer to write step-by-step how the program will reflect the desired state or perform an operation. For UI creation, developers write detailed code that tells the device how to display the components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Declarative Programming&lt;/strong&gt;&lt;br&gt;
Declarative programming allows developers to design the user interface based solely on what they want to be displayed without regard to how the device will update its state.&lt;/p&gt;

&lt;p&gt;Oftentimes, declarative frameworks rely on an underlying imperative implementation that translates the declarative command into a set of imperative instructions for the device to complete. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let's look at an example&lt;/strong&gt;&lt;br&gt;
Website designer sitting at a computer &lt;br&gt;
Web design&lt;br&gt;
Consider a simple mobile app with a button that toggles its color. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Imperative design&lt;/strong&gt;&lt;br&gt;
A developer must create a button component and add an event handler function to it. The event handler function would run when clicked and contain an if-else statement that changes the color based on the button's color when pressed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Declarative design&lt;/strong&gt;&lt;br&gt;
The developer creates a button component with the property toggle color. The declarative design appears much simpler and more intuitive.&lt;/p&gt;

&lt;p&gt;However, the declarative example only works because of an imperative implementation in the framework that interprets the toggle color property and assigns the proper event handler that determines the current button color and how it needs to change. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why use Declarative Programming?&lt;/strong&gt;&lt;br&gt;
Less code, faster development&lt;br&gt;
Imperative programming requires a lot of code to manage the UI. Developers must determine the contents, define themes, position components, and manage event handlers. With many responsibilities, developers have less time to polish the design and debug other UI issues. &lt;/p&gt;

&lt;p&gt;Moreover, imperative UI design typically requires multiple programming languages, one for the UI design and another for the functionality of each UI element.&lt;/p&gt;

&lt;p&gt;With declarative programming, on the other hand, we only need to design UIs within the Swift (for IOS) or Kotlin (for Android) code, eliminating the need to maintain and handle separate UI files. In short, declarative UI programming means less code and faster development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Theming&lt;/strong&gt;&lt;br&gt;
Declarative UIs come with a pre-configured theme. Developers can customize this theme to meet their preferences by altering properties like typography, color, buttons, etc. A pre-configured yet fully customizable theme saves developers time as they don't need to build a theme from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamism&lt;/strong&gt;&lt;br&gt;
Declarative UI frameworks provide control structures developers can use to manipulate the drawing of the user interface. Compared to imperative frameworks typically used for Android and IOS development, declaratively developed user interfaces can use these control structures to be more dynamic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State management&lt;/strong&gt;&lt;br&gt;
With an imperative design, developers face the difficulty of syncing the UI with the app state by manually updating all the components to reflect the changes in the app. Traditionally, developers created a user interface with a tree of widgets/layouts.&lt;/p&gt;

&lt;p&gt;Whenever the app's data or state required a UI component to update, the program had to traverse through the tree of widgets, select the right one, and change it. As the tree of elements grows, the design becomes more complex and error-prone.&lt;/p&gt;

&lt;p&gt;The declarative UI paradigm eliminates this process of manual synchronization. The declarative framework automatically updates the UI to match the app's state, simplifying state management by a significant degree.&lt;/p&gt;

&lt;p&gt;Other Advantages of Declarative UI Design&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live previews for rapid UI development.&lt;/li&gt;
&lt;li&gt;SwiftUI &amp;amp; Compose foster collaboration between designers and web developers &lt;/li&gt;
&lt;li&gt;It's an opportunity to learn a valuable skill.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages of Declarative UI Design&lt;/strong&gt;&lt;br&gt;
Since declarative programming has only emerged in the past few years, there are still unresolved issues and disadvantages. Current problems with declarative UI design include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Declarative UIs only support more recent operating systems &lt;/li&gt;
&lt;li&gt;You may encounter problems no one has faced before&lt;/li&gt;
&lt;li&gt;Some features are only available in native, imperative UI frameworks&lt;/li&gt;
&lt;li&gt;Best declarative programming practices and conventions have not yet been established&lt;/li&gt;
&lt;li&gt;Insufficient documentation&lt;/li&gt;
&lt;li&gt;Small developer community&lt;/li&gt;
&lt;li&gt;Little integration with third-party libraries&lt;/li&gt;
&lt;li&gt;The learning curve can be very steep initially&lt;/li&gt;
&lt;li&gt;Many issues with the declarative paradigm, however, are likely temporary. As more companies and developers see the advantages of declarative design and invest more resources into growing the development community, they will resolve many of the aforementioned issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why you should start thinking declaratively?&lt;/strong&gt;&lt;br&gt;
Declarative app development requires a new perspective. Many assumptions that developers might have, in regards to app development, no longer apply if using a declarative design.&lt;/p&gt;

&lt;p&gt;For example, with declarative programming, developers can rebuild the UI to reflect the app's current state instead of modifying it directly. The formula below illustrates this new perspective.&lt;/p&gt;

&lt;p&gt;When the state of your app changes (for example, the user flips a switch in the settings screen), the state changes, which triggers a redraw of the user interface. There is no imperative changing of the UI itself (like a textView.text = "Hi").&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
Several major names in the tech industry, such as Meta, Google, and Apple, have already adopted the declarative paradigm because they see its value as a simple yet effective method of app development.&lt;/p&gt;

&lt;p&gt;These companies believe it is the future of front-end development. Here at Rootstrap, we believe we should be ready for it too, and we encourage developers to explore how declarative design could improve UI development.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
      <category>flutter</category>
    </item>
    <item>
      <title>Flutter Navigation: Navigator vs Go Router</title>
      <dc:creator>Amaury</dc:creator>
      <pubDate>Thu, 16 Mar 2023 13:35:48 +0000</pubDate>
      <link>https://dev.to/rootstrap/flutter-navigation-navigator-vs-go-router-1bi1</link>
      <guid>https://dev.to/rootstrap/flutter-navigation-navigator-vs-go-router-1bi1</guid>
      <description>&lt;p&gt;In this post we are going to learn a little bit more about Flutter Navigation and go_router.&lt;/p&gt;

&lt;p&gt;Check the &lt;a href="https://www.rootstrap.com/blog/flutter-navigation-with-router-go" rel="noopener noreferrer"&gt;original POST&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How does flutter navigation work?&lt;/p&gt;

&lt;p&gt;Flutter navigation provides a way for you to navigate from one widget to another in your app. This is achieved by using a navigation stack, which is essentially a collection of routes.&lt;/p&gt;

&lt;p&gt;Navigator is a built-in Flutter widget that provides a simple and convenient way to manage the navigation stack and navigate between routes. It's straightforward to use and can be a good choice for basic navigation needs. On it a route represents a widget and its associated data. When you navigate to a new route, it is pushed onto the navigation stack, and when you navigate back, the current route is popped off the stack.&lt;/p&gt;

&lt;p&gt;Navigator provides a number of navigation widgets, including Navigator, MaterialPageRoute, and CupertinoPageRoute, that make it easy to manage the navigation stack and provide common navigation patterns.&lt;/p&gt;

&lt;p&gt;With Navigator, you can push and pop routes, as well as manage the navigation stack. MaterialPageRoute and CupertinoPageRoute are two common types of routes in Flutter that are used to transition between pages. They provide animation and visual effects appropriate for the material design or Cupertino design languages, respectively.&lt;/p&gt;

&lt;p&gt;Overall, Flutter navigation is designed to be flexible, allowing you to create complex navigation patterns that meet the needs of your app.&lt;/p&gt;

&lt;p&gt;But there are some limitations and potential problems that you should be aware of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limited routing features: Navigator provides basic routing functionality, but it doesn't offer advanced features like named routes, path parameters, query parameters, or wildcard matching, which can make it challenging to build more complex navigation patterns.&lt;/li&gt;
&lt;li&gt;Code complexity: As your app grows in complexity, managing the navigation stack with Navigator can become more challenging, making your code more difficult to maintain and debug.&lt;/li&gt;
&lt;li&gt;Lack of customizability: Navigator provides basic animation and visual effects for transitioning between routes, but it doesn't allow you to customize the routing transitions and animations.&lt;/li&gt;
&lt;li&gt;Global state management: Navigator doesn't provide a built-in solution for managing the global states, which can make it challenging to share data between routes.&lt;/li&gt;
&lt;li&gt;Inconsistent navigation patterns: Because Navigator is a low-level widget, it can be challenging to ensure that navigation patterns are consistent across your app. This can lead to a fragmented user experience.&lt;/li&gt;
&lt;li&gt;These limitations can make it challenging to build complex, scalable, and consistent navigation patterns with Navigator. If you encounter these limitations, you may want to consider using a third-party routing library, such as go_router, to address them.&lt;/li&gt;
&lt;li&gt;On the other hand, go_router is a third-party routing library maintained by the Flutter team, that provides advanced routing functionality that is not available in Navigator. For example, go_router provides features such as named routes, path parameters, query parameters, and wildcard matching, which can be useful for building more complex navigation patterns.&lt;/li&gt;
&lt;li&gt;Additionally, go_router provides a more elegant and declarative approach to routing, making it easier to manage the routing logic in your app. This can help to make your code more organized and maintainable, especially as your app grows in complexity.&lt;/li&gt;
&lt;li&gt;So, if you need advanced routing features and a more elegant routing solution, you may consider using go_router. But if your navigation needs are simple, Navigator may be a more appropriate choice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are several advantages of using go_router in Flutter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Named routes: go_router supports named routes, which makes it easier to manage and organize the navigation logic in your app. Named routes allow you to define the routes in a central location and then reference them throughout your app, making it easier to make changes to your navigation logic in the future.&lt;/li&gt;
&lt;li&gt;Path parameters: go_router supports path parameters, which allow you to pass data to your routes based on the URL. This is useful for building dynamic navigation patterns, such as when you need to pass an ID or other data to a route.&lt;/li&gt;
&lt;li&gt;Query parameters: go_router also supports query parameters, which allow you to pass data to your routes through the URL query string. This is useful for cases where you need to pass data that is not part of the route path.&lt;/li&gt;
&lt;li&gt;Wildcard matching: go_router supports wildcard matching, which makes it easy to define catch-all routes that can handle any URL that doesn't match another route.&lt;/li&gt;
&lt;li&gt;Declarative routing: go_router provides a more elegant and declarative approach to routing, making it easier to manage the routing logic in your app. This can help to make your code more organized and maintainable, especially as your app grows in complexity.&lt;/li&gt;
&lt;li&gt;Custom routing transitions: go_router allows you to customize the routing transitions and animations, making it possible to create custom and unique navigation experiences in your app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, go_router provides a number of advanced routing features that can make it easier to manage and organize the navigation logic in your Flutter app. If your navigation needs are complex, go_router may be a good choice for your project.&lt;/p&gt;

&lt;p&gt;Let's see an example of how we can structure our app:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohu4008w2elml8pdtob2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohu4008w2elml8pdtob2.png" alt="go_router example graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 Dart
static GoRouter onBoardingRouter(AuthState authState) =&amp;gt; GoRouter(
        initialLocation: "/logIn",
        navigatorKey: onBoardingNavigatorKey,
        routes: [
          GoRoute(
            name: "login",
            path: "/logIn",
            builder: (context, state) =&amp;gt; const LoginPage(),
            //Example of redirection        
            redirect: (context, state) =&amp;gt; authState == AuthState.loading ? "/splash" : null,
          ),
          GoRoute(
            name: "splash",
            path: "/splash",
            builder: (context, state) =&amp;gt; const SplashPage(),
          ),
          GoRoute(
            name: "signUp",
            path: "/signUp",
            builder: (context, state) =&amp;gt; const SignUpPage(),
          ),
          GoRoute(
            name: "passwordReset",
            path: "/passwordReset",
            builder: (context, state) =&amp;gt; const ResetPasswordPage(),
          ),
        ],
      );


static GoRouter mainRouter = GoRouter(
    initialLocation: "/main",
    routes: [
      ShellRoute(
        navigatorKey: mainNavigatorKey,
        builder: (context, state, child) {
          return Cookies(
            child: HomeCorePage(
              child: child,
            ),
          );
        },
        routes: [
          GoRoute(
            name: "main",
            path: "/main",
            builder: (context, state) =&amp;gt; const HomePage(),
            routes: [
              GoRoute(
                name: "postDetails",
                path: "postDetails",
                builder: (context, state) =&amp;gt;
                    PostDetailsPage(postId: state.params['id'] ?? ""),
              ),
              GoRoute(
                name: "chat",
                path: "chat",
                builder: (context, state) =&amp;gt; ChatPage(),
              ),
              //...........
            ],
          ),
        ],
      ),
      ShellRoute(
        navigatorKey: mainNavigatorKey,
        builder: (context, state, child) {
          return Cookies(
            child: SettingsCorePage(
              child: child,
            ),
          );
        },
        routes: [
          GoRoute(
            name: "settings",
            path: "/settings",
            builder: (context, state) =&amp;gt; const HomePage(),
            routes: [
              GoRoute(
                name: "profileEdit",
                path: "profile",
                builder: (context, state) =&amp;gt; const EditProfilePage(),
                routes: [
                  GoRoute(
                    name: "resetPassword",
                    path: "resetPassword",
                    builder: (context, state) =&amp;gt; const ResetPasswordPage(),
                  ),
                ],
              ),
              GoRoute(
                name: "themeSetup",
                path: "theme",
                builder: (context, state) =&amp;gt; const ResetPasswordPage(),
              ),
              //...........
            ],
          ),
        ],
      ),
    ],
  );


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Go_router manages different types of routes:&lt;br&gt;
ShellRoute: is a container, basically, all the child routes will be rendered inside of its widget.&lt;br&gt;
i.e: Here HomeCorePage can have a container that renders a child widget with a classic nav drawer or a bottom nav bar to nav between screens, as usual when you change between screen we de HomeCorePage render again the new child and keeps the state of the rest of the screen.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 Dart
static GoRoutermainRouter= GoRouter(
  initialLocation: "/main",
  routes: [
    // Home Navigation with drawer or bottom nav bar
    ShellRoute(
      navigatorKey: mainNavigatorKey,
      builder: (context, state, child) {
        return Cookies(
          child: HomeCorePage(
            child: child,
          ),
        );
      },
      routes: [
        GoRoute(
          name: "main",
          path: "/main",
          builder: (context, state) =&amp;gt; const HomePage(),
        ),
        GoRoute(
          name: "settings",
          path: "/settings",
          builder: (context, state) =&amp;gt; const SettingsPage(),
        ),
      ],
    ),
  ],
);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;GoRoute: the main route type, this is where you declare your route with the widget to render, and can have different children, let's see:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 Dart
GoRoute(
          name: "main",
          path: "/main",
          builder: (context, state) =&amp;gt; const HomePage(),
          redirect: (context, state) {
                if (!isUserAuthTo("main"))
                         return "/unauth";
                if (!state.params.containsKey("id"))
                      return "/main"
                    // return null to continue to the sub route
                return null;
          },
          routes: [
            GoRoute(
                name: "details",
                    path: "/details/:id",
                    builder: (context, state) =&amp;gt; const Details(id: state.params["id"] ?? ""),
                ),
),
@amaury901130



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here we can see different things:&lt;br&gt;
Every route can have N sub-routes.&lt;br&gt;
We can add validations before nav to a sub-route and redirect the user to any other page that we need in case don't match the validation.&lt;br&gt;
Another advantage of go_router is that we can manage nav errors easily in the GoRouter class:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 Dart
GoRouter(
   // when trying to nav to a missing route.
      errorBuilder: (context, state) {
          return ErrorPage(ErrorCode.e404);
      },
      initialLocation: "/main",
      routes: ....
)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Deeplinks:&lt;br&gt;
When a deep link is received from the platform, GoRouter will display the configured screen based on the URL path. To configure your Android or iOS app for deep linking, see the Deep linking documentation at flutter.dev:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enable deep linking on Android&lt;/strong&gt;&lt;br&gt;
Add a metadata tag and intent filter to AndroidManifest.xml inside the  tag with the ".MainActivity" name:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 xml
&amp;lt;!-- Deep linking --&amp;gt;
&amp;lt;meta-data android:name="flutter_deeplinking_enabled" android:value="true" /&amp;gt;
&amp;lt;intent-filter android:autoVerify="true"&amp;gt;
    &amp;lt;action android:name="android.intent.action.VIEW" /&amp;gt;
    &amp;lt;category android:name="android.intent.category.DEFAULT"/&amp;gt;
    &amp;lt;category android:name="android.intent.category.BROWSABLE"/&amp;gt;
    &amp;lt;data android:scheme="http" android:host="flutterbooksample.com" /&amp;gt;
    &amp;lt;data android:scheme="https" /&amp;gt;
&amp;lt;/intent-filter&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A full restart is required to apply these changes.&lt;br&gt;
Test on Android emulator&lt;br&gt;
To test with an Android emulator, give the adb command an intent where the host name matches the name defined in AndroidManifest.xml:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 xml
adb shell 'am start -a android.intent.action.VIEW \\ -c android.intent.category.BROWSABLE \\ -d "&amp;lt;http://flutterbooksample.com/book/1&amp;gt;"' \\ &amp;lt;package name&amp;gt;

Replace the &amp;lt;package name&amp;gt; with the package name of your Android app. If you named the package com.example.myflutterapp, run the following command:

adb shell 'am start -a android.intent.action.VIEW \\ -c android.intent.category.BROWSABLE \\ -d "&amp;lt;http://flutterbooksample.com/book/1&amp;gt;"' \\ com.example.myflutterapp


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For more details, see the Verify Android App Links documentation in the Android docs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enable deep linking on iOS&lt;/strong&gt;&lt;br&gt;
Add two new keys to Info.plist in the ios/Runner directory:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 xml
&amp;lt;key&amp;gt;FlutterDeepLinkingEnabled&amp;lt;/key&amp;gt; 
&amp;lt;true/&amp;gt; 
&amp;lt;key&amp;gt;CFBundleURLTypes&amp;lt;/key&amp;gt; 
&amp;lt;array&amp;gt; 
    &amp;lt;dict&amp;gt; 
    &amp;lt;key&amp;gt;CFBundleTypeRole&amp;lt;/key&amp;gt; 
    &amp;lt;string&amp;gt;Editor&amp;lt;/string&amp;gt; 
    &amp;lt;key&amp;gt;CFBundleURLName&amp;lt;/key&amp;gt; 
    &amp;lt;string&amp;gt;flutterbooksample.com&amp;lt;/string&amp;gt; 
    &amp;lt;key&amp;gt;CFBundleURLSchemes&amp;lt;/key&amp;gt; 
    &amp;lt;array&amp;gt; 
    &amp;lt;string&amp;gt;customscheme&amp;lt;/string&amp;gt; 
    &amp;lt;/array&amp;gt; 
    &amp;lt;/dict&amp;gt;
&amp;lt;/array&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The CFBundleURLName is a unique URL used to distinguish your app from others that use the same scheme. The scheme (customscheme://) can also be unique.&lt;br&gt;
A full restart is required to apply these changes.&lt;br&gt;
Test on iOS simulator&lt;br&gt;
Use the xcrun command to test on the iOS Simulator:&lt;br&gt;
xcrun simctl openurl booted &lt;br&gt;
customscheme://flutterbooksample.com/book/1&lt;/p&gt;

&lt;p&gt;In conclusion&lt;br&gt;
Go_router is an alternative to Navigator that offers some advantages over the latter, especially in large and complex applications. We strongly recommend that if you're working on a complex application and need advanced navigation management, you should consider using go_router. &lt;br&gt;
Below are some reasons why we should consider using go_router instead of Navigator:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Greater flexibility in defining routes: Go_router allows for a more flexible definition of navigation routes than Navigator. With go_router, we can define routes based on any criteria, such as the access route, URL parameters, application state, etc.&lt;/li&gt;
&lt;li&gt;Better navigation management: Go_router offers more advanced navigation management than Navigator. For example, go_router allows us to define multiple navigation stacks for different areas of the application, making it easier to navigate between them without losing context.&lt;/li&gt;
&lt;li&gt;Integration with the BLoC pattern: Go_router integrates well with the Business Logic Component (BLoC) pattern, which is a popular architecture pattern in Flutter. Go_router allows us to define routes that are directly linked to BLoC events, making it easier to manage navigation and application state.&lt;/li&gt;
&lt;li&gt;Better performance: Go_router offers performance improvements over Navigator in large and complex applications. Go_router uses a tree-based data structure to store navigation routes, allowing for faster and more efficient route lookup.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>gorouter</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>¿Qué tanto sabemos de Agile? Conceptos claves y el porqué funcionan.</title>
      <dc:creator>Karen Stoletniy</dc:creator>
      <pubDate>Thu, 02 Feb 2023 16:43:59 +0000</pubDate>
      <link>https://dev.to/rootstrap/que-tanto-sabemos-de-agile-conceptos-claves-y-el-porque-funcionan-199c</link>
      <guid>https://dev.to/rootstrap/que-tanto-sabemos-de-agile-conceptos-claves-y-el-porque-funcionan-199c</guid>
      <description>&lt;p&gt;Agile se convirtió en el grupo de metodologías de trabajo más popular en el desarrollo de software. La mayoría de los desarrolladores hemos escuchado hablar de Scrum o Kanban (entre otros) o incluso puesto en práctica, sin embargo ¿qué sabemos realmente de Agile?.&lt;/p&gt;

&lt;p&gt;Muchas veces nos unimos a equipos, con listas de funcionalidades, deadlines o sprints ya definidos, pero ¿alguna vez nos hemos preguntado como desarrolladores como surge todo esto?.&lt;/p&gt;

&lt;p&gt;Este artículo pretende develar qué hay detrás de escena de algunos conceptos claves que algunas de estas metodologías comparten, con las cuales trabajamos en el día a día como es la priorización de features o la definición de la duración de los sprints. Además quizás te inspire a dar un nuevo giro a tu carrera, u odiar un poquito menos al Product Owner o Scrum Master.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0kxtcw0dg6f7pu7dd86p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0kxtcw0dg6f7pu7dd86p.png" alt="Thinking meme" width="600" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Planificación Ágil
&lt;/h2&gt;

&lt;p&gt;Si trabajas con Scrum quizás la parte no tan divertida son las comúnmente llamadas “Sprint Planning”, reuniones previas a cada inicio de sprint para planificar el trabajo de esa iteración.&lt;/p&gt;

&lt;p&gt;Pero antes de profundizar en este tema veamos la siguiente imagen llamada Planning Onion:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjhqwkuc8fpfh627bom57.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjhqwkuc8fpfh627bom57.png" alt="Planning onion" width="500" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lo que se puede apreciar de esta imagen son los diferentes horizontes de planificación. Normalmente estamos acostumbrados a focalizar hasta el “Release Plan”, y de hecho se pretende que así sea para el equipo de desarrollo. Sin embargo me gustaría hacer foco en el hecho de cómo un plan engloba a otro y cómo un cambio puede afectar a las otras capas que engloba.&lt;/p&gt;

&lt;p&gt;Ahora bien, ¿por qué tanta planificación? Recordemos que detrás de todo proyecto hay un negocio con sus prioridades y que la planificación Ágil se dirige hacia a donde hay &lt;strong&gt;más valor&lt;/strong&gt; para dicho negocio, el cual puede ser muy variante a lo largo del proyecto.&lt;/p&gt;

&lt;p&gt;Es por eso que el foco se encuentra más en la planificación que en el plan en sí mismo, lo cual ayuda a promover los cambios y se debe dar a lo largo de todo el proyecto. Los cambios son importantes y absolutamente necesarios para obtener nuevos resultados; no es menor destacar que esta es en parte la clave del éxito de Agile dado que da más flexibilidad y tolerancia que las metodologías tradicionales.&lt;/p&gt;

&lt;p&gt;En el desarrollo la planificación también juega un rol importante a la hora de intentar encontrar una solución óptima a lo que se pretende desarrollar.&lt;/p&gt;

&lt;p&gt;Pero además una buena planificación ayuda a:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reducir riesgos.&lt;/li&gt;
&lt;li&gt;Reducir incertidumbres.&lt;/li&gt;
&lt;li&gt;Aumentar la confianza del proyecto.&lt;/li&gt;
&lt;li&gt;Mejorar la toma de decisiones.&lt;/li&gt;
&lt;li&gt;Transmitir información mediante reportes periódicos.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Iteraciones
&lt;/h2&gt;

&lt;p&gt;El producto cambia, la gente cambia, el mercado cambia, por lo tanto el plan también y es de allí donde cobra tanta importancia iterar.&lt;/p&gt;

&lt;p&gt;Si trabajamos con Scrum sabemos que en cada iteración se obtiene como resultado un entregable que aporta valor, es por eso que el equipo tiene que trabajar como uno, con el mismo propósito y foco; de aquí juega un rol clave la definición del &lt;strong&gt;largo de las iteraciones (Sprints)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Probablemente estemos acostumbrados a sprints de dos semanas, pero ¿por qué no una semana, o cuatro? Bueno, eso depende de muchos factores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Qué tan largo es el Release Plan.&lt;/li&gt;
&lt;li&gt;El nivel de incertidumbre del proyecto, se recomienda hacer iteraciones más cortas cuanta más incertidumbre haya.&lt;/li&gt;
&lt;li&gt;Elegir el largo para maximizar el feedback, se debe planear en base a los aprendizajes.&lt;/li&gt;
&lt;li&gt;Suficientemente cortas para que no cambien las prioridades durante la iteración.&lt;/li&gt;
&lt;li&gt;Último y no menos importante, debe ser suficiente para trabajar comprometidos a llegar a los entregables, pero sin estrés.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kvo6ub26l9w4qgkg5ad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kvo6ub26l9w4qgkg5ad.png" alt="No stress meme" width="600" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Es por eso que las iteraciones de dos semanas han tomado popularidad, suelen ser más organizadas y menos apresuradas, puesto que en una iteración muy corta el deadline está muy próximo y no deja tiempo de recuperación en caso de enfermedad de un miembro del equipo o si algo va mal durante dicho periodo. Por su contraparte una iteración muy larga tiende a empezar muy tranquilo, pero terminar muy desenfrenado, sin mencionar que se queda propenso a que el plan cambie durante la iteración.&lt;/p&gt;

&lt;h2&gt;
  
  
  Velocity
&lt;/h2&gt;

&lt;p&gt;Ahora que ya se tiene definido el largo de cada iteración, hay que calcular cuánto trabajo asignarle a cada miembro del equipo, y es aquí donde entra en juego los conceptos de Velocity y Story Points.&lt;/p&gt;

&lt;p&gt;Comencemos por Velocity, ¿cómo se calcula? Hay tres formas de hacerlo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Usar valores históricos: Probablemente el más usado, pero para que sea efectivo hay que cumplir las siguientes condiciones.

&lt;ul&gt;
&lt;li&gt;Las tecnologías y herramientas son las mismas.&lt;/li&gt;
&lt;li&gt;El equipo y ambiente de trabajo es el mismo.&lt;/li&gt;
&lt;li&gt;Las estimaciones son hechas por las mismas personas.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Si no se tienen datos históricos, simplemente ejecutar iteraciones y visualizar la velocidad. Esto sí, mientras más iteraciones previas a la estimación, menos incertidumbre.&lt;/li&gt;
&lt;li&gt;Hacer un pronóstico: Aquí vienen los cálculos; se estima que las personas usan el 55-80% de su tiempo para el proyecto*, teniendo en cuenta meetings, sincronización, el cafecito de la mañana y los debates del partido del fin de semana 😬. Sabiendo lo anterior y los recursos disponibles, calculamos con el siguiente ejemplo:

&lt;ul&gt;
&lt;li&gt;4 personas en el equipo, 8 horas diarias cada uno.&lt;/li&gt;
&lt;li&gt;Por lo tanto, 6 horas disponibles por día (considerando el 80% del tiempo).&lt;/li&gt;
&lt;li&gt;Iteración de dos semanas (10 días hábiles).&lt;/li&gt;
&lt;li&gt;10 días x 6 horas x 4 personas = 240 -&amp;gt; Horas por iteración&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1h76v07dtygz3u7m1r78.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1h76v07dtygz3u7m1r78.png" alt="Math meme" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Cohn, Mike. 2005. Agile estimating and planning. Prentice Hall&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Story Points
&lt;/h2&gt;

&lt;p&gt;En la diaria estamos acostumbrados a estimar las tareas/stories con dicha unidad pero no vamos a mentir, en nuestras cabezas al dar una estimación lo traducimos en horas ya que sigue siendo aún un concepto abstracto que no lo terminamos de entender del todo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjsg3695rguwgnoh1vnwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjsg3695rguwgnoh1vnwv.png" alt="Scrum Master explaining story point meme" width="666" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imaginemos que nos piden estimar cuánto tiempo nos llevaría terminar de leer un libro. En una primera instancia debemos saber el tamaño del libro, pues no es lo mismo leer un libro de 100 páginas que uno de 350. Además hay que tener en cuenta el tópico y que tanto estamos relacionado con este, ya que quizás se nos sea más complejo leer y entender un libro de Mecánica Cuántica, que “Bajo la misma estrella”. Basados en eso y la disponibilidad diaria en horas para leer el libro, podemos estimar cuándo lo vamos a terminar.&lt;/p&gt;

&lt;p&gt;Lo mismo ocurre en un proyecto de Software, para estimar la duración de un proyecto se comienza por estimar su tamaño, de aquí surge protagonismo los Story Points. Básicamente lo que hacemos al estimar con esta medida es decir que una tarea/story es más grande o pequeña que otra, la cual va a conllevar más o menos esfuerzo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estimaciones
&lt;/h2&gt;

&lt;p&gt;Probablemente ahora nos estemos preguntando, ¿y cómo encontrar la unidad mínima de estimación? Independientemente de la unidad escojamos (como por ejemplo los números de Fibonacci) hay dos formas de definirlo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tomar una story que se considere más pequeña del resto y estimarla con un punto.&lt;/li&gt;
&lt;li&gt;Tomar una story mediana y asignar puntos del medio dentro del rango de Story Points.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Una vez que tenemos esta estimación y una aproximación de la velocidad del equipo, estamos en condiciones de estimar la duración de lo que estamos planificando.&lt;/p&gt;

&lt;p&gt;¿Por qué no estimar en tiempo directamente? Una de las grandes ventajas de los story points es que son auto-corregibles, no se devalúan y se adaptan a todos los miembros del equipo. Si por alguna circunstancia en una iteración hubo una velocidad menor a la estimada, simplemente se vuelve a recalcular el número de iteraciones que el proyecto va a requerir.&lt;/p&gt;

&lt;p&gt;Cuando hablamos de estimar la duración en tiempo, hay que tener en cuenta el “Tiempo ideal” vs “El tiempo transcurrido”. Es aquí donde entran en juego los factores que pueden afectar ese “Tiempo ideal”, como las reuniones, trainings, demos, enfermedad, etc. Es por eso que en la mayoría de los casos es más difícil estimar en tiempo, en donde la estrategia de estimación cambia por completo (dicha explicación quedará para otro artículo), pero sigue siendo una opción válida para implementar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prioridades
&lt;/h2&gt;

&lt;p&gt;Para culminar este artículo, me gustaría hablar sobre el cambio de prioridades. Independientemente de la metodología que usamos dentro de Agile, a todos nos duele ver como una feature que hicimos con tanto esmero y dedicación es sacada del tablero, o que el Product Owner con la voz temblorosa nos dice a mitad de sprint que este debe cambiar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrssf8jorztfxbiqqw2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrssf8jorztfxbiqqw2y.png" alt="Boy crying meme" width="700" height="700"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como mencionamos al principio de este artículo, la planificación Agile está dirigida hacia dónde hay más valor y eso puede variar por diversos factores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Valor en el mercado&lt;/li&gt;
&lt;li&gt;Costos&lt;/li&gt;
&lt;li&gt;Nuevos conocimientos: mientras más se conoce hay menos incertidumbres&lt;/li&gt;
&lt;li&gt;Riesgos: ya sean funcionales, problemas presupuestales, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Así que la próxima vez que veas estas variaciones, recuerda que hay muchos factores en juego que a veces el propio cliente no puede controlar o predecir.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;Estas son algunas de las cosas que nos encontramos a diario con Agile y que por alguna razón las asumimos porque funcionan, y muy bien. Pero la verdad es que hay mucho más detrás, estudios, pruebas y errores que hace esta metodología tan efectiva y flexible para cada proyecto en particular frente a otras metodologías tradicionales.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4v4w9n0bodytybd7cf8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4v4w9n0bodytybd7cf8.png" alt="Cheers meme" width="600" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>productivity</category>
      <category>workplace</category>
      <category>discuss</category>
    </item>
    <item>
      <title>How to manage large files with Heroku and Amazon S3 Buckets in Django Projects</title>
      <dc:creator>brunomichetti</dc:creator>
      <pubDate>Tue, 16 Aug 2022 13:58:33 +0000</pubDate>
      <link>https://dev.to/rootstrap/how-to-manage-large-files-with-heroku-and-amazon-s3-buckets-in-django-projects-4g9m</link>
      <guid>https://dev.to/rootstrap/how-to-manage-large-files-with-heroku-and-amazon-s3-buckets-in-django-projects-4g9m</guid>
      <description>&lt;p&gt;As a developer, I’ve recently worked on a Django API  that processes large-sized images and videos. The system requires the admin to upload those mentioned files, and since the API is deployed in &lt;a href="https://www.heroku.com/home"&gt;Heroku&lt;/a&gt;, we used to get the &lt;a href="https://devcenter.heroku.com/articles/request-timeout"&gt;TIMEOUT&lt;/a&gt; error. &lt;/p&gt;

&lt;p&gt;This happens because it takes some time to upload a file that is not light causing Heroku to crash. So, what can we do if we don’t want to reduce the size or the quality of the files? &lt;/p&gt;

&lt;p&gt;In this blog post, I will provide a solution for this problem and will explain the flow and tools used. This information will hopefully help you with any working Django project that requires the management of large files.&lt;/p&gt;

&lt;h2&gt;
  
  
  The software architecture and flow
&lt;/h2&gt;

&lt;p&gt;After discussing different approaches, my team and I decided to maintain the files in &lt;a href="https://aws.amazon.com/s3/?trk=5970b1e9-218b-48cc-9862-f23c151d81b2&amp;amp;sc_channel=ps&amp;amp;sc_campaign=acquisition&amp;amp;sc_medium=ACQ-P%7CPS-GO%7CBrand%7CDesktop%7CSU%7CStorage%7CS3%7CLATAMO%7CES%7CText&amp;amp;s_kwcid=AL!4422!3!590443989051!p!!g!!amazon%20s3&amp;amp;ef_id=Cj0KCQjw2MWVBhCQARIsAIjbwoM-y4_A0WSI9atLGA_qdo1hmdoy46Oo_4xJMp8b07gLS0R3UDn5qgQaAtheEALw_wcB:G:s&amp;amp;s_kwcid=AL!4422!3!590443989051!p!!g!!amazon%20s3"&gt;Amazon S3 buckets&lt;/a&gt;. These are containers where you can store objects (such as images and videos) and access them with fast performance. &lt;/p&gt;

&lt;p&gt;As an example, if a Django model has an image attribute, let’s call it profile_picture, then we store the file in a bucket, and store the corresponding URL to that file in the database. So, if the Frontend requires the picture, the Backend returns the corresponding URL for that instance. &lt;/p&gt;

&lt;p&gt;But, we don’t want everyone on the internet to have access to the image in the bucket, we only wish for the Frontend to do that. That’s why we have to configure the bucket as private. &lt;/p&gt;

&lt;p&gt;Now, we need to answer: if the bucket is private, how does our Frontend access the files in it?. Well, in this case, we have to generate a pre-signed URL, that is a URL to grant temporary access to the file.&lt;/p&gt;

&lt;p&gt;Here I will explain the proposed solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store the system files in private buckets of Amazon S3.&lt;/li&gt;
&lt;li&gt;Frontend and/or Django admin upload the files directly to a private Amazon S3 bucket.&lt;/li&gt;
&lt;li&gt;The Backend stores the corresponding URL of each file.&lt;/li&gt;
&lt;li&gt;When the Django admin creates/updates a file attribute for a given instance, the file is uploaded directly to the bucket, and the corresponding URL is stored in the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Explanatory interaction diagram:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MmcZJeHZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0gdybytp3pzoxzxhy09u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MmcZJeHZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0gdybytp3pzoxzxhy09u.png" alt="Image description" width="626" height="240"&gt;&lt;/a&gt;&lt;br&gt;
When Frontend is about to upload a file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It requests the Backend to get a valid upload URL to upload it to the private bucket.&lt;/li&gt;
&lt;li&gt;The Backend generates and sends the upload URL.&lt;/li&gt;
&lt;li&gt;The Frontend uses the upload URL to store the file in the private bucket.&lt;/li&gt;
&lt;li&gt;After the upload, the Frontend sends the file URL to the Backend to store it for the corresponding file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Explanatory interaction diagram:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z2FIzNuP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tgnjej87oc5pb0oivt1q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z2FIzNuP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tgnjej87oc5pb0oivt1q.png" alt="Image description" width="639" height="306"&gt;&lt;/a&gt;&lt;br&gt;
When the Backend sends the URL of a file to the Frontend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It uses a function to generate a pre-signed URL so the Frontend can access the file in the private bucket.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Explanatory interaction diagram:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3O3F4zXW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kh6olelfi3q6sbxqhhav.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3O3F4zXW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kh6olelfi3q6sbxqhhav.png" alt="Image description" width="643" height="264"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  The tools I used
&lt;/h1&gt;

&lt;p&gt;Now that we have defined the solution flow, let’s talk about the tools. The first one I want to mention is &lt;a href="https://github.com/bradleyg/django-s3direct"&gt;django-s3direct&lt;/a&gt;, a library to directly upload files to the Amazon bucket from the admin panel.  Also, it provides a model field that corresponds to the URL stored in the database. &lt;/p&gt;

&lt;p&gt;On the other hand, we will use &lt;a href="https://boto3.amazonaws.com/v1/documentation/api/latest/index.html"&gt;boto3&lt;/a&gt; to generate the pre-signed URLs that the Backend sends to the Frontend in order to access the file. This library also generates the upload URL to allow the Frontend to upload files without the need of knowing the Amazon credentials. &lt;/p&gt;

&lt;p&gt;In the following section, I’ll show the corresponding configurations.&lt;/p&gt;
&lt;h1&gt;
  
  
  Amazon S3 private bucket
&lt;/h1&gt;

&lt;p&gt;I won't speak about the creation of buckets as there is plenty of documentation available. Next up, I’ll describe the configuration of the bucket to make it private and integrate it with django-s3direct. &lt;/p&gt;

&lt;p&gt;Assuming you have already created the bucket and you have a user with access ID, access secret key, and permissions, these are the next steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log in to Amazon S3 and create the bucket. Make sure you have the permissions.&lt;/li&gt;
&lt;li&gt;Select the bucket, and go to the Permission tab.&lt;/li&gt;
&lt;li&gt;In the bucket policy, paste the next policy:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AllowPublicRead"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"AWS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"s3:PutObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"s3:PutObjectAcl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"s3:ListMultipartUploadParts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"s3:AbortMultipartUpload"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::&amp;lt;name-of-bucket&amp;gt;/*"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Change  by your bucket name.&lt;/li&gt;
&lt;li&gt;Now in the same tab, block all the public access to make it private:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3m-yWxXS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qyn3lr6eonwcag56as9s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3m-yWxXS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qyn3lr6eonwcag56as9s.png" alt="Image description" width="652" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finally, in the same tab, paste this CORS configuration (needed by django-s3direct):
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"AllowedHeaders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"AllowedMethods"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"HEAD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"PUT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"DELETE"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"AllowedOrigins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ExposeHeaders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"ETag"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"MaxAgeSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And that’s it, you have configured your private bucket. Keep in mind,  if you want to change the policy in the future, first need to uncheck the blocking access.&lt;/p&gt;
&lt;h1&gt;
  
  
  Django-s3direct library
&lt;/h1&gt;

&lt;p&gt;Now I will explain the use of the django-s3direct library to directly upload the files from the Django admin. &lt;/p&gt;

&lt;p&gt;If you have a model that has a file attribute, when you change that attribute, this library will directly upload the file from the browser without sending it to the server. This is to avoid the named timeout error. &lt;/p&gt;

&lt;p&gt;Let’s go step by step on how to get this configured:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;a href="https://github.com/bradleyg/django-s3direct"&gt;django-s3direct&lt;/a&gt; in your project.&lt;/li&gt;
&lt;li&gt;Add the library to your &lt;strong&gt;INSTALLED_APPS&lt;/strong&gt; list in the settings:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# your settings file
&lt;/span&gt;
&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; 
    &lt;span class="err"&gt;…&lt;/span&gt;
    &lt;span class="s"&gt;'s3direct'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="err"&gt;…&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Make sure you have the &lt;strong&gt;APP_DIRS&lt;/strong&gt; configuration set as True in your &lt;strong&gt;TEMPLATES&lt;/strong&gt; settings:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# your settings file
&lt;/span&gt;
&lt;span class="n"&gt;TEMPLATES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="err"&gt;…&lt;/span&gt;
        &lt;span class="s"&gt;'APP_DIRS'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;…&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Add django-s3direct urls to the urlpatterns list in your main urls.py file:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.urls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'s3direct/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'s3direct.urls'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Add the configuration corresponding to Amazon to your settings (strongly recommended to put the access id and secret key values in environment variables):
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# your settings file
&lt;/span&gt;
&lt;span class="c1"&gt;# If these are set to None, the EC2 instance profile and IAM role are used.
&lt;/span&gt;&lt;span class="n"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'your-aws-access-key-id'&lt;/span&gt;
&lt;span class="n"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'your-aws-secret-access-key'&lt;/span&gt;
&lt;span class="c1"&gt;# Bucket name
&lt;/span&gt;&lt;span class="n"&gt;AWS_STORAGE_BUCKET_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'your-aws-s3-bucket-name'&lt;/span&gt;
&lt;span class="c1"&gt;# The region of your bucket, more info:
# http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_REGION_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'eu-west-1'&lt;/span&gt;
&lt;span class="c1"&gt;# The endpoint of your bucket, more info:
# http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_ENDPOINT_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'https://s3.eu-west-1.amazonaws.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Run collect static if needed:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py collectstatic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Now you can define in your model a file attribute corresponding to an image or video. Let’s take a look at an example:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;s3direct.fields&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;S3DirectField&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;S3DirectField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'example_destination'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;And, what is example_destination? When you define a file attribute as an S3DirectField, you have to specify the dest parameter. There you have to put the string corresponding to the key in the &lt;strong&gt;S3DIRECT_DESTINATIONS&lt;/strong&gt; dictionary in the configuration.&lt;/li&gt;
&lt;li&gt;Let’s look at an example of the configuration to understand this better:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# your settings file
&lt;/span&gt;
&lt;span class="n"&gt;S3DIRECT_DESTINATIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="s"&gt;'example_destination'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="s"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'images/example-images/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s"&gt;'region'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AWS_S3_REGION_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s"&gt;'acl'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'private'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s"&gt;'allow_existence_optimization'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Inside each dictionary, you can configure a lot of parameters and check the library documentation to know them. - This example has the key where you define the folders where you want to store the corresponding file inside the bucket.
You can test that it’s working by creating an instance of the class in the Django admin:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;example_app.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'image'&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;


&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ExampleAdmin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Now you should go to django admin and you will see something like this:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lhaFhwj9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1tn1vg2o6gqcntu6shix.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lhaFhwj9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1tn1vg2o6gqcntu6shix.png" alt="Image description" width="425" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Then if you select an image, the library will directly upload the image to the private bucket and store the corresponding URL in the database. The Backend won’t receive a file, just a string URL. After the correct upload, you will see something like:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kjWFe2Uz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oiuc4jsyd14a4phj4zm1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kjWFe2Uz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oiuc4jsyd14a4phj4zm1.png" alt="Image description" width="376" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;And the stored URL will have this structure:
&lt;code&gt;https://s3.&amp;lt;region-name&amp;gt;.amazonaws.com/&amp;lt;bucket-name&amp;gt;/&amp;lt;key&amp;gt;/&amp;lt;file-name&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If you click on the name of the picture, the browser will try to open it, but you won’t be able to see it because the bucket is private, and that’s ok. Also, if you click on REMOVE, the file will be removed from the instance but it will continue existing in the bucket.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Customize the name of the pictures
&lt;/h2&gt;

&lt;p&gt;As highlighted, in the &lt;strong&gt;S3DIRECT_DESTINATIONS&lt;/strong&gt; dictionary, you can configure in the key attribute the folders route to store the files corresponding to the given destination. &lt;/p&gt;

&lt;p&gt;But what happens when you upload a file with the same name as another existing one in the same folder? &lt;/p&gt;

&lt;p&gt;Well, that will overwrite the file, and I assume you don’t want that because it can be the file of another instance in your system. &lt;/p&gt;

&lt;p&gt;Here are two tips for this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a timestamp to the file name to avoid repeated file names in the system.&lt;/li&gt;
&lt;li&gt;Use a slugify function to manage spaces and/or invalid characters in the file name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you do that, you can avoid a lot of future problems. Let’s look at an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# your settings file
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils.text&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;slugify&lt;/span&gt;


&lt;span class="err"&gt;…&lt;/span&gt; 


&lt;span class="n"&gt;EXAMPLE_DEST_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'images/example-images/'&lt;/span&gt;
&lt;span class="n"&gt;TIMESTAMP_FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'%m-%d-%Y_%H-%M-%S-%f'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;normalize_filename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;splitext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;time_stamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TIMESTAMP_FORMAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time_stamp&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;--&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;


&lt;span class="n"&gt;S3DIRECT_DESTINATIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s"&gt;'example_destination'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;EXAMPLE_DEST_KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;normalize_filename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;'region'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AWS_S3_REGION_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;'acl'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'private'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;'allow_existence_optimization'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, for each new file, we apply a normalization of the file name. First, we normalize the filename using the slugify function from Django, and then we append at the beginning the timestamp. &lt;/p&gt;

&lt;p&gt;If we upload a file with the name image.png, the system will store that file with the form &lt;code&gt;&amp;lt;timestamp&amp;gt;--image.png&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Another example is if we have a file with the name &lt;code&gt;FILE with SPACEs.png&lt;/code&gt;, the system will store &lt;code&gt;&amp;lt;timestamp&amp;gt;--file-with-spaces.png&lt;/code&gt;. This is super easy to understand and solves a lot of problems. &lt;/p&gt;

&lt;p&gt;Now, in this next section, we will see how to return presigned files to the Frontend.&lt;/p&gt;

&lt;p&gt;It’s important to note, that this mentioned normalization will be executed only when uploading files in the Django admin. To be consistent, it would be nice to make the same normalization in the Frontend when it has to upload a file.&lt;/p&gt;

&lt;h1&gt;
  
  
  Boto3 and pre-signed URL
&lt;/h1&gt;

&lt;p&gt;We know how to configure a private Amazon S3 bucket, and how to integrate it with django-s3direct to directly upload a file from the Django admin. &lt;/p&gt;

&lt;p&gt;Now, we need to know how to do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send a pre-signed URL from the Backend to the Frontend so this last can access the existing file in the private bucket.&lt;/li&gt;
&lt;li&gt;Send a pre-signed upload URL from the Backend to the Frontend so this last can upload a file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s now look at how we do this using the boto3 library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate and send a view pre-signed URL
&lt;/h2&gt;

&lt;p&gt;Since the bucket is private, if we take a file URL in the database and we try to access it, we won’t be able to. We would then see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uMf5mQrS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tsw5xom756m0lv4y5ab0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uMf5mQrS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tsw5xom756m0lv4y5ab0.png" alt="Image description" width="619" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is why we need to generate a pre-signed URL in the Backend for the corresponding file to be temporarily available for the Frontend. For this I use the boto3 library, let’s look at the used functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# some utils file for boto3 functions
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;boto3&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_object_key_from_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="s"&gt;'''
   Returns the object key for the given url
   '''&lt;/span&gt;
   &lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_ENDPOINT_URL&lt;/span&gt;
   &lt;span class="n"&gt;bucket_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWS_STORAGE_BUCKET_NAME&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_presigned_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="s"&gt;'''
   Returns a presigned URL for the given object key
   '''&lt;/span&gt;
   &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_REGION_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;s3client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'s3'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generate_presigned_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="n"&gt;ClientMethod&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'get_object'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="s"&gt;'Bucket'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWS_STORAGE_BUCKET_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="s"&gt;'Key'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;object_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="n"&gt;ExpiresIn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWS_PRESIGNED_URL_EXPIRATION_TIME_MINUTES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the function get_object_key_from_url obtains the object key from the file URL. As mentioned, the object key is the route of folders and the file name in the bucket. &lt;/p&gt;

&lt;p&gt;Why do we need the object key? We need it to generate the presigned URL of the file. That object key will be used in the get_presigned_url function for that purpose.&lt;/p&gt;

&lt;p&gt;The next thing to do is to execute that function when the Frontend makes a GET request and needs to access the file. &lt;/p&gt;

&lt;p&gt;There are several ways of doing that, and I’m going to show you one that I find easy to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a property in the model, that corresponds to the pre-signed attribute:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# your models file
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;s3direct.fields&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;S3DirectField&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;example_app.s3_files&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_object_key_from_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_presigned_url&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;S3DirectField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'example_destination'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;property&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;presigned_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;get_presigned_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_object_key_from_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The presigned_image property first extracts the object key for the stored URL and then generates the pre-signed one to send it to the Frontend.&lt;/li&gt;
&lt;li&gt;Now add the property to a model serializer:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;rest_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;serializers&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;example_app.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelSerializer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

   &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;
       &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'presigned_image'&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This way, if you use that serializer to send the data to the Frontend, it will return a pre-signed URL in the presigned_image field.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A pre-signed URL looks like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://&amp;lt;bucket name&amp;gt;.s3.amazonaws.com/images/&amp;lt;object key&amp;gt;?&amp;lt;lots of needed parameters&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;With that URL, the file will be available for the time period defined in your settings as in the example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate an upload pre-signed URL
&lt;/h2&gt;

&lt;p&gt;Last but not least, I’m going to explain how to send an upload URL from the Backend to the Frontend. Imagine we have an app where the users have profile pictures and the Frontend needs to upload the image. &lt;/p&gt;

&lt;p&gt;The flow would then look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Frontend generates the object key of the image file (recommend adding a timestamp and normalizing the file name as in the example in the previous section).&lt;/li&gt;
&lt;li&gt;The Frontend sends the object key in a POST request to the Backend.&lt;/li&gt;
&lt;li&gt;The Backend takes the object key and generates a pre-signed upload URL.&lt;/li&gt;
&lt;li&gt;The Frontend uploads the image using the pre-signed upload URL.&lt;/li&gt;
&lt;li&gt;The Frontend sends the image URL to the backend so it can be stored in the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at an example of this function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_presigned_url_dict_to_upload_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
   &lt;span class="s"&gt;'''
   Returns a dict with the necessary data to upload a file for the given key
   '''&lt;/span&gt;
   &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_REGION_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;s3client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'s3'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generate_presigned_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWS_STORAGE_BUCKET_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;object_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;Fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="s"&gt;'acl'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'private'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="n"&gt;Conditions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
           &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'acl'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'private'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="p"&gt;],&lt;/span&gt;
       &lt;span class="n"&gt;ExpiresIn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWS_PRESIGNED_URL_EXPIRATION_TIME_MINUTES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let’s look at an example in a viewset for the action POST to receive an object key and return the necessary information to upload the file.&lt;/p&gt;

&lt;p&gt;First, we must define the serializer like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;rest_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;serializers&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;example_app.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelSerializer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

   &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;
       &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'presigned_image'&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleUploadURLSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serializer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;object_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;upload_url_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DictField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, we must define the viewset with the corresponding action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;rest_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;viewsets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;rest_framework.decorators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;rest_framework.response&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;rest_framework.request&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;example_app.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;example_app.serializers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ExampleSerializer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ExampleUploadURLSerializer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;example_app.s3_files&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_presigned_url_dict_to_upload_file&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleViewSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewsets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelViewSet&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;serializer_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ExampleSerializer&lt;/span&gt;
   &lt;span class="n"&gt;queryset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

   &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'post'&lt;/span&gt;&lt;span class="p"&gt;,),&lt;/span&gt; &lt;span class="n"&gt;url_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'upload-url'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="n"&gt;serializer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ExampleUploadURLSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="n"&gt;serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raise_exception&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

       &lt;span class="n"&gt;object_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validated_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'object_key'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

       &lt;span class="n"&gt;upload_url_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_presigned_url_dict_to_upload_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

       &lt;span class="n"&gt;return_serializer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ExampleUploadURLSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'object_key'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;object_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'upload_url_dict'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;upload_url_dict&lt;/span&gt;&lt;span class="p"&gt;,}&lt;/span&gt;
       &lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="n"&gt;return_serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raise_exception&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;return_serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validated_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTP_200_OK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, after this, we are sending the necessary information to upload the file. And that’s it! We have finished the proposed solution. &lt;/p&gt;

&lt;p&gt;I have created a &lt;a href="https://github.com/brunomichetti/example_django"&gt;public repo&lt;/a&gt; where you can see this little example project to get further insight. If you’d like, you can download it and give it a try for yourself.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Throughout this blog, I presented a solution for a common problem for developers - managing big files in Django projects that are deployed using Heroku. &lt;/p&gt;

&lt;p&gt;I detailed in-depth the defined flow and tools I used, as well as providing some useful tips and examples to help you create a good solution. &lt;/p&gt;

&lt;p&gt;However, this doesn’t mean that the workaround we found is the only possible one. But it could very well be the most efficient. &lt;/p&gt;

&lt;p&gt;I hope you enjoyed reading and if you have another process that you think is useful, feel free to let us know in the comments section. Thanks for reading! &lt;/p&gt;

</description>
      <category>django</category>
      <category>python</category>
      <category>aws</category>
      <category>heroku</category>
    </item>
    <item>
      <title>Building an Engine-modularized API in Rails</title>
      <dc:creator>GuilleLeopold</dc:creator>
      <pubDate>Fri, 05 Aug 2022 13:51:33 +0000</pubDate>
      <link>https://dev.to/rootstrap/building-an-engine-modularized-api-in-rails-eg7</link>
      <guid>https://dev.to/rootstrap/building-an-engine-modularized-api-in-rails-eg7</guid>
      <description>&lt;p&gt;When big development teams build an API, one of the main challenges that we face is being able to work together on different features while avoiding bottle-necks or other common issues such as conflicting perspectives or avoid overlapping efforts on particular features.&lt;/p&gt;

&lt;p&gt;Because of this, when we started working on our latest project, we decided to work with a new approach; designed to help us scale by splitting the app into microservices. I recommend taking a look into this article to learn more about the approach we took: &lt;a href="https://martinfowler.com/bliki/MonolithFirst.html"&gt;Monolith First&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We knew from the beginning that it wouldn't be easy since this approach is novel and there aren't too many examples regarding APIs built this way. It forced us to think outside the box from the start as we had to devise an approach from the ground up, by using our Rails base project with some common gems and the signup/signin already ready to use.&lt;/p&gt;

&lt;p&gt;With all this compiled, we conducted some research, looking for some examples and studying how &lt;em&gt;engines&lt;/em&gt; work on Rails. Afterwards, we proceeded to the next step: start working on building a modularized API, leaving behind the classic monolithic architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  First steps: Designing the API
&lt;/h2&gt;

&lt;p&gt;Before starting development, it’s crucial to take some time to give a structure to these modules.&lt;br&gt;
The implication is that we must take into account the whole project, and therefore we need to think about how we can separate our application in a way that makes sense to encapsulate certain pieces of code.&lt;/p&gt;

&lt;p&gt;In our case, we were working on an e-commerce website, which made us think that some of the larger modules could be the following: catalog, user, orders, payments and admin. Of course this may grow and we are not attached to only using these modules; we may need to add new engines/modules when the app starts growing but at least this will give us the big picture of the app.&lt;/p&gt;

&lt;p&gt;Given this, a first picture of the API architecture may look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dCRykx75--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/an441gtudm10lm0v01ys.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dCRykx75--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/an441gtudm10lm0v01ys.png" alt="First Glance at Engines API architecture" width="880" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It may look weird that the engines in the box are the ones that have access to the user engine and not the opposite but when implementing this, we need to have it this way.&lt;br&gt;
To explain this better, let’s take the orders engine as an example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;order&lt;/strong&gt; engine has a model called &lt;strong&gt;purchase&lt;/strong&gt;, it represents all the purchases in the system.&lt;/li&gt;
&lt;li&gt;Each &lt;strong&gt;purchase&lt;/strong&gt; belongs to a &lt;strong&gt;customer&lt;/strong&gt; (which belongs to the &lt;strong&gt;user&lt;/strong&gt; engine) and a &lt;strong&gt;customer&lt;/strong&gt; can have many &lt;strong&gt;purchases&lt;/strong&gt; in terms of business.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to be able to model this properly, we need to add a foreign key to the customer table in the purchases table and because of this, the &lt;strong&gt;purchase&lt;/strong&gt; (and so too the &lt;strong&gt;orders&lt;/strong&gt;  engine) will, by definition, need to have access to the &lt;strong&gt;user&lt;/strong&gt; engine.&lt;br&gt;
GuilleLeopold marked this conversation as resolved.&lt;br&gt;
This implies two considerations in terms of code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can define the &lt;code&gt;belongs_to&lt;/code&gt; association in the purchase model due to orders engine having access to the user engine.&lt;/li&gt;
&lt;li&gt;We won’t be able to have the &lt;code&gt;has_many&lt;/code&gt; association in the user engine and because of that, if we need to have all the purchases for a user, we will need to define a scope in the purchases model that retrieves that information.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ND1tBkRU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n2lyzxi90dvjf6orcz88.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ND1tBkRU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n2lyzxi90dvjf6orcz88.png" alt="Relationship between tables in different Engines" width="351" height="61"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Building an engine
&lt;/h2&gt;

&lt;p&gt;After going through the first steps, the benefits of working with an API built with engines are already visible. The modules make it easier to split the team and start working on different features at the same time (let’s say: a pair of devs in the catalog, another pair in the admin and a few ones in the user engine).&lt;/p&gt;

&lt;p&gt;But it also made us feel uncomfortable as the lack of information, especially in terms of configuration and how to work between engines were a constant issue during the first few weeks.&lt;/p&gt;

&lt;p&gt;We decided to implement some scripts that would help us when we created new engines and we also wrote down some guides. At this point the collaboration between our development team was really important to learn how to deal with the engines.&lt;/p&gt;

&lt;p&gt;Having said that, the following is a helpful step-by-step guide on what to do when adding a new engine.&lt;/p&gt;

&lt;p&gt;The first step, of course, is to create the engine. To achieve that, rails provides a command with some flags:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rails plugin new engines/engine_name --mountable --skip-test --dummy-path=engines/engine_name/spec/dummy&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
.&lt;br&gt;
Let’s got a bit deeper on how this command is built:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The

&lt;code&gt;--mountable&lt;/code&gt;

flag means that we want to create the engine inside an isolated namespace.
* The

&lt;code&gt;--skip-test&lt;/code&gt;

flag means that we don’t want the engine to include the scaffold for the testing suite as we are going to install rspec.&lt;/li&gt;
&lt;li&gt;The

&lt;code&gt;--dummy-path&lt;/code&gt;

is used to define the path where we want to generate the dummy app that "works as the main app" when running the test suite.
Following this, we started by cleaning our engine, removing a lot of unnecessary files, adding some common configuration and copying some files that we need across all the engines.
After building a few engines, we decided to improve our experience and create our own script to create the engine getting some files from an "engine template" and also complete the following forementioned steps.
The script basically does the following:
* Runs the command to create the engine
* Removes some unnecessary folders and files
* Copy some files to the engine, like a generic gemspec with gems that we use in all the engines. I.e: rspec, rails, pg, rubocop, etc.
* Installs rspec
* Runs bundle install and remove some new unnecessary files
* Copy a generic version spec_helper, rails_helper.
* Rename/adapt some filenames to the standard that we are following.
The script looks like this:
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

I won’t go deeper on which files we removed or we added as this may change according to the needs of each project but I would like to mention a few configurations that are needed to make this work properly and are going to save you a couple of headaches.
1. Related to the migrations, we want each engine to have it owns migrations files, to achieve this we added the following block of code in lib/engine_name/engine.rb:
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

So, with this code we are telling Rails to look for new migrations in the db/migrate folder that belongs to the engine.
2. We want to be able to define a routes file in each engine and have the ability to have access to those endpoints from the main app. An easy way to achieve this and keeping it generic, is adding this piece of code in the routes.rb in the main application
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

This is iterating over the engines directory and mounting the routes of each engine.
3. A similar approach is taken to load all the engines in the gemfile in the main application.
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

By creating the script and adding these configurations into the main application, we were able to simplify a lot of the processes to add new engines and we ensured that all the engines are going to follow the same standard.
## Developing and testing a feature: considerations and tips
Now that we have gone through all this process that may seem extra thorough at the beginning, we are ready to start.
At this point, if everything is ok, there shouldn’t be many problems or inconveniences as we are going to be doing pretty much what we already used to do while developing features in Rails.
There are still a few considerations related to how engines work that are worth mentioning:
* An engine works like a library, so when adding dependencies in between, we must take care to avoid circular dependencies. This means that if we have the engines A, B and C; if the engine A can see B and B can see C, then A may be able to see C but C shouldn’t be able to see either B or A.
GuilleLeopold marked this conversation as resolved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ID4yYCyV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/byac50csyeah9ocx4nia.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ID4yYCyV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/byac50csyeah9ocx4nia.png" alt="Circular dependency in Engines" width="397" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Following on from this, when adding a dependency between engines, we need to add this one into the gemfile that belongs to that engine, not the gemspec. We will also need to add the engines that the other engine has as dependencies too.
GuilleLeopold marked this conversation as resolved.&lt;/li&gt;
&lt;li&gt;When installing a new gem, in order to be able to use it across the whole engine, we should require it in lib/engine_name.rb&lt;/li&gt;
&lt;li&gt;As a result of the engine namespace models, services, controllers and even the tables in the database will be namespaced too.&lt;/li&gt;
&lt;li&gt;Some tips I suggest:

&lt;ul&gt;
&lt;li&gt;Create an engine that is visible in all the engines across the application. This engine will help us reduce repeated code and may contain things that we want to use in all the engines. In our case, we have an engine called Api that handles all the requests validations (checking headers, render some common errors, etc) and all the main controller from other engines inherit from a controller located in the Api engine&lt;/li&gt;
&lt;li&gt;Create “libraries” that work as interfaces between our API and external services. I.e: In our case we have a gem called Catalog Providers and inside it we have services for each provider that we use in our system (like Target, Amazon, BestBuy)&lt;/li&gt;
&lt;li&gt;It might be useful to create a few scripts to run migrations and test suite or install gems across all the engines. The scripts will iterate over the engines folder and run the command inside each one.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Finally, it's worth to mention two ways to handle the escenario where we need to trigger an action in an engine where we don't have access from the engine we are currently working:

&lt;ul&gt;
&lt;li&gt;Using an async pattern like queue with pub/sub where the element that is running publishes a message in the queue and the element that is responsible of running the desired triggered action is subscribed to that queue and reads any message to know which action has to take.&lt;/li&gt;
&lt;li&gt;The other pattern that works fine is implementing an observer pattern, in our application we followed this pattern as Rails already has a good implementation of it for ActiveRecord. This pattern works by having classes called observers that are responsible of beware of any action happening in the "observed class" and trigger actions depending on what action was taken in the followed class.
Using the &lt;strong&gt;User&lt;/strong&gt; and &lt;strong&gt;Payment&lt;/strong&gt; engines where &lt;strong&gt;Payment&lt;/strong&gt; can access to &lt;strong&gt;User&lt;/strong&gt; but &lt;strong&gt;User&lt;/strong&gt; can't access to &lt;strong&gt;Payment&lt;/strong&gt; to avoid having a circular dependency, let's see step by step how we implement the &lt;strong&gt;observer pattern&lt;/strong&gt; in our application:

&lt;ol&gt;
&lt;li&gt;Create an observer class inside our models folder in the &lt;strong&gt;Payment engine&lt;/strong&gt; as we want to trigger an action in this engine as a consequence of an action in the &lt;strong&gt;User&lt;/strong&gt; engine.&lt;/li&gt;
&lt;li&gt;Set up the observer in the &lt;strong&gt;engine.rb&lt;/strong&gt; file by adding the following line:
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Implement your observer, we need to specify which model we want to observe and implement an action according that behaves similar to a callback in ActiveRecord. It might look like this:
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Finally, if you want to disable observers while running &lt;strong&gt;rspec&lt;/strong&gt;, you can add this line in your test suite as a before action:
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

On the other hand, testing inside an engine with rspec is quite similar to what we are used to doing, it provides us with some guarantees and the only considerations that took us some time are:&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;We will need to load manually factories from other engines when using FactoryBot in our engine&lt;/li&gt;
&lt;li&gt;We need to require in our rails_helper.rb any development dependency that we are using in our test suite
After developing a feature, one of the advantages that we found out about working with engines is that we can just run the test suite for the engine where we have been working on without the necessity to go through all the suite tests in the application. This reduces the time to run tests locally dramatically.
The API architecture and the project structure will look something like this:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kVnSYwSu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lbxy5gkmjh1kbjpid93h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kVnSYwSu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lbxy5gkmjh1kbjpid93h.png" alt="Engines Structure + Folder Struct" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summing up
&lt;/h2&gt;

&lt;p&gt;It’s been a while since we started working with this project structure. Right now we are at a point where we can already look back and find the pros and cons of making this particular decision:&lt;br&gt;
&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Helps to parallelize work when working with medium-sized development teams.&lt;/li&gt;
&lt;li&gt;Makes the code more organized and easier to read or find where a feature should be implemented.&lt;/li&gt;
&lt;li&gt;The possibility to run tests inside an engine scope reduces time when testing locally.&lt;/li&gt;
&lt;li&gt;A modular application like this is a mid-step between a monolith API and microservices.&lt;/li&gt;
&lt;li&gt;Possibility to pick a full engine and use it in another API. We did this with our catalog engine and an admin that we develop in another project.
&lt;strong&gt;Cons:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Lot of configuration and changes are needed when starting working with engines, as a consequence it requires a bit of time during the first weeks.&lt;/li&gt;
&lt;li&gt;Lack of information on the internet, building an API with engines doesn’t seem to be a popular choice.&lt;/li&gt;
&lt;li&gt;Despite our effort to avoid repeating code, we repeat even more code than when working in a monolith api.
After overcoming the initial learning curve, we feel quite comfortable working with engines and we found out the benefits of having chosen this architecture instead of a monolith one, so I think that I would use it again if it fits the project requirements. In spite of that, we still have a lot to learn and improve upon what we already did.
As a pending feature, we would also like to see how we could take advantage of this architecture and try to deploy our application into microservices; of course that implies some changes will need to be made but it would be a valuable achievement.
## Learning about engines and references
When we started our investigation on how to build our application, we learned a few things from this Medium post: &lt;a href="https://medium.com/@dan_manges/the-modular-monolith-rails-architecture-fb1023826fc4"&gt;The Modular Monolith: Rails Architecture&lt;/a&gt;
To learn more about engines, the &lt;a href="https://guides.rubyonrails.org/engines.html"&gt;Ruby on Rails official documentation&lt;/a&gt; is a good follow-on reading.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read this article and more content in the Rootstrap blog: &lt;a href="https://www.rootstrap.com/blog"&gt;https://www.rootstrap.com/blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>engines</category>
      <category>modularization</category>
      <category>ruby</category>
    </item>
    <item>
      <title>How to Effectively Integrate a Typography System in React Native</title>
      <dc:creator>Agustina Chaer</dc:creator>
      <pubDate>Tue, 05 Jul 2022 15:17:34 +0000</pubDate>
      <link>https://dev.to/rootstrap/how-to-effectively-integrate-a-typography-system-in-react-native-20hd</link>
      <guid>https://dev.to/rootstrap/how-to-effectively-integrate-a-typography-system-in-react-native-20hd</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;If there is one thing that most react native apps have in common, it is how they display text. Some simple apps show very little, but most apps display a lot. That is why it's vital to put the right system in place, to ensure adding and refactoring texts in your app is as simple as possible.&lt;/p&gt;

&lt;p&gt;Now, you might be thinking - why am I reading an article on how to manage such a simple component? It's just text, right? Yes, but more often than not, you need to apply styles and properties to said text to make it look the way you want. When you have to do it for all the texts in your app, it adds up. This, of course, begs the question - how can I reduce the amount of code repetition?&lt;/p&gt;

&lt;p&gt;Over the years, I have seen my fair share of attempts at solving this problem, and I can finally say that I have landed on one solution that left me quite pleased.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;The solution is quite simple, but it's based on following best UI practices. What do I mean? Having predefined styles for headings, paragraphs, and other texts that you might need. What is important is that all the texts of your app fall into one of those definitions and that the amount doesn't get out of hand. It's good to keep the number of definitions short, so it's easier to remember what to put when coding and that your app is consistent in its respective styling, which in turn, gives you usability and accessibility points.&lt;/p&gt;

&lt;p&gt;Without any further ado, here is the aforementioned code of the component and supplementary files:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Once you have all of that in place, you can simply import the component and use it as follows:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;It's as simple as that, and usually, you will only have to apply extra styles to a particular text component instance if you need to add spacing, color, or wrapping.&lt;/p&gt;

&lt;p&gt;Taking the naming convention and hierarchy of text components from the web makes it very easy to understand and remember. As you may have noticed, with the use of prop-types, it is very easy to notice if you misspelled a type when setting it on an instance of said component.&lt;/p&gt;

&lt;p&gt;Although adding custom fonts to your React Native project is out of scope for this article, you might find (link here) this article useful where we cover that subject (&lt;a href="https://www.rootstrap.com/blog/how-to-customize-fonts-in-react-native/" rel="noopener noreferrer"&gt;How to customize fonts in React Native&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As you can see, the solution is very clean and simple, but it fixes a problem in a component that is so basic that it is also often overlooked. Putting a bit of time and thought into these kinds of components that make up the core DNA of our apps can go a long way.&lt;/p&gt;

&lt;p&gt;I hope you find this article helpful and that you end up implementing this solution in your projects.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Digital Accessibility 101: The importance of Digital Accessibility</title>
      <dc:creator>Agustina Chaer</dc:creator>
      <pubDate>Tue, 05 Jul 2022 14:56:41 +0000</pubDate>
      <link>https://dev.to/rootstrap/digital-accessibility-101-the-importance-of-digital-accessibility-1eff</link>
      <guid>https://dev.to/rootstrap/digital-accessibility-101-the-importance-of-digital-accessibility-1eff</guid>
      <description>&lt;p&gt;This is the first installment of a series of articles where we are going to dive into digital accessibility. Some of the topics that are going to be covered are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why is this subject so important?&lt;/li&gt;
&lt;li&gt;Why should you make your sites/applications accessible?&lt;/li&gt;
&lt;li&gt;How can you make your site/app accessible?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we are going to tackle the first two questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is Digital Accessibility so Important?
&lt;/h2&gt;

&lt;p&gt;It is estimated that over 1.2 billion people live with some form of disability. This makes up approximately 15% of the population. As you can imagine, just as with the real world, people with disabilities encounter barriers to navigate the digital space on a daily basis.&lt;/p&gt;

&lt;p&gt;When done right, the digital medium can be a great tool for people with disabilities as it can enable them to do things that they couldn’t do before or make a task more effortless. Some examples of this are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A blind person reading the news with their computer using a screen reader whereas before there were only newspapers.&lt;/li&gt;
&lt;li&gt;A physically disabled person opting for online shopping vs going to the store in person.&lt;/li&gt;
&lt;li&gt;A hard-of-hearing person opting for the online version of a class they are attending at a university because it’s close-captioned.&lt;/li&gt;
&lt;li&gt;A speech-impaired person filling in a contact form or ordering food with an app because calling is not an option or they would have needed to ask someone else to do it for them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many more examples just like those. That being said, the one thing they all rely on to actually work is that they need to be accessible. We can easily transform the examples above so they are not useful anymore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The news site is not coded properly, it does not have the correct text hierarchy. Without going into too much detail, the site becomes just a big blob of text to the tools a blind person would use to navigate the site making it unusable. The blind person would need to find another place to get the news from.&lt;/li&gt;
&lt;li&gt;The e-commerce site is not keyboard accessible so many of the physically disabled people that don’t use a mouse to navigate the web wouldn’t be able to shop online in that store. They would need to find another site or go to the store in person.&lt;/li&gt;
&lt;li&gt;The university uploads the class but doesn’t add any closed captions or transcripts making it useless for the hard of hearing.&lt;/li&gt;
&lt;li&gt;The restaurant has a web page showcasing their menu but the only way to order is by calling the phone number on their site, speech impaired people would probably order someplace else that is accessible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the digital medium becoming more and more popular, the lack of digital accessibility can become even prohibitive and exclusionary. Think how many things you do on a normal day that involves interacting with a website or an app?&lt;/p&gt;

&lt;p&gt;How would you feel if those sites and apps weren’t built in a way that you could use them? You would feel left out and you would probably find it harder to get things done.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Curb Cut Effect
&lt;/h2&gt;

&lt;p&gt;The curb cut effect is the phenomenon that occurs when disability-friendly features end up benefiting a larger group of people than the people it was originally designed for.&lt;/p&gt;

&lt;p&gt;The name comes from the curb cut ramps installed for people in wheelchairs, those ended up being really useful to other people. For example, parents pushing strollers, workers pushing heavy carts, business travelers wheeling luggage, even runners and skateboarders.&lt;/p&gt;

&lt;p&gt;The same can be identified in many places in the digital space. One very clear example that probably most people have used is the addition of closed captions, now included in all streaming services and video platforms.&lt;/p&gt;

&lt;p&gt;Closed captions have allowed hard-of-hearing people to watch and understand content in a way they couldn’t before. They also allow everyone to watch content in a foreign language or watch a video without audio if needed too, especially in situations like being at a library without headphones.&lt;/p&gt;

&lt;h2&gt;
  
  
  We all suffer at some point some kind of disability, limitation or impairment
&lt;/h2&gt;

&lt;p&gt;To further solidify the concept of “Accessibility benefits us all” let’s talk broaden the scope and talk about disabilities, limitations and impairments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Permanent: These are the ones we normally think about when we think of disabilities (Visual, Auditory, Cognitive, Physical, and Speech).&lt;/li&gt;
&lt;li&gt;Temporary: Ear infection that prevents you from hearing properly for a while or you broke your arm and now can’t use a mouse to use your computer for a couple of weeks.&lt;/li&gt;
&lt;li&gt;Situational: You are on a bus and want to watch a video but you forgot your headphones so you need close captions. Even something as simple as looking at your phone outside if it’s very bright, not enough contrast ratio on an app you might be using might make it unusable in that situation.&lt;/li&gt;
&lt;li&gt;Socioeconomic: Using a very old device with a poor internet connection in a site that has not been optimized for those conditions can result in a very poor experience for the user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see the lack of accessibility affects us all to some degree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should You Make Your Site/Application Accessible?
&lt;/h2&gt;

&lt;p&gt;The simplest answer to the question above is that “It’s the right thing to do”. That alone should be enough to convince you. It should be a no-brainer that making your products usable by anyone and not excluding more than a billion people is the way to go.&lt;/p&gt;

&lt;p&gt;What you might not know is that by doing the right thing, you will also be benefiting your company in a lot of ways. Some of them are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reach a wider audience, hence more customers and more revenue.&lt;/li&gt;
&lt;li&gt;Better SEO (Search Engine Optimization), which means that people will find you more easily when using search engines like Google.&lt;/li&gt;
&lt;li&gt;Better user experience for all users can help with user retention.&lt;/li&gt;
&lt;li&gt;Avoid potential discrimination lawsuits, if you want to know more here is a good &lt;a href="https://krisrivenburgh.medium.com/does-every-website-need-to-be-ada-compliant-accessible-3d4f176cf1e9" rel="noopener noreferrer"&gt;article about digital discrimination lawsuits in the US&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Comes Next?
&lt;/h2&gt;

&lt;p&gt;Now that we have talked about why accessibility is so important and why you should make your site/application accessible, it’s time to start the journey of making your site/application accessible.&lt;/p&gt;

&lt;p&gt;To help you in that journey, we’ll be posting more articles in the upcoming weeks that will touch on key things you need to do and take into consideration.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>ada</category>
      <category>wcag</category>
    </item>
    <item>
      <title>How to Test React Native Apps</title>
      <dc:creator>Manuela Viola</dc:creator>
      <pubDate>Thu, 23 Jun 2022 20:06:49 +0000</pubDate>
      <link>https://dev.to/rootstrap/how-to-test-react-native-apps-4n3</link>
      <guid>https://dev.to/rootstrap/how-to-test-react-native-apps-4n3</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Have you ever had a bug introduced by solving another problem and didn't realize it until weeks later? This could have been easily avoided if you had tests for your App.&lt;/p&gt;

&lt;p&gt;In a project with a tight schedule, tests are generally the first thing to be forgone, but they are being underestimated. Once you make that click and understand how to create tests, it's not that complicated to add them and avoid future problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;In this article, you will learn how to test React Native Apps using &lt;a href="https://jestjs.io/docs/en/getting-started" rel="noopener noreferrer"&gt;jest&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/@testing-library/react-native" rel="noopener noreferrer"&gt;@testing-library/react-native&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  About @testing-library/react-native
&lt;/h2&gt;

&lt;p&gt;One important thing to point out is that this testing library has changed many times and migrations were needed from one version to another. This tutorial will work with the latest version at the time of writing it (&lt;code&gt;7.0.1&lt;/code&gt;). For migrations, you can check their &lt;a href="https://callstack.github.io/react-native-testing-library/docs/migration-v7/#guide-for-testing-libraryreact-native-users" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  App to be tested
&lt;/h2&gt;

&lt;p&gt;For this tutorial, we are going to use &lt;a href="https://github.com/ManuViola77/testing_react_native_apps" rel="noopener noreferrer"&gt;this&lt;/a&gt; simple app, but of course, you can apply everything learned in any app you want. In this app, we just have two simple screens with some components we can test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Since we are going to use the &lt;code&gt;@testing-library/react-native&lt;/code&gt; library, we need to install it. To use the specific &lt;code&gt;7.0.1&lt;/code&gt; version we can install it like this:&lt;br&gt;
&lt;code&gt;npm install @testing-library/react-native@7.0.1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We also need to configure jest. We should create a &lt;code&gt;jest.config.js&lt;/code&gt; file in the projects root folder and for our example, we can fill it with this content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;modulePathIgnorePatterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;extras&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;setupFiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;rootDir&amp;gt;/node_modules/react-native-gesture-handler/jestSetup.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;transformIgnorePatterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node_modules/(?!(jest-)?react-native|react-(native|universal|navigation)-(.*)|@react-native-community/(.*)|@react-navigation/(.*)|bs-platform|@rootstrap/redux-tools)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I will mention some of these configurations later in this tutorial and you can find more information &lt;a href="https://jestjs.io/docs/en/configuration" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests folder
&lt;/h2&gt;

&lt;p&gt;As you can see, our project already has a &lt;code&gt;__tests__&lt;/code&gt; folder with an &lt;code&gt;App-test.js&lt;/code&gt; file inside. This tests if the app is created and rendered correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating tests
&lt;/h2&gt;

&lt;p&gt;To actually create a test, we should create a file with a &lt;code&gt;.spec.js&lt;/code&gt; extension inside the &lt;code&gt;__tests__&lt;/code&gt; folder. We could even add folders inside to better organize the tests, for example, add a folder called &lt;code&gt;screens&lt;/code&gt; and have one test per screen inside.&lt;/p&gt;

&lt;p&gt;Inside these tests, we are going to use &lt;code&gt;describe()&lt;/code&gt; just to describe what we want to test, &lt;code&gt;it()&lt;/code&gt; (same as &lt;code&gt;test()&lt;/code&gt;) to create the tests we want to have, &lt;code&gt;expect()&lt;/code&gt; to assert our expected behavior and &lt;code&gt;beforeEach()&lt;/code&gt; to execute some code before each &lt;code&gt;it()&lt;/code&gt; test. &lt;a href="https://jestjs.io/docs/en/api" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is a complete list of &lt;code&gt;jest&lt;/code&gt; methods to use inside tests.&lt;/p&gt;

&lt;p&gt;Correspondingly, we use the &lt;code&gt;@testing-library/react-native&lt;/code&gt; library to test our components behavior. We could use &lt;code&gt;fireEvent&lt;/code&gt; to fire a button pressed event or &lt;code&gt;waitFor()&lt;/code&gt; to wait for promises results. &lt;a href="https://www.native-testing-library.com/docs/cheat-sheet" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is a cheat sheet with the possible methods to use.&lt;/p&gt;

&lt;p&gt;Notice that to know how to ask for a specific component, we can simply add the &lt;code&gt;testID="componentId"&lt;/code&gt; property to the component we want to identify. To simplify, I already added this property to all the components we are testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating tests for our App
&lt;/h2&gt;

&lt;p&gt;Now we need to create our own tests, so we can test our &lt;code&gt;MainScreen&lt;/code&gt; and &lt;code&gt;SecondaryScreen&lt;/code&gt;. We can create the &lt;code&gt;screens&lt;/code&gt; folder inside the &lt;code&gt;__tests__&lt;/code&gt; folder to have better organized tests.&lt;/p&gt;

&lt;p&gt;Also, we are going to need some extra files to handle navigation and screen parameters data, so we can create an &lt;code&gt;extras&lt;/code&gt; folder inside the &lt;code&gt;__tests__&lt;/code&gt; folder for this purpose. If you paid attention you may have noticed that we added &lt;code&gt;modulePathIgnorePatterns: ['extras']&lt;/code&gt; to the &lt;code&gt;jest.config.js&lt;/code&gt; file. This is to tell jest to ignore the &lt;code&gt;.js&lt;/code&gt; files that this folder contains, otherwise, it will consider them as tests and we don't want that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Navigation helper
&lt;/h3&gt;

&lt;p&gt;To test our app we need to start on some screen. If we use a simple &lt;code&gt;render AppStack&lt;/code&gt; we would always start on the same screen and we would need to have some flow to go to a different one. To avoid this, we could just start our stack with the screen we want and if we need to test our screen redirecting to some other screen we just need to add that other screen to the stack for the test to work.&lt;/p&gt;

&lt;p&gt;Keeping this in mind, we will create a &lt;code&gt;helpers.js&lt;/code&gt; file inside the &lt;code&gt;extras&lt;/code&gt; folder that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;NavigationContainer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@react-navigation/native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createStackNavigator&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@react-navigation/stack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@testing-library/react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createStackNavigator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderOtherComponents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;otherComponents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screenConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;otherComponents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Screen&lt;/span&gt;
        &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;screenConfig&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderWithNavigation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;mainComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;otherComponents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;navigatorConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;screenConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NavigationContainer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Navigator&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;navigatorConfig&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Screen&lt;/span&gt;
          &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;screenConfig&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TestNavigator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mainComponent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;otherComponents&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
          &lt;span class="nf"&gt;renderOtherComponents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;otherComponents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screenConfig&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Stack.Navigator&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/NavigationContainer&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will get more into detail when we use it in the &lt;code&gt;SecondaryScreen&lt;/code&gt; test.&lt;/p&gt;

&lt;h3&gt;
  
  
  MainScreen test
&lt;/h3&gt;

&lt;p&gt;Inside the &lt;code&gt;screens&lt;/code&gt; folder, we should create the &lt;code&gt;MainScreen.spec.js&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Since we are in the AppStack's first screen we can just render the entire &lt;code&gt;AppStack&lt;/code&gt; and it would start rendering the screen we want.&lt;/p&gt;

&lt;p&gt;This file should test the &lt;code&gt;MainScreen&lt;/code&gt; components existence and behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It renders the &lt;code&gt;MainScreen&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;It renders the &lt;code&gt;button-to-secondary-screen&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;When pressing &lt;code&gt;button-to-secondary-screen&lt;/code&gt; component, it redirects to the &lt;code&gt;SecondaryScreen&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;It renders the &lt;code&gt;alert-button&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;When pressing &lt;code&gt;alert-button&lt;/code&gt; component, it shows an alert&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these items should translate to an &lt;code&gt;it()&lt;/code&gt; jest method and so our &lt;code&gt;MainScreen.spec.js&lt;/code&gt; file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;act&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@testing-library/react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Alert&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;AppStack&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../src/navigators/AppStack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;renderWithNavigation&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../extras/helpers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;MainScreen /&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderWithNavigation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppStack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should render the main screen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MainScreen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should render the go to secondary screen button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button-to-secondary-screen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when go to secondary screen button is pressed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;it should render SecondaryScreen &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;press&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button-to-secondary-screen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SecondaryScreen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should render the alert button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alert-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when take alert button is pressed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;it should render an Alert &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alertSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Alert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;press&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alert-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;alertSpy&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SecondaryScreen test
&lt;/h3&gt;

&lt;p&gt;Inside the &lt;code&gt;screens&lt;/code&gt; folder we should create the &lt;code&gt;SecondaryScreen.spec.js&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Since we are not in the AppStack's first screen and our screen receives parameters, we are going to take advantage of the helper we created. We will call the function &lt;code&gt;renderWithNavigation&lt;/code&gt; with these parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mainComponent&lt;/code&gt;: &lt;code&gt;SecondaryScreen&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;otherComponents&lt;/code&gt;: &lt;code&gt;[{name: 'MainScreen', component: MainScreen}]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;screenConfig&lt;/code&gt;: &lt;code&gt;{initialParams: { screenParameters }}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will create a &lt;code&gt;data.js&lt;/code&gt; file inside the &lt;code&gt;extras&lt;/code&gt; folders to set our &lt;code&gt;screenParameters&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;screenParameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;paramOne&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This is a parameter that is a text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;paramTwo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This is a parameter that is an object with a content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file should test the &lt;code&gt;SecondaryScreen&lt;/code&gt; components existence and behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It renders the &lt;code&gt;SecondaryScreen&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;It renders the &lt;code&gt;back-button&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;When pressing &lt;code&gt;back-button&lt;/code&gt; component, it redirects to the &lt;code&gt;MainScreen&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;It renders the &lt;code&gt;title&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;It renders the &lt;code&gt;param-one&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;It renders the &lt;code&gt;param-two-content&lt;/code&gt; component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice that our &lt;code&gt;param-one&lt;/code&gt; and &lt;code&gt;param-two-content&lt;/code&gt; only render if they have content to render, so if our tests pass means that the screen is getting the parameters right!&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;SecondaryScreen.spec.js&lt;/code&gt; file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;act&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@testing-library/react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;screenParameters&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../extras/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;renderWithNavigation&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../extras/helpers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SecondaryScreen&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../src/screens/SecondaryScreen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;MainScreen&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../src/screens/MainScreen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;SecondaryScreen /&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;otherComponents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MainScreen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MainScreen&lt;/span&gt;&lt;span class="p"&gt;}];&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderWithNavigation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SecondaryScreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;otherComponents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;screenConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;initialParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;screenParameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should render SecondaryScreen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SecondaryScreen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should render the go back button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;back-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when go back button is pressed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;it should render MainScreen &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;press&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;back-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MainScreen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should render title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should render param one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;param-one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should render param two content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;param-two-content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running tests
&lt;/h2&gt;

&lt;p&gt;To run the tests we just simply need to execute &lt;code&gt;npm test&lt;/code&gt;. This command will run all the tests we have under the &lt;code&gt;__tests__&lt;/code&gt; folder. If we want to just run one particular test, we can do &lt;code&gt;npm test __tests__/screens/one_particular_test.js&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;We can see how our tests run successfully:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8nl93txlxdf8k8sq6s2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8nl93txlxdf8k8sq6s2.png" alt="Our tests"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging tests
&lt;/h2&gt;

&lt;p&gt;If we want to debug our tests we can use &lt;a href="https://www.npmjs.com/package/ndb" rel="noopener noreferrer"&gt;ndb&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To use this, first, we need to install it, as the documentation explains, by running &lt;code&gt;npm install ndb&lt;/code&gt; and you can add the &lt;code&gt;-g&lt;/code&gt; option to make it global.&lt;/p&gt;

&lt;p&gt;Then, we can configure our project by setting the value &lt;code&gt;"test:debug": "ndb jest",&lt;/code&gt; inside our &lt;code&gt;package.json&lt;/code&gt; file under the &lt;code&gt;"scripts"&lt;/code&gt; section (we can put it right after the &lt;code&gt;"test"&lt;/code&gt; value).&lt;/p&gt;

&lt;p&gt;Finally, we can debug our tests by running &lt;code&gt;npm run test:debug&lt;/code&gt; and this will install Chromium where you will be able to set breakpoints in your tests and debug them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mocks
&lt;/h2&gt;

&lt;p&gt;Since our app was really simple, we didn't need to implement mocks, but it's really an important part of testing react native apps. I will explain why we need them and how we can create them.&lt;/p&gt;

&lt;p&gt;The first thing you need to know is that there are lots of functionalities you will have to mock in order to use and test. For this purpose, you can create a &lt;code&gt;__mocks__&lt;/code&gt; folder inside the &lt;code&gt;__tests__&lt;/code&gt; one. Every file in there will be named after a react native library, so when you are testing something that uses that library, jest will take and use the implementation made in the mock.&lt;/p&gt;

&lt;p&gt;Let's exemplify. Suppose that in our project we have a backend we access through a URL that we have saved in an &lt;code&gt;.env&lt;/code&gt; file with the name &lt;code&gt;API_URL&lt;/code&gt; and we use the library &lt;code&gt;react-native-config&lt;/code&gt; to access that information. Our tests won't be able to access that &lt;code&gt;.env&lt;/code&gt; file, so instead, we have to mock it. We would create (if we haven't already) the &lt;code&gt;__mocks__&lt;/code&gt; folder and inside we would create a file named &lt;code&gt;react-native-config.js&lt;/code&gt; that contains the URL we want, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://mock.com/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A slightly more complex example would be if we use an &lt;code&gt;ImagePicker&lt;/code&gt; to access our photos gallery. Let's say we use the &lt;code&gt;react-native-image-crop-picker&lt;/code&gt; library and in our code we call &lt;code&gt;ImagePicker.openPicker()&lt;/code&gt; expecting a selected image as a result. If our test calls the button that opens the picker expecting a selected image as a result but we don't have the picker mocked, our test would fail, getting a null result, which is not what we expected. So, we have to create our &lt;code&gt;react-native-image-crop-picker.js&lt;/code&gt; file and inside we could have something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;openPicker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;mockImplementation&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we are doing here is mocking our &lt;code&gt;openPicker&lt;/code&gt; function, by saying it is a function (&lt;code&gt;jest.fn()&lt;/code&gt;) and that its mocked implementation would be &lt;code&gt;Promise.resolve(result)&lt;/code&gt;. Notice that since our real &lt;code&gt;openPicker&lt;/code&gt; is a promise, our mocked implementation is also a promise. We can decide if we want our mocked promise to be completed successfully (with &lt;code&gt;Promise.resolve()&lt;/code&gt;) or if we want it to fail (with &lt;code&gt;Promise.reject()&lt;/code&gt;). We also decide if we want to return something, like in this case, we are returning &lt;code&gt;result&lt;/code&gt;. A question you might be asking is, how do I test both fail and success results by only mocking one &lt;code&gt;openPicker&lt;/code&gt; function? Well, the answer is quite simple actually, and it would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;openPicker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// default implementation&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mockImplementationOnce&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// first time is called&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mockImplementationOnce&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="c1"&gt;// second time is called&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using the method &lt;code&gt;mockImplementationOnce()&lt;/code&gt; we can give different behaviors to our function, depending on each time you call it. So in this case, the first time we call it we would get a rejected promise (for example to mock that we don't have access to the gallery yet), the second time would be a successful case with our expected result and if we were to call it more times it would use the default implementation inside the &lt;code&gt;fn()&lt;/code&gt; method, which in this case would also be successful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we saw how to use &lt;code&gt;jest&lt;/code&gt; and &lt;code&gt;@testing-library/react-native&lt;/code&gt; to test React Native Apps. You can find the complete GitHub project (with tests included) &lt;a href="https://github.com/ManuViola77/testing_react_native_apps_with_tests" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Read this article and more content in the Rootstrap blog: &lt;a href="https://www.rootstrap.com/blog/how-to-test-react-native-apps/" rel="noopener noreferrer"&gt;https://www.rootstrap.com/blog/how-to-test-react-native-apps/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>testing</category>
      <category>jest</category>
      <category>mock</category>
    </item>
    <item>
      <title>How to customize fonts in React Native</title>
      <dc:creator>Manuela Viola</dc:creator>
      <pubDate>Thu, 23 Jun 2022 19:38:27 +0000</pubDate>
      <link>https://dev.to/rootstrap/how-to-customize-fonts-in-react-native-1mjl</link>
      <guid>https://dev.to/rootstrap/how-to-customize-fonts-in-react-native-1mjl</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;At Rootstrap, my first assignment was to develop a React Native application, that involved maps, chat, and following the design specifications to the letter. While doing this, one of the first things I tried was to set the family font that the application uses, and I found out it wasn't as easy as I expected.&lt;/p&gt;

&lt;p&gt;The biggest issue was that all the tutorials said to execute &lt;code&gt;react-native link&lt;/code&gt;, which in my experience was a bad idea because afterwards the application stopped building successfully. This occurs because since React Native 0.60 released, there is a property called Autolinking that changed how to link a library, as &lt;a href="https://aboutreact.com/react-native-autolinking/"&gt;this post&lt;/a&gt; explains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you will learn how to add and link fonts manually, without using the link command.&lt;/p&gt;

&lt;p&gt;First, you will see all the configurations that need to be done to use custom fonts and then you will learn its usage in a simple application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create Project
&lt;/h3&gt;

&lt;p&gt;First of all, you should create a project in react native. To do that, open the terminal and navigate to the folder you want to create your project in, and execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;react-native init customize_fonts_react_native_tutorial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install libraries
&lt;/h3&gt;

&lt;p&gt;After creating the project, some libraries need to be installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;customize_fonts_react_native_tutorial
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; react-native-global-props
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; babel-plugin-module-resolver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Download and rename fonts
&lt;/h3&gt;

&lt;p&gt;Since this is a tutorial about customizing fonts, you will need to have some fonts downloaded, in order to add them to your React Native project.&lt;/p&gt;

&lt;p&gt;There are many places where you can get these customized fonts. For this tutorial, I used two different fonts, &lt;a href="https://www.wfonts.com/font/good-feeling-sans"&gt;GoodFeelingSans&lt;/a&gt; and &lt;a href="https://www.fontspace.com/dans-disney-font-f24536"&gt;Dan'sDisney&lt;/a&gt;. In both cases, I ended up with a .ttf file.&lt;/p&gt;

&lt;p&gt;In iOS, it is important for the font filename to be the same as its PostScript name. To find the PostScript name, open your &lt;code&gt;ttf&lt;/code&gt; file with Font Book, see what its PostScript name is and rename the font to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EVRQJecq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9sa4mk98me2ie5jrt8cg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EVRQJecq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9sa4mk98me2ie5jrt8cg.png" alt="Font Book" width="876" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configurations in React Native
&lt;/h3&gt;

&lt;p&gt;To use your custom fonts in a React Native project, each platform requires a different configuration, but there are also some general configurations all platforms need.&lt;/p&gt;

&lt;h4&gt;
  
  
  General
&lt;/h4&gt;

&lt;p&gt;In both cases, you will need to create these folders in your project's root: &lt;strong&gt;src/assets/fonts&lt;/strong&gt;. Inside the fonts folder, you should add you custom fonts .ttf files, one for every font you want to use.&lt;/p&gt;

&lt;p&gt;Also, modify &lt;strong&gt;babel.config.js&lt;/strong&gt; adding this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module-resolver&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/assets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Android
&lt;/h4&gt;

&lt;p&gt;In Android, you need to add this folders into android/app/src/main/ : &lt;strong&gt;assets/fonts&lt;/strong&gt;. You should also add your &lt;code&gt;ttf&lt;/code&gt; files to the fonts folder.&lt;/p&gt;

&lt;h4&gt;
  
  
  iOS
&lt;/h4&gt;

&lt;p&gt;In iOS, the configuration is a little more tricky. You need to go to &lt;strong&gt;ios/&lt;/strong&gt; folder and open the&lt;br&gt;
&lt;strong&gt;customize_fonts_react_native_tutorial.xcodeproj&lt;/strong&gt; file with Xcode.&lt;/p&gt;

&lt;p&gt;Afterwards, you need to press on &lt;strong&gt;customize_fonts_react_native_tutorial -&amp;gt; Build Phases&lt;/strong&gt; and find the &lt;strong&gt;Copy Bundle Resources&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wrd8Ivn_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/epeff1r1egnhm7gc1uwj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wrd8Ivn_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/epeff1r1egnhm7gc1uwj.png" alt="Add fonts IOS" width="880" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should add your fonts by pressing the plus symbol, then &lt;strong&gt;Add Other...&lt;/strong&gt; option, select your fonts from your src/assets/fonts folder and &lt;strong&gt;Finish&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TvbDHk-l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/http://g.recordit.co/QpziFaqrls.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TvbDHk-l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/http://g.recordit.co/QpziFaqrls.gif" alt="Fonts selection" width="880" height="426"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Finally, after all these steps you will be able to use customized fonts in your React Native project.&lt;/p&gt;

&lt;p&gt;As an example, you can create a very simple screen, just to test everything is configured correctly. Add in src folder App.js and styles.js files.&lt;/p&gt;

&lt;p&gt;App.js:&lt;/p&gt;




&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;SafeAreaView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TextInput&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setCustomText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCustomTextInput&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native-global-props&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./styles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customTextProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GoodFeelingSans&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nx"&gt;setCustomText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customTextProps&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;setCustomTextInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customTextProps&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SafeAreaView&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;How&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;Customize&lt;/span&gt; &lt;span class="nx"&gt;Fonts&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="nx"&gt;Native&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textDefaultFont&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;This&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;font&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GoodFeelingSans&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textDifferentFont&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;This&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;different&lt;/span&gt; &lt;span class="nx"&gt;font&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DansDisneyUI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TextInput&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This input has default font&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TextInput&lt;/span&gt;
        &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This input has a different font&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputDifferentFont&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/SafeAreaView&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;styles.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;StyleSheet&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;differentFont&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dan'sDisneyUI&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;StyleSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;space-around&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bold&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;textAlign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;inputDifferentFont&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;differentFont&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;textDefaultFont&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;marginVertical&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;textAlign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;textDifferentFont&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;differentFont&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;marginVertical&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;textAlign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also modify &lt;strong&gt;index.js&lt;/strong&gt; to point to new ./src/App instead of ./App.&lt;/p&gt;

&lt;p&gt;In this app, you are using the &lt;code&gt;react-native-global-props&lt;/code&gt; library to set a custom font as default, in case you have an entire app with some default font. This is not necessary if you don't need it, you can just set the custom font only in the texts you want. Note how you can still do this even if you define a default custom font. Also note that there are two different default settings, one for the Text component and another for the TextInput.&lt;/p&gt;

&lt;p&gt;Now, if you execute &lt;code&gt;react-native run-ios&lt;/code&gt; or &lt;code&gt;react-native run-android&lt;/code&gt; you should see the text with your custom fonts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fSvZjrx2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/84zonvv5u2x8q9vka206.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fSvZjrx2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/84zonvv5u2x8q9vka206.png" alt="Results in IOS" width="679" height="747"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  iOS "Unrecognized font family" error
&lt;/h2&gt;

&lt;p&gt;After following all these steps, if you are getting the very common &lt;strong&gt;Unrecognized font family&lt;/strong&gt; error, there are still some things you can check or do.&lt;/p&gt;

&lt;p&gt;First, you should check your &lt;strong&gt;info.plist&lt;/strong&gt; file, located in the &lt;strong&gt;ios/customize_fonts_react_native_tutorial&lt;/strong&gt; folder. This file must have a section with key &lt;strong&gt;UIAppFonts&lt;/strong&gt; that contains an array of strings with your fonts. If this is missing, then you should add this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;UIAppFonts&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;GoodFeelingSans.ttf&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;Dan'sDisneyUI.ttf&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your project has multiple build targets, you should apply this change to their respective &lt;strong&gt;info.plist&lt;/strong&gt; files.&lt;/p&gt;

&lt;p&gt;After this, if you are still getting the error, you could try doing some (or all) of the things listed below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uninstall app from simulator (or device)&lt;/li&gt;
&lt;li&gt;Delete &lt;strong&gt;node_modules&lt;/strong&gt; folder, &lt;strong&gt;package-lock.json&lt;/strong&gt; file and execute &lt;strong&gt;npm install&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;ios&lt;/strong&gt; folder delete &lt;strong&gt;Pods&lt;/strong&gt; folder, &lt;strong&gt;Podfile.lock&lt;/strong&gt; file and execute &lt;strong&gt;pod install&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Reset cache by runnning &lt;strong&gt;npm start --reset-cache&lt;/strong&gt; command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope after doing all this, you now have your new custom fonts working!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you learned how to add, link and use custom fonts in React Native. You can find the GitHub project &lt;a href="https://github.com/ManuViola77/customize_fonts_react_native_tutorial"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Read this article and more content in the Rootstrap blog: &lt;a href="https://www.rootstrap.com/blog/how-to-customize-fonts-in-react-native/"&gt;https://www.rootstrap.com/blog/how-to-customize-fonts-in-react-native/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>fonts</category>
      <category>customize</category>
      <category>style</category>
    </item>
    <item>
      <title>AppleAuth: Ruby Gem for Apple Sign in Rails Integration</title>
      <dc:creator>Antonieta Alvarez</dc:creator>
      <pubDate>Fri, 10 Jun 2022 15:37:36 +0000</pubDate>
      <link>https://dev.to/rootstrap/appleauth-ruby-gem-for-apple-sign-in-rails-integration-3cl3</link>
      <guid>https://dev.to/rootstrap/appleauth-ruby-gem-for-apple-sign-in-rails-integration-3cl3</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h5c59oWo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/93ryggfryr3v7c3v2wwn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h5c59oWo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/93ryggfryr3v7c3v2wwn.png" alt="Person walking in front of an apple store" width="880" height="1110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is AppleAuth?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://rubygems.org/gems/apple_auth"&gt;AppleAuth&lt;/a&gt; is a Ruby gem we developed to integrate Apple Sign In in our server side applications.&lt;/p&gt;

&lt;p&gt;Apple announced Sign in with Apple in the &lt;a href="https://developer.apple.com/videos/play/wwdc2019/706/"&gt;WWDC 2019 Conference&lt;/a&gt;, then in September 2019 Apple updated its &lt;a href="https://developer.apple.com/news/?id=09122019b"&gt;App Store Review Guideline&lt;/a&gt; to state that if your iOS app implements a third-party or social login service (like Facebook Login or Google Sign-In) it will be mandatory to offer Apple sign in as an option by the end of April 2020.  Besides following the standards, implementing apple authentication will let sign in using their two-factor authentication Apple ID. After the user follows Sign in with Apple to log in, your app receives tokens and user information that you can use to authenticate the user in your server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apple sign-in workflow
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5p9bvSQv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o1w2frzn34chtuo6fs68.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5p9bvSQv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o1w2frzn34chtuo6fs68.png" alt="Apple sign in flow" width="880" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more information, check the &lt;a href="https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api"&gt;Apple Official Documentation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How does AppleAuth works?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Validation
&lt;/h3&gt;

&lt;p&gt;Apple authentication follows the OAuth 2.0 flow, and this gem will help us with this flow. After the user signs in the client-side, we will have access to the user_identity, code, and JWT on our server-side. The last one is a token that will be used to validate user authentication with Apple. So Apple_Auth's first step will be to decode and validate the JWT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# with a valid JWT&lt;/span&gt;
&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'000343.1d22d2937c7a4e56806dfb802b06c430...'&lt;/span&gt;
&lt;span class="n"&gt;valid_jwt_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'eyJraWQiOiI4NkQ4OEtmIiwiYWxnIjoiUlMyNTYifQ.eyJpc...'&lt;/span&gt;
&lt;span class="no"&gt;AppleAuth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UserIdentity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valid_jwt_token&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;validate!&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;exp: &lt;/span&gt;&lt;span class="mi"&gt;1595279622&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s2"&gt;"user@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;email_verified: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# with an invalid JWT&lt;/span&gt;
&lt;span class="n"&gt;invalid_jwt_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'eyJraWQiOiI4NkQsd4OEtmIiwiYWxnIjoiUlMyNTYifQ.edsyJpc...'&lt;/span&gt;
&lt;span class="no"&gt;AppleAuth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;UserIdentity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;invalid_jwt_token&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;validate!&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;  &lt;span class="no"&gt;AppleAuth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Conditions&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;JWTValidationError&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;If we successfully validate the JWT, we can authenticate users and get their information. At this point, we can persist refresh-token, to once a day, and if needed get a fresh token from Apple and ensure that the user continues to have their apple_id validated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'cfb77c21ecd444390a2c214cd33decdfb.0.mr...'&lt;/span&gt;
&lt;span class="no"&gt;AppleAuth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;authenticate!&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;access_token: &lt;/span&gt;&lt;span class="s2"&gt;"a7058d..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;expires_at: &lt;/span&gt;&lt;span class="mi"&gt;1595894672&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;refresh_token: &lt;/span&gt;&lt;span class="s2"&gt;"r8f1ce..."&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DeviseTokenAuth Integration
&lt;/h3&gt;

&lt;p&gt;If you already have DeviseTokenAuth gem implemented on your Rails project, you can just run this generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rails g apple_sign_in:appple_auth_controller [scope]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the scope you need to write your path from controllers to your existent devise controllers and set up the routes.&lt;/p&gt;

&lt;p&gt;This generator will create a controller, that implements AppleAuth methods, get the user's email, and register them.&lt;/p&gt;

&lt;p&gt;You can find more info and the guide to install the gem on the &lt;a href="https://github.com/rootstrap/apple_auth"&gt;README&lt;/a&gt;.&lt;br&gt;
You can find a full implementation of this gem in this &lt;a href="https://github.com/rootstrap/apple-sign-in-rails"&gt;demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Read this article and more content in the Rootstrap blog: &lt;a href="https://www.rootstrap.com/blog"&gt;https://www.rootstrap.com/blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Testing in Django &amp; Django REST – Useful Tools &amp; Best Practices</title>
      <dc:creator>brunomichetti</dc:creator>
      <pubDate>Mon, 06 Jun 2022 19:44:10 +0000</pubDate>
      <link>https://dev.to/rootstrap/testing-in-django-django-rest-useful-tools-best-practices-1ie4</link>
      <guid>https://dev.to/rootstrap/testing-in-django-django-rest-useful-tools-best-practices-1ie4</guid>
      <description>&lt;p&gt;If you are a developer, you already know how important testing is in any software project. In particular, automatic testing, as it can help you to corroborate your coding, and quickly see what your program does what you want it to do. It also helps to corroborate any new changes in your code that didn’t break any previous functionalities.&lt;/p&gt;

&lt;p&gt;In saying that, does this mean that if you have automatic tests then your project won’t have any errors? It does not. As computer scientist Edsger W. Dijkstra once said:&lt;/p&gt;

&lt;p&gt;“Program testing can be used to show the presence of bugs, but never to show their absence”.&lt;/p&gt;

&lt;p&gt;This phrase helps us to understand that we can’t be sure that a program is perfect, but, testing is fundamental to help us discover errors and make fixes and improvements. As a person that works in software development, I can say that it’s a lot better when you discover an error rather than your client or a user in production. In this article, I’ll talk about basic knowledge in automatic testing, useful tools, and good practices in &lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; projects, with a focus on API’s.&lt;/p&gt;

&lt;h1&gt;
  
  
  Testing in Django &amp;amp; Django REST Framework
&lt;/h1&gt;

&lt;p&gt;Django has very nice &lt;a href="https://docs.djangoproject.com/en/3.2/topics/testing/"&gt;documentation about testing&lt;/a&gt;, and &lt;a href="https://www.django-rest-framework.org/api-guide/testing/"&gt;Django REST Framework too&lt;/a&gt;. So, in this blog, I’ll talk about the main tools in those two frameworks, and what you can do and use to improve your testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Main Testing Classes
&lt;/h2&gt;

&lt;p&gt;Django provides several classes for testing. The one I’ll talk about here is &lt;a href="https://docs.djangoproject.com/en/3.2/topics/testing/tools/#django.test.TestCase"&gt;TestCase&lt;/a&gt;, which is very useful if your application uses databases.&lt;br&gt;
As the name states, to create a test case in your Django project, you will define a class that inherits from TestCase. By doing this, you can use all the methods and properties of the named class that will help you to create and execute your tests. Then you will define different functions that will correspond to each unit test inside the test case. Let’s see an example of the structure of a test case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# your test file
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCase&lt;/span&gt;


&lt;span class="c1"&gt;# class to define a test case for login
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserLoginTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;# some setup here, explained later
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_correct_login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# unit test
&lt;/span&gt;        &lt;span class="c1"&gt;# Corroborate the expected scenario
&lt;/span&gt;        &lt;span class="p"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_if_password_incorrect_then_cant_login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# unit test
&lt;/span&gt;        &lt;span class="c1"&gt;# Corroborate that user's password needs to be only the correct one
&lt;/span&gt;        &lt;span class="p"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_if_user_not_registered_cant_login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# unit test
&lt;/span&gt;        &lt;span class="c1"&gt;# Corroborate that user's are able to login only if they're registered
&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, you can use the &lt;a href="https://docs.djangoproject.com/en/3.2/topics/testing/tools/#django.test.Client"&gt;Client class&lt;/a&gt; of Django which simulates a dummy browser so you can make HTTP requests and test how your Django API responds.&lt;/p&gt;

&lt;p&gt;Since we are focusing on API testing, I want to talk about &lt;a href="https://www.django-rest-framework.org/api-guide/testing/#api-test-cases"&gt;APITestCase&lt;/a&gt;, a class of Django REST framework that is a mirror of TestCase but uses a different client class: &lt;a href="https://www.django-rest-framework.org/api-guide/testing/#apiclient"&gt;APIClient&lt;/a&gt;. This client extends the TestClient, meaning that it has the same functionalities and adds others such as the credentials function. This function is very useful to overwrite authentication headers, for example, using OAuth1, OAuth2, or any simple token authentication scheme.&lt;br&gt;
So, if you are working on a project with the Django REST framework, you can change the previous example in this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# your test file
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;rest_framework.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;APITestCase&lt;/span&gt;


&lt;span class="c1"&gt;# class to define a test case of login
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserLoginTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;APITestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Main Testing Functions
&lt;/h2&gt;

&lt;p&gt;The aforementioned classes each have a set of methods that will help you through the testing. With these functions, you are able to corroborate that your software works as expected and make necessary test fixtures.&lt;br&gt;
A test fixture is an environment that you can create to run your tests using consistency. Using fixtures you can make sure that certain conditions are met before the execution. For example, to have a determined set of data in your testing database, or to create needed objects. Here is a list of the main ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;setUp&lt;/code&gt; and &lt;code&gt;tearDown&lt;/code&gt;: These are functions to be executed before (setUp) and after (tearDown) in each unit test. These are very useful for fixtures.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setUpClass&lt;/code&gt; and &lt;code&gt;tearDownClass&lt;/code&gt;: In an analogous way of setUp and tearDown, these functions are executed before (setUpClass) and after (tearDownClass) during the whole test case. This means that it is executed only once for a test case. Since they’re used by Django to make important configurations, if you overwrite these methods in your test case class, don’t forget to call the super implementation inside the functions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setUpTestData&lt;/code&gt;: this function can be used to have a class-level atomic block to define data for the whole test case. That means that this function automatically rollbacks the changes in the database after the finalization of all the unit tests in the test case. This is mainly used to load data to your test database.&lt;/li&gt;
&lt;li&gt;Assert methods: Django has the set of &lt;a href="https://docs.python.org/3/library/unittest.html#assert-methods"&gt;assert methods from unittest&lt;/a&gt;, and has &lt;a href="https://docs.djangoproject.com/en/3.2/topics/testing/tools/#assertions"&gt;another ones created for the framework&lt;/a&gt;. These methods, such as &lt;code&gt;assertEqual&lt;/code&gt;, &lt;code&gt;assertIsNone&lt;/code&gt;, &lt;code&gt;assertTrue&lt;/code&gt;, etc, can be used in the unit test to check the different conditions that have to be met. For example, in the unit test of login, you can assert that the response has a status code of 200 because you want to make sure that if the data is sent correctly, then your application has to return the response with that code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can avoid using these functions, or use any combination of them. They’re not needed, but they’re very useful and will help you a lot.&lt;/p&gt;
&lt;h2&gt;
  
  
  Test Discovery &amp;amp; Databases
&lt;/h2&gt;

&lt;p&gt;Where do you have to put your test classes? By default, Django recognizes any file that fulfills the pattern &lt;code&gt;test*.py&lt;/code&gt; under the current working directory. That is, any file under the directory that starts with a test, and of course, has the &lt;code&gt;.py&lt;/code&gt; extension. Inside it, Django will execute any function in the test case class starting with test.&lt;br&gt;
When you run your tests, you can pass a parameter indicating the desired pattern just in case you want to use a different one. With that behavior, the place to put the test cases can be anywhere, but I recommend dividing the tests into the several Django apps that you may have in your project. For example, if you have a Django app called &lt;code&gt;users&lt;/code&gt; that has the models and functionalities related to the users in your project, you can define in there a test folder as a module (adding the init.py file) with all the tests that cover that important part: tests for login, for sign-up, for user data, account deletion, etc. Something similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my_project
   |__django_apps
       |__users
          |__test
             |__ __init__.py
             |__ test_login.py
             |__ test_signup.py
             |__ test_delete_user.py
             ...
        ...
    ...
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Regarding the databases, when you run your tests by default, Django creates one and then destroys it after the finalization. This is to avoid conflicts between your production and/or development databases, as well as your testing database. You can define and customize a database for tests, inside the &lt;code&gt;DATABASES&lt;/code&gt; dictionary, adding another one named &lt;code&gt;TEST&lt;/code&gt;. Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASES = {
    'default': {
        'ENGINE': '&amp;lt;engine&amp;gt;',
        'NAME': '&amp;lt;database name&amp;gt;',
        'USER': '&amp;lt;database user&amp;gt;',
        ...
    },
    ...
    'TEST': {
        # testing database customization here
            'NAME': '&amp;lt;your testing database name&amp;gt;',
            # some other customization, for example the user user, password, etc
        },
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a look at the &lt;a href="https://docs.djangoproject.com/en/3.2/ref/settings/#test"&gt;available keys&lt;/a&gt; for the TEST dictionary. If you don’t define it, Django will create a database naming it equal to your database in the default settings, appending the _test suffix.&lt;/p&gt;

&lt;h1&gt;
  
  
  Useful Tools
&lt;/h1&gt;

&lt;p&gt;In this section, I will talk about two external tools that are very helpful for fixtures: &lt;a href="https://faker.readthedocs.io/en/master/"&gt;Faker&lt;/a&gt; and &lt;a href="https://factoryboy.readthedocs.io/en/stable/"&gt;Factory Boy&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Faker
&lt;/h2&gt;

&lt;p&gt;Faker is a python package used to generate fake but realistic data. You can use its functionalities to load data into your testing database, generate data for the requests that you want to test, generate data for the models, etc. Faker has a huge and diverse set of possibilities. You can generate execution time, for example, first names, last names, phone numbers, dates, passwords, emails, etc.&lt;/p&gt;

&lt;p&gt;Additionally, you can pass parameters to the functions to generate data with different constraints, for example, you can obtain a password specifying if you want to use special characters or not, the desired length, and if you want to have an upper case or not, etc. Take a look at the Faker &lt;a href="https://faker.readthedocs.io/en/master/providers.html"&gt;providers&lt;/a&gt; to see all the different fake but realistic data that you can generate for your tests. Instead of using for example &lt;a href="mailto:user_test@mail.com"&gt;user_test@mail.com&lt;/a&gt; for your test cases, you can have data that is closer to reality and improve the quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Factory Boy
&lt;/h2&gt;

&lt;p&gt;If you use factories for tests, you define in a class the data structure that you want to have in your testing database. Then in your tests, it will be easier to have instances of your models, and loaded data with the desired structure. Factory Boy is a tool to replace static, hard-to-maintain fixtures using factories. Also, you can combine it with Faker to have factories with fake but realistic data. Let’s see an example:&lt;/p&gt;

&lt;p&gt;In this case, we can see a static fixture of Django, to load data to the testing database in a model called Person, which has a first name and last name. This JSON file defines two instances:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myapp.person"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"first_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lennon"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myapp.person"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"first_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Paul"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"McCartney"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s see the analogous definition using Factory Boy and Faker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;factory&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;myapp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt; 

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PersonFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DjangoModelFactory&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;

    &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'first_name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'last_name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, you can use functions that Factory Boy provides, for example, &lt;code&gt;PersonFactory.create_batch(2)&lt;/code&gt; to create instantly 2 instances in your testing database of Persons with different and realistic first and last names. In the JSON case, if you want to have for example 10 instances, you will need to add them and define new first and last names explicitly. And if you want to have huge datasets, the JSON file will be enormous and hard to maintain, while in the factory case you only need to call a function. Also, what if the model Person changes? In the JSON case, you have to update one by one each defined instance. In the factory case, you only need to change a class definition. Factory Boy documentation has a section called &lt;a href="https://factoryboy.readthedocs.io/en/stable/recipes.html"&gt;common recipes&lt;/a&gt;, where you can find useful tools, practices, and tips from the library. Here is a list of interesting functionalities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can create factories of models including the model relationships using the RelatedFactory or SubFactory class.&lt;/li&gt;
&lt;li&gt;Use the create function to create a new instance of the model in the factory and save it into the testing database. If you want to have instances but without saving them into the database, use build instead.&lt;/li&gt;
&lt;li&gt;In an analogous way, you can create and save into the database N instances of the model in the factory using &lt;code&gt;create_batch(N)&lt;/code&gt;, and &lt;code&gt;build_batch(N)&lt;/code&gt; if you don’t want to save them.&lt;/li&gt;
&lt;li&gt;You can define an attribute in a factory that selects a choice of a set of choices just like a Django model choice field, using &lt;code&gt;random_element&lt;/code&gt; of Faker. In the next section, we will see an example of this.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Test Example &amp;amp; Good Practices
&lt;/h1&gt;

&lt;p&gt;In this section, I want to wrap up and show a little example. This is the tiny reality: In the project, we have a Django app called users where we have the user model that has a username, phone_number, and a category in the system, that could be admin, common user, and guest. The user can sign-up and login into the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model &amp;amp; Factory Definition
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# model.py in the Django app called users
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AbstractUser&lt;/span&gt;


&lt;span class="c1"&gt;# class to define choices
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextChoices&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;GUEST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'G'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Guest user'&lt;/span&gt;
    &lt;span class="n"&gt;COMMON_USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'C'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Common user'&lt;/span&gt;
    &lt;span class="n"&gt;ADMIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Admin user'&lt;/span&gt;


&lt;span class="c1"&gt;# user model that inherits from AbstractUser
# there is no need to define a username field
# because is defined in the parent class
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AbstractUser&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blank&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'G'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s see the implementation of the user factory. This file is inside a test folder in the users Django app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# factory.py inside users/test
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;faker&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;FakerClass&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;factory&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_generation&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;users.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Category&lt;/span&gt;


&lt;span class="n"&gt;CATEGORIES_VALUES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DjangoModelFactory&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;

    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'user_name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'phone_number'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'random_element'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elements&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CATEGORIES_VALUES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;post_generation&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extracted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;extracted&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;extracted&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;FakerClass&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;special_chars&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;upper_case&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;lower_case&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A couple of things about this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the &lt;code&gt;Meta&lt;/code&gt; class inside the factory, we have to indicate the corresponding model.&lt;/li&gt;
&lt;li&gt;In the category attribute, we use &lt;code&gt;random_element&lt;/code&gt; of Faker to randomly select a choice of the Category choice list. We’ve defined &lt;code&gt;CATEGORIES_VALUES&lt;/code&gt; to specify which part of the choice we want to take. That is because each element in the choices list is a tuple, where the first part is the value, and the second one the explanatory text. So, that &lt;code&gt;CATEGORIES_VALUES&lt;/code&gt; set has only that first part for each element.&lt;/li&gt;
&lt;li&gt;Through the &lt;code&gt;post_generator&lt;/code&gt; decorator and the password function, we indicate that at the moment of the creation of a user instance, we can pass a desired password to be used, or else we generate one using the Faker functionality.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Django Test Case
&lt;/h2&gt;

&lt;p&gt;Now, let’s see an example of testing for the user sign-up functionality. The implementation of the authentication part for this example project has been made using dj-rest-auth. I won’t talk about that, but if you want to know more, please take a look at &lt;a href="https://www.rootstrap.com/blog/registration-and-authentication-in-django-apps-with-dj-rest-auth/"&gt;this blog&lt;/a&gt;. This is a test_singup.py file inside the test folder of the user Django app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.urls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;rest_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;rest_framework.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;APITestCase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;APIClient&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;users.test.factory&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UserFactory&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;users.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Category&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;faker&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserSignUpTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;APITestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setUpClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;setUpClass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_saved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;APIClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signup_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'rest_register'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;faker_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_if_data_is_correct_then_signup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Prepare data
&lt;/span&gt;        &lt;span class="n"&gt;signup_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;'password1'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'test_Pass'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;'password2'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'test_Pass'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;'phone_number'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;'category'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;# Make request
&lt;/span&gt;        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signup_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signup_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Check status response
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTP_201_CREATED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Check database
&lt;/span&gt;        &lt;span class="n"&gt;new_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;new_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;new_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_if_username_already_exists_dont_signup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Prepare data with already saved user
&lt;/span&gt;        &lt;span class="n"&gt;signup_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;'password1'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'test_Pass'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;'password2'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'test_Pass'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;'phone_number'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;'category'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;# Make request
&lt;/span&gt;        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signup_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signup_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Check status response
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTP_400_BAD_REQUEST&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
            &lt;span class="s"&gt;'A user with that username already exists.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Check database
&lt;/span&gt;        &lt;span class="c1"&gt;# Check that there is only one user with the saved username
&lt;/span&gt;        &lt;span class="n"&gt;username_query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username_query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, in this file we have a test case, defined as &lt;code&gt;UserSignUpTestCase&lt;/code&gt;, and two unit tests inside, one to test the expected case, and the other to test an error case. I want to list a couple of main points to discuss good practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Take a look at the unit tests names: You can name them as you want (always starting with test unless you change the pattern). A good practice is to make a name that describes it. This will help you when testing and debugging, for example, if a test fails and you want to trace the error.&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;setUpClass&lt;/code&gt; method, we call the &lt;code&gt;super()&lt;/code&gt; function, remembering that it’s important for Django. Also, we set up the needed instances, such as the API client, a user, and a faker instance. Besides we load a user to the database with the create function.&lt;/li&gt;
&lt;li&gt;Notice that the &lt;code&gt;sign_up&lt;/code&gt; URL defined in the &lt;code&gt;setUpClass&lt;/code&gt; method, uses &lt;code&gt;reverse&lt;/code&gt; from Django. You can put directly the URL string if you want. By using reverse, you obtain an URL entering the related name. In the dj-rest-auth package, the related name is rest_register. I recommend you use reverse in your project to improve maintainability. When you define URLs, give them a name, and then in the tests use reverse. After that, if a URL changes, you won’t need to change anything in the tests, because reverse solves this.&lt;/li&gt;
&lt;li&gt;The general structure chosen for the unit test is:

&lt;ul&gt;
&lt;li&gt;Prepare data: You can use the instance generated with build to obtain the data for the request.&lt;/li&gt;
&lt;li&gt;Make the request: Use the APIClient to make the request.&lt;/li&gt;
&lt;li&gt;Check the response: Check the response data (if there is data) and status. When working with Django REST framework, I recommend using &lt;a href="https://www.django-rest-framework.org/api-guide/status-codes/"&gt;status&lt;/a&gt;. With this, you have the HTTP status codes in a cleaner way. If you don’t want to, you can just put the number directly.&lt;/li&gt;
&lt;li&gt;Check the database: If the request generates changes in the database, please corroborate it to make sure that the functionality modifies it correctly. If you have the case that doesn’t modify the database, it’s a good practice to test that the database remains without changes (just as in the second unit test).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was an example of a Test Case with two unit tests. You can add more unit tests, for example, to test if some data is incorrect in the request or the user can’t sign-up. Besides you can add in that file (or another file) more test case classes, for example, to test the login functionality of the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Your Tests
&lt;/h2&gt;

&lt;p&gt;Once you have coded a couple of tests, you can execute them with this command in the main folder of the project (the one that has the manage.py file):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will search for all of your tests files under that folder and then execute them, showing the error or success of each unit test.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customize the Execution of the Tests
&lt;/h3&gt;

&lt;p&gt;You can customize the test execution, for example, you can indicate to test:&lt;/p&gt;

&lt;p&gt;Only a Django app, specifying &lt;code&gt;&amp;lt;Django app name&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py test users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only a file in a given Django app, specifying &lt;code&gt;&amp;lt;Django app name&amp;gt;.path.to.file&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py test users.test.test_signup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only a test case class, specifying: &lt;code&gt;&amp;lt;Django app name&amp;gt;.path.to.file.ClassName&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py test users.test.test_signup.UserSignUpTestCase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only a unit test function, specifying &lt;code&gt;&amp;lt;Django app name&amp;gt;.path.to.file.ClassName.function_name&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py test users.test.test_signup.UserSignUpTestCase.test_if_data_is_correct_then_signup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also add some flags to the command to customize the execution, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--keepdb&lt;/code&gt; to avoid erasing the database between executions. This can improve the speed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--parallel&lt;/code&gt; to run the tests in parallel improving also the speed on multi-core hardware. If you do this make sure they’re well isolated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Throughout this blog, I’ve presented some basics of testing with Django and Django REST framework. Also, I showcased some useful tools and good practices that are more focused on API’s. I highlighted some examples, and how to run and customize your tests with the named framework. I hope you enjoyed reading and I hope that this blog can help you to test and improve your own Django API projects.&lt;/p&gt;

&lt;p&gt;Read this article and more content in the Rootstrap blog: &lt;a href="https://www.rootstrap.com/blog"&gt;https://www.rootstrap.com/blog&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
