<?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: Rabbil Yasar Sajal </title>
    <description>The latest articles on DEV Community by Rabbil Yasar Sajal  (@rabbilyasar).</description>
    <link>https://dev.to/rabbilyasar</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F638533%2F5b6a18b3-da82-494e-9ee4-097eeb78c232.png</url>
      <title>DEV Community: Rabbil Yasar Sajal </title>
      <link>https://dev.to/rabbilyasar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rabbilyasar"/>
    <language>en</language>
    <item>
      <title>Why .env.example Gets Out of Sync — and What Actually Fixes It</title>
      <dc:creator>Rabbil Yasar Sajal </dc:creator>
      <pubDate>Mon, 24 Aug 2026 21:59:00 +0000</pubDate>
      <link>https://dev.to/rabbilyasar/why-envexample-gets-out-of-sync-and-what-actually-fixes-it-1a76</link>
      <guid>https://dev.to/rabbilyasar/why-envexample-gets-out-of-sync-and-what-actually-fixes-it-1a76</guid>
      <description>&lt;h2&gt;
  
  
  Quick answer
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;.env.example&lt;/code&gt; goes out of sync because it's a plain text file with no way to enforce that it matches your code — nothing runs when a variable is added, removed, or made conditional. The fix isn't a better example file; it's a schema: a single, versioned file that declares what your configuration is actually supposed to be, which a tool then &lt;em&gt;generates&lt;/em&gt; the example file from and validates everything else against. In EnvShield, that schema is &lt;code&gt;env.schema.toml&lt;/code&gt; — &lt;code&gt;envshield schema sync&lt;/code&gt; generates &lt;code&gt;.env.example&lt;/code&gt; from it, and &lt;code&gt;envshield schema sync --check&lt;/code&gt; fails the moment the two drift, typically wired into a pre-commit hook so it's caught before the commit lands, not after a teammate hits it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Open almost any repo's &lt;code&gt;.env.example&lt;/code&gt; and you'll find the same story: it was accurate the day someone wrote it, and it's been quietly drifting ever since. A teammate adds &lt;code&gt;STRIPE_WEBHOOK_SECRET&lt;/code&gt; to the payment service, the PR gets reviewed for logic, and nobody thinks to update the template — why would they? It's not code. Six weeks later someone clones the repo, copies &lt;code&gt;.env.example&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt;, runs the app, and it crashes on a variable the file never mentioned.&lt;/p&gt;

&lt;p&gt;Search for this problem and you'll find a lot of people asking variations of the same three questions: how do I keep &lt;code&gt;.env&lt;/code&gt; and &lt;code&gt;.env.example&lt;/code&gt; in sync, why does &lt;code&gt;.env.example&lt;/code&gt; keep missing environment variables, and is there a better way to do environment variable documentation than a hand-maintained text file. This article is about why the file drifts in the first place, and what actually stops it — not just what to rename it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it happens
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;.env.example&lt;/code&gt; isn't badly designed. It's just structurally unable to enforce itself. It's a text file with no relationship to the code that reads the variables it lists, no relationship to the deployment manifest that's supposed to provide them, and no mechanism that runs when either of those changes. Someone has to remember to update it, by hand, every single time — and "someone has to remember" is exactly the kind of process that survives right up until it doesn't.&lt;/p&gt;

&lt;p&gt;The result is a file that's simultaneously trusted and untrustworthy. New teammates copy it assuming it's complete. It usually isn't. Not because anyone was careless, but because nothing in the workflow ever made keeping it current cheaper than ignoring it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What people do about it today
&lt;/h2&gt;

&lt;p&gt;A few common approaches, and where each one runs out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manual discipline and code review.&lt;/strong&gt; Ask reviewers to check for new env vars during PR review. Works until the variable is added inside a conditional branch nobody exercises in the diff, or a reviewer just misses it — which, over enough PRs, is a when, not an if.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A comment or checklist in the README.&lt;/strong&gt; "Remember to update &lt;code&gt;.env.example&lt;/code&gt; when you add a variable." This is a policy, not a mechanism. Policies that depend on memory have the same failure mode as the problem they're meant to solve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A custom sync script.&lt;/strong&gt; Some teams write a script that diffs &lt;code&gt;.env&lt;/code&gt; against &lt;code&gt;.env.example&lt;/code&gt; and fails CI if they don't match. This genuinely helps — it catches &lt;em&gt;drift between two files&lt;/em&gt;. What it can't do is tell you a variable is missing from &lt;em&gt;both&lt;/em&gt; of them, because it only knows about keys, not requirements. It can't tell you &lt;code&gt;PORT=banana&lt;/code&gt; is invalid, and a hand-maintained &lt;code&gt;.env.example&lt;/code&gt; has no way to express that a variable is only required when another flag is set — at best it can carry that as a comment someone wrote once; nothing enforces it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema-based &lt;code&gt;.env&lt;/code&gt; tooling.&lt;/strong&gt; This is the right direction, and it's not just EnvShield's idea — &lt;a href="https://varlock.dev" rel="noopener noreferrer"&gt;Varlock&lt;/a&gt;, for one, takes the same core position: a committed schema, not a hand-maintained example file, should be the source of truth, and it adds real things on top of that (type-safe resolution, automatic secret redaction in logs). If your stack is JS/TS-centric and your concern is specifically keeping &lt;code&gt;.env&lt;/code&gt; files honest inside that ecosystem, it's worth a look. EnvShield takes the same schema-as-contract idea further in a different direction: the same schema also validates against a Docker Compose or Kubernetes manifest, a second service written in a different language, and the actual git history of what the contract used to require — not just a local &lt;code&gt;.env&lt;/code&gt; file. That's the gap the rest of this article is about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: a schema as the source of truth, not a description of one
&lt;/h2&gt;

&lt;p&gt;The actual fix isn't a better &lt;code&gt;.env.example&lt;/code&gt;. It's removing &lt;code&gt;.env.example&lt;/code&gt; from being anyone's source of truth at all, and replacing it with something that can be validated instead of just read.&lt;/p&gt;

&lt;p&gt;Concretely: a schema — a single, versioned, machine-readable file — declares what a project's configuration is supposed to be. Everything else (a local &lt;code&gt;.env&lt;/code&gt;, a generated template, a deployment manifest) gets checked against it. Nobody maintains the template by hand anymore, because it isn't a document anyone writes — it's an output.&lt;/p&gt;

&lt;p&gt;Here's what that looks like as &lt;code&gt;env.schema.toml&lt;/code&gt;, EnvShield's implementation of the idea — this article stays focused on the sync problem specifically; for the fuller walkthrough of the contract itself, see &lt;a href="https://www.envshield.dev/blog/env-end-of-chaos" rel="noopener noreferrer"&gt;The End of .env Chaos&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[DATABASE_URL]&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PostgreSQL connection string"&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"url"&lt;/span&gt;
&lt;span class="py"&gt;secret&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="nn"&gt;[PORT]&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Port the API listens on"&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"port"&lt;/span&gt;
&lt;span class="py"&gt;defaultValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"8000"&lt;/span&gt;

&lt;span class="nn"&gt;[LOG_LEVEL]&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Controls the application's log verbosity"&lt;/span&gt;
&lt;span class="py"&gt;defaultValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"info"&lt;/span&gt;

&lt;span class="nn"&gt;[STRIPE_SECRET_KEY]&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Stripe secret key -- only needed once payments are turned on"&lt;/span&gt;
&lt;span class="py"&gt;secret&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;requiredIf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;var&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PAYMENTS_ENABLED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;equals&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice &lt;code&gt;DATABASE_URL&lt;/code&gt; has neither a default nor a condition — that's what makes it required. There's no separate &lt;code&gt;required = true&lt;/code&gt; field to remember: a variable is required unconditionally unless a &lt;code&gt;defaultValue&lt;/code&gt; waives it or a &lt;code&gt;requiredIf&lt;/code&gt; makes it conditional on another variable's value. &lt;code&gt;type&lt;/code&gt; gives &lt;code&gt;check&lt;/code&gt; something to actually validate against (&lt;code&gt;PORT=banana&lt;/code&gt; fails, rather than surfacing three services downstream). &lt;code&gt;requiredIf&lt;/code&gt; is what a static example file structurally can't express — &lt;code&gt;STRIPE_SECRET_KEY&lt;/code&gt; is optional while &lt;code&gt;PAYMENTS_ENABLED=false&lt;/code&gt;, and becomes required the moment that flag flips, in any environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The schema is not the example file
&lt;/h2&gt;

&lt;p&gt;This is the distinction that matters most, because collapsing it back into "just a nicer &lt;code&gt;.env.example&lt;/code&gt;" undoes the whole point: &lt;strong&gt;&lt;code&gt;env.schema.toml&lt;/code&gt; is the contract. &lt;code&gt;.env.example&lt;/code&gt; is a rendering of it.&lt;/strong&gt;&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="nv"&gt;$ &lt;/span&gt;envshield schema &lt;span class="nb"&gt;sync

&lt;/span&gt;Generating .env.example from schema...
✓ Successfully created/updated .env.example!

Next step:
  Review the updated template, &lt;span class="k"&gt;then &lt;/span&gt;commit it.
  If it added a variable you need locally, run &lt;span class="s1"&gt;'envshield setup'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That command generates the actual file:&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="c"&gt;# This file was auto-generated by EnvShield on &amp;lt;generation timestamp&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;# It is generated from the contract defined in env.schema.toml.&lt;/span&gt;
&lt;span class="c"&gt;# DO NOT EDIT THIS FILE MANUALLY.&lt;/span&gt;

&lt;span class="c"&gt;# PostgreSQL connection string&lt;/span&gt;
&lt;span class="c"&gt;# secret&lt;/span&gt;
&lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="c"&gt;# Port the API listens on&lt;/span&gt;
&lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8000

&lt;span class="c"&gt;# Controls the application's log verbosity&lt;/span&gt;
&lt;span class="nv"&gt;LOG_LEVEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info

&lt;span class="c"&gt;# Stripe secret key -- only needed once payments are turned on&lt;/span&gt;
&lt;span class="c"&gt;# secret; required if PAYMENTS_ENABLED = "true"&lt;/span&gt;
&lt;span class="nv"&gt;STRIPE_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit that file by hand and the next &lt;code&gt;schema sync&lt;/code&gt; silently overwrites your change — which is correct, not a bug, because once a schema exists, &lt;code&gt;.env.example&lt;/code&gt; is generated output, not something anything else reads. (An existing &lt;code&gt;.env.example&lt;/code&gt; can still be one of the inputs &lt;code&gt;envshield init&lt;/code&gt; reads to seed a schema in the first place, on a project that doesn't have one yet — the "output, not source" rule is about what happens after the schema exists, not before.) The variable that actually stops drift is &lt;code&gt;--check&lt;/code&gt;:&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="nv"&gt;$ &lt;/span&gt;envshield schema &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="nt"&gt;--check&lt;/span&gt;
✓ &lt;span class="s1"&gt;'.env.example'&lt;/span&gt; is &lt;span class="k"&gt;in &lt;/span&gt;&lt;span class="nb"&gt;sync &lt;/span&gt;with schema.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a field to the schema without regenerating the template, and the same command tells you exactly what's missing instead of letting it slip through:&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="nv"&gt;$ &lt;/span&gt;envshield schema &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="nt"&gt;--check&lt;/span&gt;
✗ Missing from &lt;span class="s1"&gt;'.env.example'&lt;/span&gt;: NEW_FEATURE_FLAG &lt;span class="o"&gt;(&lt;/span&gt;run &lt;span class="s1"&gt;'envshield schema sync'&lt;/span&gt; to regenerate it&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire that into &lt;code&gt;envshield hook install&lt;/code&gt; and it runs automatically before a commit lands — but only for a schema you actually staged, so it never blocks a commit that didn't touch configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;secret = true&lt;/code&gt; actually does — and doesn't
&lt;/h2&gt;

&lt;p&gt;Worth being precise here, because it's the field people most often over-assume about. &lt;code&gt;secret = true&lt;/code&gt; tells EnvShield a field is sensitive. It does not, and structurally cannot, hold the value — the schema only ever describes the &lt;em&gt;shape&lt;/em&gt; of configuration, never real values. The actual key lives in your local, git-ignored &lt;code&gt;.env&lt;/code&gt;, nowhere else.&lt;/p&gt;

&lt;p&gt;What the flag buys you: &lt;code&gt;.env.example&lt;/code&gt; shows &lt;code&gt;# secret&lt;/code&gt; instead of a value, and any code EnvShield generates treats the field as sensitive too. That's the feature, in full. &lt;strong&gt;EnvShield doesn't store secret values, doesn't retrieve them from anywhere, and isn't where you'd go to rotate one.&lt;/strong&gt; If you need an actual vault, that's what 1Password, HashiCorp Vault, or AWS Secrets Manager are for — this is a contract describing a secret, not a place to keep one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validating that the contract actually holds
&lt;/h2&gt;

&lt;p&gt;Once the schema exists, &lt;code&gt;check&lt;/code&gt; is what enforces it against a real local file:&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="nv"&gt;$ &lt;/span&gt;envshield check

Validating .env against schema...
┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Status        ┃ Variable Name ┃ Source                             ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ Invalid Value │ PORT          │ must be a port number from 1-65535 │
└───────────────┴───────────────┴────────────────────────────────────┘

Suggestion: Run &lt;span class="s1"&gt;'envshield setup'&lt;/span&gt; to fill &lt;span class="k"&gt;in &lt;/span&gt;missing/blank values or fix invalid ones.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the check a text file can't do. A stale &lt;code&gt;.env.example&lt;/code&gt; never had an opinion on whether &lt;code&gt;PORT=banana&lt;/code&gt; is valid — it doesn't know what a port is. A schema does, because &lt;code&gt;type&lt;/code&gt; is a real constraint, not a comment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The next layer: what about variables nobody declared at all?
&lt;/h2&gt;

&lt;p&gt;Everything so far assumes someone remembered to add the variable to the schema in the first place. That's a real gap on its own — a schema you have to remember to update by hand has just moved the discipline problem one level up, not solved it. That's a distinct problem from the one this article is about, and EnvShield has a separate command for it (&lt;code&gt;envshield undeclared&lt;/code&gt;, which catches a variable your code just started reading that the schema doesn't know about yet) — worth its own explanation rather than a paragraph tacked onto this one.&lt;/p&gt;

&lt;h2&gt;
  
  
  When this approach makes sense — and when it doesn't
&lt;/h2&gt;

&lt;p&gt;A schema is overhead you don't need for a two-variable prototype script you'll delete next week, or a project with exactly one contributor who already holds the whole configuration in their head. In those cases, a plain &lt;code&gt;.env&lt;/code&gt; and a five-minute memory are genuinely fine — don't add a contract to solve a problem you don't have yet.&lt;/p&gt;

&lt;p&gt;It starts paying for itself once any of these are true: more than one person touches the project, configuration differs by environment (local vs. staging vs. production), or a deployment manifest needs to independently agree with the application about what it requires. That's also roughly where a hand-maintained &lt;code&gt;.env.example&lt;/code&gt; starts failing anyway — the timing isn't a coincidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical migration path
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;envshield init&lt;/code&gt;&lt;/strong&gt;, whether or not you already have real configuration. It builds &lt;code&gt;env.schema.toml&lt;/code&gt; from an existing &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;.env.example&lt;/code&gt;, or a Python config module when one of those exists — and falls back to a framework-aware template on a genuinely fresh project with none of that yet. Either way, it also writes &lt;code&gt;envshield.yml&lt;/code&gt;, registers a deployment manifest if it finds one, updates &lt;code&gt;.gitignore&lt;/code&gt;, and offers to install Git hooks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review the generated schema.&lt;/strong&gt; Mark real secrets with &lt;code&gt;secret = true&lt;/code&gt;, add &lt;code&gt;defaultValue&lt;/code&gt; for anything genuinely optional, and &lt;code&gt;requiredIf&lt;/code&gt; for anything conditional. This is the one manual step, and it only happens once per variable, not once per drift.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;envshield schema sync&lt;/code&gt;&lt;/strong&gt;, then commit both &lt;code&gt;env.schema.toml&lt;/code&gt; and the generated &lt;code&gt;.env.example&lt;/code&gt; together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;envshield check&lt;/code&gt;&lt;/strong&gt; against your own local file to confirm nothing's actually missing or malformed right now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;envshield hook install&lt;/code&gt;&lt;/strong&gt; so a schema change without a regenerated template gets caught before it's committed, not after someone else hits it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this requires believing configuration is glamorous. It requires believing that "what does this project actually need to run" shouldn't depend on someone remembering to update a text file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;envshield
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/rabbilyasar/envshield" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://docs.envshield.dev" rel="noopener noreferrer"&gt;Docs&lt;/a&gt; · &lt;a href="https://www.envshield.dev/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>devops</category>
      <category>configuration</category>
      <category>cli</category>
    </item>
    <item>
      <title>Want to simplify how you manage your env files and secrets?</title>
      <dc:creator>Rabbil Yasar Sajal </dc:creator>
      <pubDate>Wed, 24 Sep 2025 15:05:06 +0000</pubDate>
      <link>https://dev.to/rabbilyasar/want-to-simplify-how-you-manage-your-env-files-and-secrets-3gi4</link>
      <guid>https://dev.to/rabbilyasar/want-to-simplify-how-you-manage-your-env-files-and-secrets-3gi4</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/rabbilyasar/the-end-of-env-chaos-introducing-envshield-524p" class="crayons-story__hidden-navigation-link"&gt;The End of .env Chaos: How a Configuration Contract Fixes Environment Variables&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/rabbilyasar" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F638533%2F5b6a18b3-da82-494e-9ee4-097eeb78c232.png" alt="rabbilyasar profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/rabbilyasar" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Rabbil Yasar Sajal 
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Rabbil Yasar Sajal 
                
                
              
              &lt;div id="story-author-preview-content-2851603" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/rabbilyasar" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F638533%2F5b6a18b3-da82-494e-9ee4-097eeb78c232.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Rabbil Yasar Sajal &lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/rabbilyasar/the-end-of-env-chaos-introducing-envshield-524p" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 17 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/rabbilyasar/the-end-of-env-chaos-introducing-envshield-524p" id="article-link-2851603"&gt;
          The End of .env Chaos: How a Configuration Contract Fixes Environment Variables
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devops"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devops&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/python"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;python&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/configuration"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;configuration&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/rabbilyasar/the-end-of-env-chaos-introducing-envshield-524p" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;10&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/rabbilyasar/the-end-of-env-chaos-introducing-envshield-524p#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>security</category>
      <category>devops</category>
      <category>python</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Read about the how EnvShield is compared to TruffleHog</title>
      <dc:creator>Rabbil Yasar Sajal </dc:creator>
      <pubDate>Wed, 24 Sep 2025 15:02:54 +0000</pubDate>
      <link>https://dev.to/rabbilyasar/read-about-the-how-envshield-is-compared-to-trufflehog-3okl</link>
      <guid>https://dev.to/rabbilyasar/read-about-the-how-envshield-is-compared-to-trufflehog-3okl</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/rabbilyasar/envshield-vs-trufflehog-a-practical-guide-to-local-first-secret-scanning-3c1o" class="crayons-story__hidden-navigation-link"&gt;EnvShield vs. TruffleHog: Configuration Contracts vs. Secret Scanning&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/rabbilyasar" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F638533%2F5b6a18b3-da82-494e-9ee4-097eeb78c232.png" alt="rabbilyasar profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/rabbilyasar" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Rabbil Yasar Sajal 
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Rabbil Yasar Sajal 
                
                
              
              &lt;div id="story-author-preview-content-2861206" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/rabbilyasar" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F638533%2F5b6a18b3-da82-494e-9ee4-097eeb78c232.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Rabbil Yasar Sajal &lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/rabbilyasar/envshield-vs-trufflehog-a-practical-guide-to-local-first-secret-scanning-3c1o" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 22 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/rabbilyasar/envshield-vs-trufflehog-a-practical-guide-to-local-first-secret-scanning-3c1o" id="article-link-2861206"&gt;
          EnvShield vs. TruffleHog: Configuration Contracts vs. Secret Scanning
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devops"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devops&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cli"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cli&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/rabbilyasar/envshield-vs-trufflehog-a-practical-guide-to-local-first-secret-scanning-3c1o" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/rabbilyasar/envshield-vs-trufflehog-a-practical-guide-to-local-first-secret-scanning-3c1o#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>security</category>
      <category>devops</category>
      <category>cli</category>
      <category>productivity</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Rabbil Yasar Sajal </dc:creator>
      <pubDate>Wed, 24 Sep 2025 15:01:58 +0000</pubDate>
      <link>https://dev.to/rabbilyasar/-1gfl</link>
      <guid>https://dev.to/rabbilyasar/-1gfl</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/rabbilyasar/envshield-vs-trufflehog-a-practical-guide-to-local-first-secret-scanning-3c1o" class="crayons-story__hidden-navigation-link"&gt;EnvShield vs. TruffleHog: Configuration Contracts vs. Secret Scanning&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/rabbilyasar" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F638533%2F5b6a18b3-da82-494e-9ee4-097eeb78c232.png" alt="rabbilyasar profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/rabbilyasar" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Rabbil Yasar Sajal 
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Rabbil Yasar Sajal 
                
                
              
              &lt;div id="story-author-preview-content-2861206" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/rabbilyasar" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F638533%2F5b6a18b3-da82-494e-9ee4-097eeb78c232.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Rabbil Yasar Sajal &lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/rabbilyasar/envshield-vs-trufflehog-a-practical-guide-to-local-first-secret-scanning-3c1o" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 22 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/rabbilyasar/envshield-vs-trufflehog-a-practical-guide-to-local-first-secret-scanning-3c1o" id="article-link-2861206"&gt;
          EnvShield vs. TruffleHog: Configuration Contracts vs. Secret Scanning
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devops"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devops&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cli"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cli&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/rabbilyasar/envshield-vs-trufflehog-a-practical-guide-to-local-first-secret-scanning-3c1o" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/rabbilyasar/envshield-vs-trufflehog-a-practical-guide-to-local-first-secret-scanning-3c1o#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>security</category>
      <category>devops</category>
      <category>cli</category>
      <category>productivity</category>
    </item>
    <item>
      <title>EnvShield vs. TruffleHog: Configuration Contracts vs. Secret Scanning</title>
      <dc:creator>Rabbil Yasar Sajal </dc:creator>
      <pubDate>Mon, 22 Sep 2025 15:26:46 +0000</pubDate>
      <link>https://dev.to/rabbilyasar/envshield-vs-trufflehog-a-practical-guide-to-local-first-secret-scanning-3c1o</link>
      <guid>https://dev.to/rabbilyasar/envshield-vs-trufflehog-a-practical-guide-to-local-first-secret-scanning-3c1o</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EnvShield&lt;/strong&gt; is a configuration-contract engine (&lt;code&gt;env.schema.toml&lt;/code&gt;) — it validates configuration, discovers undeclared variables, reviews schema changes across git revisions, and validates deployment manifests. Secret scanning is one supporting check inside it, not the product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TruffleHog&lt;/strong&gt; is a dedicated secret-scanning engine — 800+ credential detectors, deep git-history scanning across many platforms, live verification against provider APIs.&lt;/li&gt;
&lt;li&gt;These solve different problems. Use EnvShield to keep your configuration contract enforced day-to-day; reach for TruffleHog when you need a deep, org-wide secret-history audit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/rabbilyasar/envshield" rel="noopener noreferrer"&gt;Install EnvShield →&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this comparison keeps coming up
&lt;/h2&gt;

&lt;p&gt;Both tools get mentioned in the same "how do I stop leaking secrets" conversations, so it's a fair question whether they compete. They don't, for a simple reason: &lt;strong&gt;they're built to answer different questions.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TruffleHog answers: &lt;em&gt;is there a real, exposed credential anywhere in this codebase or its history?&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;EnvShield answers: &lt;em&gt;does this project's configuration actually match what it declares it needs?&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Secret exposure is one specific way a project's configuration can go wrong. It's not the only way, and it's not EnvShield's reason for existing — &lt;code&gt;envshield scan&lt;/code&gt; catches hardcoded secrets and undeclared variables as a supporting check alongside the configuration contract, not the product itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What each one actually does
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;EnvShield&lt;/th&gt;
&lt;th&gt;TruffleHog&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary problem&lt;/td&gt;
&lt;td&gt;Configuration contract — does your config match what the project declares it needs&lt;/td&gt;
&lt;td&gt;Secret detection — find exposed, &lt;em&gt;active&lt;/em&gt; credentials in code and history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema/contract (&lt;code&gt;env.schema.toml&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Yes — this is the core artifact&lt;/td&gt;
&lt;td&gt;No concept of a configuration schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local configuration validation&lt;/td&gt;
&lt;td&gt;Yes (&lt;code&gt;check&lt;/code&gt;, &lt;code&gt;doctor&lt;/code&gt;, &lt;code&gt;setup&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Undeclared env-var detection&lt;/td&gt;
&lt;td&gt;Yes (&lt;code&gt;undeclared&lt;/code&gt;, &lt;code&gt;scan&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema diff across git revisions&lt;/td&gt;
&lt;td&gt;Yes (&lt;code&gt;schema diff&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment manifest validation (Compose/K8s)&lt;/td&gt;
&lt;td&gt;Yes (&lt;code&gt;check&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secret scanning (pattern-based)&lt;/td&gt;
&lt;td&gt;Yes — supporting check (&lt;code&gt;scan&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Yes — this is the core product&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git/history scanning&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes — full git history, across GitHub, GitLab, Slack, and more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live credential verification&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes — checks whether a found credential is still active against the provider's real API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;TruffleHog's own numbers put this in perspective: 800+ credential detectors, most supporting active verification against the provider's API. That's a different scale of investment in secret detection specifically than &lt;code&gt;scan&lt;/code&gt; is trying to be — EnvShield isn't competing with it there, and doesn't need to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where they genuinely overlap
&lt;/h2&gt;

&lt;p&gt;Both can flag a hardcoded secret sitting in your current working tree. That's the actual overlap — one row in the table above. Everything else is either EnvShield-only (the whole configuration-contract side) or TruffleHog-only (history scanning, live verification, detector breadth).&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;TruffleHog&lt;/strong&gt; — scan a repo's full history for verified, active secrets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;trufflehog git https://github.com/org/repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;EnvShield&lt;/strong&gt; — validate configuration against the contract, and check for undeclared reads in the current tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;envshield check
envshield scan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neither command is a substitute for the other's actual job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using both
&lt;/h2&gt;

&lt;p&gt;If you want deep, verified secret-history auditing, TruffleHog is built for exactly that, and &lt;code&gt;scan&lt;/code&gt; doesn't try to replicate it. If you want your configuration itself — types, requiredness, what's declared versus what code actually reads, what changed in a PR — to be a real, checkable contract instead of a folder of assumptions, that's what EnvShield is for. Running both isn't a "layered security" strategy so much as just using two tools for two different jobs, the same way a linter and a type checker don't compete for the same role.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Use EnvShield to keep your configuration contract enforced day-to-day. Reach for TruffleHog when you specifically need deep, org-wide secret-history auditing. Neither replaces the other, and neither is trying to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;envshield
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/rabbilyasar/envshield" rel="noopener noreferrer"&gt;EnvShield on GitHub&lt;/a&gt; · &lt;a href="https://www.envshield.dev/" rel="noopener noreferrer"&gt;Website&lt;/a&gt; · &lt;a href="https://docs.envshield.dev" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>devops</category>
      <category>security</category>
      <category>cli</category>
    </item>
    <item>
      <title>The End of .env Chaos: How a Configuration Contract Fixes Environment Variables</title>
      <dc:creator>Rabbil Yasar Sajal </dc:creator>
      <pubDate>Wed, 17 Sep 2025 14:27:21 +0000</pubDate>
      <link>https://dev.to/rabbilyasar/the-end-of-env-chaos-introducing-envshield-524p</link>
      <guid>https://dev.to/rabbilyasar/the-end-of-env-chaos-introducing-envshield-524p</guid>
      <description>&lt;p&gt;Let's be honest: &lt;code&gt;.env.example&lt;/code&gt; files have a reputation problem. Everyone knows they're important and nobody quite trusts them. Mine's usually out of date within a month — missing whatever variable got added in a PR nobody reviewed for configuration changes.&lt;/p&gt;

&lt;p&gt;But here's a distinction worth making up front, because getting it wrong is exactly why &lt;code&gt;.env.example&lt;/code&gt; stays broken: &lt;strong&gt;&lt;code&gt;.env&lt;/code&gt; itself isn't the problem.&lt;/strong&gt; A key-value file is a perfectly fine way to hold configuration at runtime. The actual problem is that nothing declares, in one place, what a project's configuration is &lt;em&gt;supposed&lt;/em&gt; to look like. Requirements are implicit — they live in application code, in a deployment manifest, in a &lt;code&gt;.env.example&lt;/code&gt; someone wrote by hand once, and in a senior developer's memory of "oh, you also need &lt;code&gt;STRIPE_SECRET_KEY&lt;/code&gt; if payments are on." None of those four places are required to agree with each other. They usually do, right up until they don't.&lt;/p&gt;

&lt;p&gt;That's what a &lt;strong&gt;configuration contract&lt;/strong&gt; actually fixes — not "cleaner env files," but one real, versioned, checkable description of what a project needs, that everything else (code, deployment manifests, a new teammate's local setup) gets validated against instead of just trusted.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;env.schema.toml&lt;/code&gt;: the contract, not a description of one
&lt;/h2&gt;

&lt;p&gt;EnvShield's answer is a file named &lt;code&gt;env.schema.toml&lt;/code&gt;, committed to git, next to the code it configures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[DATABASE_URL]&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PostgreSQL connection string"&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"url"&lt;/span&gt;
&lt;span class="py"&gt;secret&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="nn"&gt;[PORT]&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Port the API listens on"&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"port"&lt;/span&gt;
&lt;span class="py"&gt;defaultValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"8000"&lt;/span&gt;

&lt;span class="nn"&gt;[LOG_LEVEL]&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Controls the application's log verbosity"&lt;/span&gt;
&lt;span class="py"&gt;defaultValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"info"&lt;/span&gt;

&lt;span class="nn"&gt;[STRIPE_SECRET_KEY]&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Stripe secret key -- only needed once payments are turned on"&lt;/span&gt;
&lt;span class="py"&gt;secret&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;requiredIf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;var&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PAYMENTS_ENABLED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;equals&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five fields doing five different jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;type&lt;/code&gt;&lt;/strong&gt; — the value's shape (&lt;code&gt;string&lt;/code&gt;, &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;port&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;). &lt;code&gt;check&lt;/code&gt;, &lt;code&gt;setup&lt;/code&gt;, and &lt;code&gt;doctor&lt;/code&gt; all enforce it — &lt;code&gt;PORT=banana&lt;/code&gt; fails validation, not at runtime three services downstream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;defaultValue&lt;/code&gt;&lt;/strong&gt; — what &lt;code&gt;setup&lt;/code&gt; writes automatically if nobody provides one. The variable still has to be present in the local file; the default just means &lt;code&gt;check&lt;/code&gt; stops complaining once it is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;description&lt;/code&gt;&lt;/strong&gt; — shown in &lt;code&gt;setup&lt;/code&gt;'s prompt and copied into any generated config code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;secret&lt;/code&gt;&lt;/strong&gt; — marks a field sensitive. More on exactly what that does below, because it's easy to over-assume.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;requiredIf&lt;/code&gt;&lt;/strong&gt; — makes a field required only when another variable's value says it should be. &lt;code&gt;STRIPE_SECRET_KEY&lt;/code&gt; above is optional while &lt;code&gt;PAYMENTS_ENABLED=false&lt;/code&gt;, and becomes required the moment that flag flips, in any environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice &lt;code&gt;DATABASE_URL&lt;/code&gt; has neither a default nor a condition — that's what makes it required. There's no separate &lt;code&gt;required = true&lt;/code&gt; field to remember to set: a variable is required unconditionally unless a &lt;code&gt;defaultValue&lt;/code&gt; waives it or a &lt;code&gt;requiredIf&lt;/code&gt; makes it conditional.&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;secret = true&lt;/code&gt; actually does (and doesn't)
&lt;/h2&gt;

&lt;p&gt;Worth being precise here, since it's easy to read past: &lt;code&gt;secret = true&lt;/code&gt; tells EnvShield this field is sensitive. It does not, and structurally cannot, hold the value itself — the schema only ever describes the &lt;em&gt;shape&lt;/em&gt; of your configuration, never the actual values. The real key lives in your local, git-ignored &lt;code&gt;.env&lt;/code&gt;, nowhere else.&lt;/p&gt;

&lt;p&gt;What the flag buys you: &lt;code&gt;setup&lt;/code&gt; masks your keystrokes when prompting for it, &lt;code&gt;import&lt;/code&gt; never guesses a real value into the committed schema, and any code EnvShield generates treats the field as sensitive too. That's the whole feature. &lt;strong&gt;EnvShield doesn't store secret values, doesn't retrieve them from anywhere, and isn't where you'd go to rotate one.&lt;/strong&gt; If you need an actual secrets vault, that's what 1Password, HashiCorp Vault, or AWS Secrets Manager are for. EnvShield's job is making sure the &lt;em&gt;contract&lt;/em&gt; around a secret is honest, not holding the secret itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;env.schema.toml&lt;/code&gt; vs. &lt;code&gt;.env.example&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is the distinction the rest of this article depends on: &lt;strong&gt;&lt;code&gt;.env.example&lt;/code&gt; is a representation of the contract. &lt;code&gt;env.schema.toml&lt;/code&gt; is the contract.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;envshield schema &lt;span class="nb"&gt;sync&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;generates &lt;code&gt;.env.example&lt;/code&gt; straight from the schema — documentation for a human skimming the repo, not something EnvShield itself reads back. Edit it by hand, and the next &lt;code&gt;schema sync&lt;/code&gt; overwrites your change, because it's an output, not a source. Every command that actually validates or enforces anything — &lt;code&gt;check&lt;/code&gt;, &lt;code&gt;setup&lt;/code&gt;, &lt;code&gt;doctor&lt;/code&gt;, &lt;code&gt;undeclared&lt;/code&gt;, &lt;code&gt;schema diff&lt;/code&gt;, &lt;code&gt;generate&lt;/code&gt; — reads &lt;code&gt;env.schema.toml&lt;/code&gt; directly. If you've ever watched a &lt;code&gt;.env.example&lt;/code&gt; drift from reality within a month, this is why: it never had a mechanism to stay honest on its own. &lt;code&gt;envshield hook install&lt;/code&gt; wires a pre-commit check (&lt;code&gt;schema sync --check&lt;/code&gt;) that catches a schema edited without regenerating its template — but that's something you opt into, not something that happens automatically just because a schema exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  One contract, several workflows
&lt;/h2&gt;

&lt;p&gt;Every command below reads the same file — none of them invent their own idea of what your configuration is supposed to be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="err"&gt;env.schema.toml&lt;/span&gt;
     &lt;span class="err"&gt;↓&lt;/span&gt;
&lt;span class="err"&gt;validation&lt;/span&gt; &lt;span class="err"&gt;·&lt;/span&gt; &lt;span class="err"&gt;discovery&lt;/span&gt; &lt;span class="err"&gt;·&lt;/span&gt; &lt;span class="err"&gt;documentation&lt;/span&gt; &lt;span class="err"&gt;·&lt;/span&gt; &lt;span class="err"&gt;onboarding&lt;/span&gt; &lt;span class="err"&gt;·&lt;/span&gt; &lt;span class="err"&gt;review&lt;/span&gt; &lt;span class="err"&gt;·&lt;/span&gt; &lt;span class="err"&gt;deployment&lt;/span&gt; &lt;span class="err"&gt;checks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;envshield init&lt;/code&gt;&lt;/strong&gt; — builds the schema from your real, existing config (a &lt;code&gt;.env&lt;/code&gt;, a Python config module, or a deployment manifest) if one exists, or a framework-aware template if it doesn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;envshield setup&lt;/code&gt;&lt;/strong&gt; — interactive onboarding: prompts for whatever's missing, masks secret input, writes a local file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;envshield check&lt;/code&gt;&lt;/strong&gt; — validates a local file, &lt;em&gt;or&lt;/em&gt; a Docker Compose or Kubernetes manifest, against the schema.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;envshield schema sync&lt;/code&gt;&lt;/strong&gt; — regenerates &lt;code&gt;.env.example&lt;/code&gt; from the schema.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;envshield undeclared&lt;/code&gt;&lt;/strong&gt; — catches a variable your code just started reading that the schema doesn't know about yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;envshield schema diff&lt;/code&gt;&lt;/strong&gt; — compares the contract itself across two git revisions, classified by impact (breaking, security-sensitive, informational).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A concrete example: catching a variable before it's committed
&lt;/h2&gt;

&lt;p&gt;Say this line shows up in &lt;code&gt;app/main.py&lt;/code&gt;:&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="n"&gt;analytics_key&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ANALYTICS_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nobody's added &lt;code&gt;ANALYTICS_KEY&lt;/code&gt; to &lt;code&gt;env.schema.toml&lt;/code&gt;. It hasn't even been committed yet. Running:&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="nv"&gt;$ &lt;/span&gt;envshield undeclared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────┬─────────────┬──────┬───────────┬──────────────────────┐
│ Variable       │ File        │ Line │ Access    │ Contract status      │
├────────────────┼─────────────┼──────┼───────────┼──────────────────────┤
│ ANALYTICS_KEY  │ app/main.py │ 42   │ os.getenv │ missing declaration  │
└────────────────┴─────────────┴──────┴───────────┴──────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, &lt;code&gt;undeclared&lt;/code&gt; compares your working tree against &lt;code&gt;HEAD&lt;/code&gt; — including uncommitted and untracked files — so this is caught before &lt;code&gt;git add&lt;/code&gt;, not after a teammate hits it in staging.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;scan&lt;/code&gt; does something related but broader: it inventories every currently-undeclared read across the &lt;em&gt;whole&lt;/em&gt; codebase in one pass, alongside hardcoded-secret detection — useful for a first audit of an existing project. &lt;code&gt;undeclared&lt;/code&gt;'s job is narrower and more precise: what's &lt;em&gt;new&lt;/em&gt; since a given revision, which is what makes the example above possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment manifests get checked too
&lt;/h2&gt;

&lt;p&gt;The same schema also validates the manifest that actually deploys a service, not just a local file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;envshield check docker-compose.yml
envshield check k8s/deployment.yaml &lt;span class="nt"&gt;--container&lt;/span&gt; api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker Compose's &lt;code&gt;${VAR}&lt;/code&gt;/&lt;code&gt;${VAR:-default}&lt;/code&gt; interpolation is resolved; Kubernetes Deployments, StatefulSets, DaemonSets, Jobs, CronJobs, and bare Pods are supported, including multi-document manifests. A handful of narrower cases aren't handled yet — the no-colon &lt;code&gt;${VAR-default}&lt;/code&gt; Compose form, Kubernetes &lt;code&gt;secretKeyRef&lt;/code&gt;/&lt;code&gt;configMapKeyRef&lt;/code&gt; matched by key rather than env-var name, &lt;code&gt;envFrom.prefix&lt;/code&gt;. None of them produce a false-clean on a genuinely missing required variable; they're just not resolved yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;None of this requires believing configuration is glamorous. It requires believing that "what does this project actually need to run" shouldn't be an oral tradition. A schema doesn't add a sixth place to keep configuration description in sync by hand — it gives the other five (code, &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;.env.example&lt;/code&gt;, the deployment manifest, and whatever a teammate remembers) something to actually be checked against.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;envshield
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/rabbilyasar/envshield" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://docs.envshield.dev" rel="noopener noreferrer"&gt;Docs&lt;/a&gt; · &lt;a href="https://www.envshield.dev/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>devops</category>
      <category>python</category>
      <category>configuration</category>
    </item>
    <item>
      <title>Django: Testing The What, Why and How. (Code)</title>
      <dc:creator>Rabbil Yasar Sajal </dc:creator>
      <pubDate>Mon, 05 Jul 2021 11:49:03 +0000</pubDate>
      <link>https://dev.to/rabbilyasar/django-testing-the-what-why-and-how-3b8l</link>
      <guid>https://dev.to/rabbilyasar/django-testing-the-what-why-and-how-3b8l</guid>
      <description>&lt;p&gt;This is a continuation of two part series, please check out &lt;a href="https://dev.to/nerdy_picasso/django-testing-the-what-why-and-how-1d4c"&gt;part-1&lt;/a&gt; to look at the theoretical part of testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Coverage&lt;/li&gt;
&lt;li&gt;Testing Models&lt;/li&gt;
&lt;li&gt;Testing Views&lt;/li&gt;
&lt;li&gt;Testing Forms&lt;/li&gt;
&lt;li&gt;Next Topics To Cover&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this blog we will be looking at how to start with testing and later on how to implement.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://dev.to/nerdy_picasso/django-testing-the-what-why-and-how-1d4c#our-testing-project"&gt;Our project setup&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Coverage
&lt;/h2&gt;

&lt;p&gt;First we need to know what needs to be tested. As I said before, there is &lt;strong&gt;no need to test Django' vanilla model or Django ORM and python's in-built functions&lt;/strong&gt;.&lt;br&gt;
This is where coverage comes in handy, it is a convenient way for a beginner to find out the functions we need to test.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://dev.to/nerdy_picasso/django-testing-the-what-why-and-how-1d4c#coverage"&gt;How to setup coverage&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing Models
&lt;/h2&gt;

&lt;p&gt;let's open up &lt;code&gt;index.html&lt;/code&gt; in our newly created &lt;code&gt;htmlscov&lt;/code&gt; folder in our project:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;book-library &amp;gt; htmlscov &amp;gt; index.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;We will see something like this:&lt;br&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%2Fogwljj1m6fohry4j6msx.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%2Fogwljj1m6fohry4j6msx.png" alt="Screenshot_2021-06-28_13-08-04" width="800" height="642"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's go to our &lt;code&gt;catalog/models.py&lt;/code&gt;. &lt;br&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%2F9pde8ymjbujgud019srz.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%2F9pde8ymjbujgud019srz.png" alt="Screenshot_2021-06-28_13-13-12" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The red line shows the line we need to test and the percentage represents how much code has been covered with testing.&lt;/p&gt;

&lt;p&gt;Considering our &lt;code&gt;Book&lt;/code&gt; model:&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;class&lt;/span&gt; &lt;span class="nc"&gt;Book&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Model representing a book
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;title&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="nc"&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;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;author&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="nc"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Author&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_delete&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;SET_NULL&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;summary&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="nc"&gt;TextField&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;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Enter a brief description of the book&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;isbn&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="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ISBN&lt;/span&gt;&lt;span class="sh"&gt;'&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;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&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;help_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;13 Character &amp;lt;a href=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.isbn-international.org/content/what-isbn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;ISBN number&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;genre&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="nc"&gt;ManyToManyField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Genre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Select a genre for this book&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;related_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;books&lt;/span&gt;&lt;span class="sh"&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;__str__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_absolute_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Returns the url to access a detail record for this book.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;book-detail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test Model Implementation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestModels&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="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setUpTestData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;thriller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Genre&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="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;thriller&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;scifi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Genre&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="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;scifi&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&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="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test summary&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;isbn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test isbn&lt;/span&gt;&lt;span class="sh"&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;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;thriller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scifi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&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_book_has_genre&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;checks for genres in book
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&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="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_book_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Checks str for book
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&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="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test title&lt;/span&gt;&lt;span class="sh"&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_book_absolute_url_with_200&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&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="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_absolute_url&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/catalog/book/1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_absolute_url&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing str
&lt;/h3&gt;

&lt;p&gt;First we setup our data and then we test for the &lt;code&gt;str&lt;/code&gt; representation.&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;test_book_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Checks str for book
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&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="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing to note is the test method name should always start with &lt;code&gt;test_&lt;/code&gt; and it should be precise so that one can understand what the test is about. However, in cases where you feel that the naming may not be precise or meaningful make sure to use docstring to explain what it is supposed to do. &lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Many To Many
&lt;/h3&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;test_book_has_genre&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;checks for genres in book
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&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="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We assigned two genres for our book when we were creating our book. We are simply checking for the count of genres in the book, which tells us that the genres have been assigned successfully.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Absolute Url
&lt;/h3&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;test_book_absolute_url_with_200&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&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="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_absolute_url&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/catalog/book/1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_absolute_url&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our &lt;code&gt;Book&lt;/code&gt; model we have a method called &lt;code&gt;get_absolute_url&lt;/code&gt; which gets the information of a single book. We check if the status code is 200 and the &lt;code&gt;get_absolute_url&lt;/code&gt; method actually is the correct url.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Views
&lt;/h2&gt;

&lt;p&gt;To validate our views we will make use of &lt;a href="https://dev.to/nerdy_picasso/django-testing-the-what-why-and-how-1d4c#client"&gt;Django's test client&lt;/a&gt;.&lt;br&gt;
&lt;strong&gt;&lt;em&gt;Note: This is not strictly "unit test". It is actually quite difficult to write independent unit test for Django view code.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can check for multiple things in our views but these are some common ones.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check if only the logged in user can authorize your view (If user needs to be logged in to access).&lt;/li&gt;
&lt;li&gt;Check if the correct user can authorize your view.&lt;/li&gt;
&lt;li&gt;Check if the both &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt; is working the way it should.&lt;/li&gt;
&lt;li&gt;Check for the validation you may have implemented.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will refer to our &lt;code&gt;index&lt;/code&gt; view for our testing.&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="n"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BookInstance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Genre&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.contrib.auth.decorators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;login_required&lt;/span&gt;

&lt;span class="nd"&gt;@login_required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;login_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/login/&lt;/span&gt;&lt;span class="sh"&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;index&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;View function for home page of site.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="c1"&gt;# Generate counts of some of the main objects
&lt;/span&gt;    &lt;span class="n"&gt;num_books&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Book&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="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;num_instances&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BookInstance&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="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Available books (status = 'a')
&lt;/span&gt;    &lt;span class="n"&gt;num_instances_available&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BookInstance&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="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status__exact&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;num_authors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Author&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="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Number of visits to this view, as counted in the session variable.
&lt;/span&gt;    &lt;span class="n"&gt;num_visits&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;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_visits&lt;/span&gt;&lt;span class="sh"&gt;'&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="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_visits&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;num_visits&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_books&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;num_books&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_instances&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;num_instances&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_instances_available&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;num_instances_available&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_authors&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;num_authors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_visits&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;num_visits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Render the HTML template index.html with the data in the context variable
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test View Implementation
&lt;/h3&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="n"&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;catalog.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Author&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&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="n"&gt;django.contrib.auth.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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;model_bakery&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;baker&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IndexViewTest&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="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setUpTestData&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&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="nf"&gt;create_user&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_user&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;baker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;catalog.Book&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_quantity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&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_view_deny_anonymous&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test the view for unauthenticated user if unauthenticated will redirect to login page
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/catalog/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertRedirects&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/login/?next=/catalog/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/catalog/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertRedirects&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/login/?next=/catalog/&lt;/span&gt;&lt;span class="sh"&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_view_url_accesible_by_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test view is accesible by the reverse method
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&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="nf"&gt;login&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_user&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="mi"&gt;200&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_view_uses_correct_template_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test view is using correct template
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&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="nf"&gt;login&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_user&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertTemplateUsed&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&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_view_has_context_num_books&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&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="nf"&gt;login&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_user&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_books&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&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;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_books&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deny Unauthorized User
&lt;/h3&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;test_view_deny_anonymous&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test the view for unauthenticated user if unauthenticated will redirect to login page
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/catalog/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertRedirects&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/login/?next=/catalog/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/catalog/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertRedirects&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/login/?next=/catalog/&lt;/span&gt;&lt;span class="sh"&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 are checking if the unauthenticated user is being denied and redirected to the login page.&lt;/p&gt;

&lt;h3&gt;
  
  
  URL Is Accessed By Name
&lt;/h3&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;test_view_url_accessible_by_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test view is accessible by the reverse method
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&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="nf"&gt;login&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_user&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We check if the &lt;code&gt;reverse&lt;/code&gt; method is working and gets us a status code &lt;code&gt;200&lt;/code&gt; for both &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt; method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Correct Template Usage
&lt;/h3&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;test_view_uses_correct_template_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test view is using correct template
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&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="nf"&gt;login&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_user&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertTemplateUsed&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&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 checks if the correct template is being used by using &lt;code&gt;assertTemplateUsed&lt;/code&gt; method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test &lt;code&gt;num_books&lt;/code&gt;
&lt;/h3&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;test_view_has_context_num_books&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&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="nf"&gt;login&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_user&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test_password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_books&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&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;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;num_books&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;10&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 method we test if the number of books are assigned correctly. We use &lt;a href="https://model-bakery.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;model-bakery&lt;/a&gt; here to create 10 &lt;code&gt;Book&lt;/code&gt; instances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Forms
&lt;/h2&gt;

&lt;p&gt;Forms are something that can be easily unit tested. We can test for the validation in forms. They accept dictionary of values, validate it and return either errors or cleaned data.&lt;br&gt;
In our project we do not have a form, so let's make one for this tutorial's sake.&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="n"&gt;django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;forms&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Book&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddBookForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelForm&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;Book&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&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;clean_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cleaned_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;title&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;title&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;title&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="nf"&gt;isupper&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Should start with an uppercase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Should not end with a full stop&lt;/span&gt;&lt;span class="sh"&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;title&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We included some validation in our model which we will be testing.&lt;/p&gt;

&lt;p&gt;Our testing would look something like this.&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="n"&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;catalog.forms&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AddBookForm&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddBookFormTests&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_title_starting_lowercase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AddBookForm&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a lowercase title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Should start with an uppercase&lt;/span&gt;&lt;span class="sh"&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;def&lt;/span&gt; &lt;span class="nf"&gt;test_title_ending_full_stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AddBookForm&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A stopped title.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Should not end with a full stop&lt;/span&gt;&lt;span class="sh"&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;def&lt;/span&gt; &lt;span class="nf"&gt;test_title_with_ampersand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AddBookForm&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This field is required.&lt;/span&gt;&lt;span class="sh"&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;As we can see in forms we mainly test for validations. If we want to add some sort of integration tests alongside unit test we could check the validation by using using client doing something like this:&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;test_title_starting_lowercase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/books/add/&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a lowercase title&lt;/span&gt;&lt;span class="sh"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertContains&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Should start with an uppercase letter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;html&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Topics To Cover
&lt;/h2&gt;

&lt;p&gt;I have tried to cover as much as possible in this series but there are tons of things out there when it comes to testing. I will try to cover these topics in my upcoming blogs. A short list includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Continuous Integration&lt;/strong&gt;: Automatically run all tests whenever there a new commit is made. It can be done using GitHub Actions, Travis CI, Circle CI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mock&lt;/strong&gt;: It is an act of replacing part of the application you are testing with a dummy version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pytest&lt;/strong&gt;: It is a testing framework just like &lt;code&gt;unittest&lt;/code&gt;. We can also use this instead of &lt;code&gt;unittest&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Model Bakery&lt;/strong&gt;: We saw a small implementation of this in our project but there is more to discuss.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TDD&lt;/strong&gt;: Test Driven Development, this is actually quite an advance topic which takes a while to get used to, but once you start doing and understand the benefits, you will never want to go back.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;To conclude, there is not any concrete way to write your tests. It depends on the project you are working on or the company you are working at. Everyone can have their own way of testing their codes.&lt;/p&gt;

&lt;p&gt;The final advice I can give you is, do not be scared of writing tests. Yes, it can be intimidating at first but once you start writing tests you will understand your codes better, it will stimulate to question yourself to think about all the cases where your program can go wrong making you a better programmer.&lt;/p&gt;

&lt;p&gt;So this is it for today, if you have any question regarding this please do mention it in the comment or if you have any other questions please do not hesitate to contact me at the following platforms I am available on &lt;a href="https://twitter.com/rabbilyasar" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;, &lt;a href="https://github.com/rabbilyasar" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/rabbilyasar/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

</description>
      <category>django</category>
      <category>python</category>
      <category>testing</category>
      <category>unittest</category>
    </item>
    <item>
      <title>Django: Testing The What, Why and How (Theory).</title>
      <dc:creator>Rabbil Yasar Sajal </dc:creator>
      <pubDate>Fri, 18 Jun 2021 14:17:50 +0000</pubDate>
      <link>https://dev.to/rabbilyasar/django-testing-the-what-why-and-how-1d4c</link>
      <guid>https://dev.to/rabbilyasar/django-testing-the-what-why-and-how-1d4c</guid>
      <description>&lt;p&gt;A website cannot be perfect as it is nearly impossible for a website to run perfectly the first time without errors. As the project gets large it becomes hard for all the developers to keep track of all the changes; one single change in an existing component can make the whole project to break. This is why it is very essential to test every single functionality. &lt;/p&gt;

&lt;h2&gt;
  
  
  Table Of Content
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Types Of Testing&lt;/li&gt;
&lt;li&gt;What Should Be Tested&lt;/li&gt;
&lt;li&gt;Our Testing Project&lt;/li&gt;
&lt;li&gt;Structure&lt;/li&gt;
&lt;li&gt;
Third Party Packages

&lt;ul&gt;
&lt;li&gt;coverage&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Django Package

&lt;ul&gt;
&lt;li&gt;
client &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Testing

&lt;ul&gt;
&lt;li&gt;TestCase&lt;/li&gt;
&lt;li&gt;How To Run A Test&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Next &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Types Of Testing
&lt;/h2&gt;

&lt;p&gt;Unit and Integration testing are two main types of testing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Unit Tests&lt;/em&gt; are isolated tests responsible for only one functionality. &lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Integration Tests&lt;/em&gt; are aimed at mimicking the user behavior, usually combined with multiple functions to make sure they behave correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As Unit Tests are small they should be written all the time. These tests are easy to debug. The more you write these, the lesser you will likely to write integration testing. However, sometimes it is essential to run integration testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Should Be Tested
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If something can break, it should be tested. These includes models, views, forms, templates, validators and so on.&lt;/li&gt;
&lt;li&gt;Each test should be focused on testing one functionality.&lt;/li&gt;
&lt;li&gt;It should run whenever a code is being PUSHed or PULLed from a repo in the staging environment, before PUSHing to production.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That being said if the code in question is a builtin django or python function/library, don't test it. For example datetime or model fields. The only time you test something if the function or code is written by you. &lt;/p&gt;

&lt;h2&gt;
  
  
  Our Testing Project
&lt;/h2&gt;

&lt;p&gt;In this tutorial we will be using a very basic project &lt;a href="https://github.com/rabbilyasar/book-library" rel="noopener noreferrer"&gt;book-library&lt;/a&gt;.&lt;br&gt;
To set up the project, first we will need to clone the repo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/rabbilyasar/book-library.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install virtualenv if not installed already&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install virtualenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;activate our virtualenv&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source env/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;install the packages in our &lt;em&gt;requirements.txt&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;asgiref==3.3.4
autopep8==1.5.7
coverage==5.5
Django==3.2.4
mccabe==0.6.1
pycodestyle==2.7.0
pytz==2021.1
sqlparse==0.4.1
toml==0.10.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Migrate our database&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 migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Structure
&lt;/h2&gt;

&lt;p&gt;When you create a django app it includes a &lt;em&gt;test.py&lt;/em&gt; file that you can use to write your tests, however, I prefer to create a module called &lt;em&gt;tests&lt;/em&gt; and have separate files for our models, forms, views.&lt;br&gt;
&lt;strong&gt;&lt;em&gt;Note: If the project gets big it gets hard to maintain in this structure. Another way of doing would be to have a folder for models, views and forms and create file for each view or model.&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
We would be following the following structure for our project.&lt;/p&gt;

&lt;pre&gt;└── app_name
    └── tests
        ├── __init__.py
        ├── test_forms.py
        ├── test_models.py
        └── test_views.py
&lt;/pre&gt;

&lt;p&gt;By default django's &lt;code&gt;unittest&lt;/code&gt; module uses its &lt;a href="https://docs.python.org/3/library/unittest.html#unittest-test-discovery" rel="noopener noreferrer"&gt;builtin-test-discovery&lt;/a&gt;. It will discover tests under the current directory with filename pattern &lt;strong&gt;test*.py&lt;/strong&gt;. For our current structure to work we will have to delete our existing &lt;em&gt;tests.py&lt;/em&gt; file which has been provided by our django app.&lt;/p&gt;
&lt;h2&gt;
  
  
  Third Party Packages
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://coverage.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;coverage&lt;/a&gt;: It can be used to have  a rough overview of a project's total test coverage. It is a remarkable tool for any beginner as it can give you suggestion on what needs to be tested. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pypi.org/project/mock/" rel="noopener noreferrer"&gt;mock&lt;/a&gt;: This is a utilitarian tool for isolating part of your code that are not critical to the test you are writing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://model-bakery.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;model-bakery&lt;/a&gt;: This is a fine tool for creating fixtures for testing. We will go through some usage and examples in the next blog.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Coverage
&lt;/h3&gt;

&lt;p&gt;Let's a take a look at coverage and see how we can use it.&lt;br&gt;
To run coverage inside a project:&lt;br&gt;
Install coverage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install coverage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After every time you add some code to your application run this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coverage run --omit='*/venv/*' manage.py test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to see more details you can add -v flag. In this case it is using verbosity level 2.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coverage run manage.py test -v 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running it once we can can get a coverage report using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coverage report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also generate an HTML report in a new folder called &lt;em&gt;htmlcov&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coverage html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Django Package
&lt;/h2&gt;

&lt;p&gt;Django's default framework for testing is Python's standard library module &lt;a href="https://docs.python.org/3/library/unittest.html#module-unittest" rel="noopener noreferrer"&gt;unittest&lt;/a&gt;. Despite the name this module can be used for both unit test and integration test. There are other modules like &lt;a href="https://docs.pytest.org/en/6.2.x/" rel="noopener noreferrer"&gt;pytest&lt;/a&gt;, which has it's own benefits and drawbacks on which we will focus later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client
&lt;/h3&gt;

&lt;p&gt;Django's test client is an amazing tool which can be used for integration testing. It can simulate &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt; requests on a &lt;code&gt;URL&lt;/code&gt; and one can observe the response. &lt;a href="https://docs.djangoproject.com/en/3.0/topics/testing/tools/#the-test-client" rel="noopener noreferrer"&gt;Test Client&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  TestCase
&lt;/h3&gt;

&lt;p&gt;Django provides us with few base classes (&lt;a href="https://docs.djangoproject.com/en/3.1/topics/testing/tools/#simpletestcase" rel="noopener noreferrer"&gt;SimpleTestCase&lt;/a&gt;, &lt;a href="https://docs.djangoproject.com/en/3.1/topics/testing/tools/#liveservertestcase" rel="noopener noreferrer"&gt;LiveServerTestCase&lt;/a&gt;, &lt;a href="https://docs.djangoproject.com/en/3.1/topics/testing/tools/#transactiontestcase" rel="noopener noreferrer"&gt;TransactionTestCase&lt;/a&gt;, &lt;a href="https://docs.djangoproject.com/en/3.1/topics/testing/tools/#testcase" rel="noopener noreferrer"&gt;TestCase&lt;/a&gt;). You can derive your test class from any of the base classes and have your own method which will test for a specific functionality.&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;class&lt;/span&gt; &lt;span class="nc"&gt;YourTestClass&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Setup run before every test method.
&lt;/span&gt;        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tearDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Clean up run after every test method.
&lt;/span&gt;        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_something_that_will_pass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertFalse&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_something_that_will_fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The best base class for most tests is &lt;a href="https://docs.djangoproject.com/en/3.1/topics/testing/tools/#testcase" rel="noopener noreferrer"&gt;django.test.TestCase&lt;/a&gt;. This creates a clean database before its tests are run, and runs every test function in its own transaction. The test &lt;code&gt;client&lt;/code&gt; can be derived from this. &lt;em&gt;TestCase&lt;/em&gt; provides us with an additional method &lt;code&gt;setUpTestData&lt;/code&gt; along with &lt;code&gt;setUp&lt;/code&gt; for setting up our data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;&lt;code&gt;setUp()&lt;/code&gt;&lt;/em&gt;: It is called before every test method. This is useful when creating object that will need modifying before each test method.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;&lt;code&gt;setUpTestData()&lt;/code&gt;&lt;/em&gt;: This is called once before running the whole test. You use this one when you know the data you will create in this method will be the same for every test method. As this is run once for every test class, it is faster than &lt;code&gt;setUp()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;tearDown()&lt;/code&gt; method is not very useful when it comes to test that involves database. Moreover &lt;code&gt;TestCase&lt;/code&gt; does a full database flush at the start of a new test, so  it will take care of the teardown for you.&lt;/p&gt;

&lt;p&gt;We have some test methods provided to us by &lt;strong&gt;&lt;em&gt;unittest&lt;/em&gt;&lt;/strong&gt;, which can be used to test the condition of our code. Some standard assertions are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AssertTrue&lt;/code&gt;: Checks if the condition is true.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AsserFalse&lt;/code&gt;: Checks if the condition is false.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AssertEqual&lt;/code&gt;: Checks if the condition is equal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are some of the python specific assert methods:&lt;br&gt;
&lt;a href="https://docs.python.org/3/library/unittest.html#assert-methods" rel="noopener noreferrer"&gt;python unittest assert&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Django specific assert methods:&lt;br&gt;
&lt;a href="https://docs.djangoproject.com/en/3.2/topics/testing/tools/#assertions" rel="noopener noreferrer"&gt;django unittest assert&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  How To Run A Test
&lt;/h3&gt;

&lt;p&gt;To run all the test in the project use the command:&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 discover all files with the pattern &lt;strong&gt;test*.py&lt;/strong&gt; under the directory and run all tests defined.&lt;br&gt;
If we want to show more test information we can change the verbosity like below:&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 --verbosity 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Allowed verbosity levels are 0,1,2 and 3, the default being 1.&lt;br&gt;
To run  a specific test:&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;# Run the specified module
&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt; &lt;span class="n"&gt;manage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;

&lt;span class="c1"&gt;# Run the specified module
&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt; &lt;span class="n"&gt;manage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test_models&lt;/span&gt;

&lt;span class="c1"&gt;# Run the specified class
&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt; &lt;span class="n"&gt;manage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test_models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;YourTestClass&lt;/span&gt;

&lt;span class="c1"&gt;# Run the specified method
&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt; &lt;span class="n"&gt;manage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test_models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;YourTestClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_test_method&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;In the next part of our series we will get our hands dirty and will see how we can implement unit testing to our book-library.&lt;/p&gt;

&lt;p&gt;If you like what I write and want to support me and my work, please follow me on &lt;a href="https://twitter.com/rabbilyasar" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;, &lt;a href="https://github.com/rabbilyasar" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/rabbilyasar/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

</description>
      <category>django</category>
      <category>testing</category>
      <category>unittest</category>
      <category>python</category>
    </item>
    <item>
      <title>How To Deploy Django App To Heroku- The Simple Way</title>
      <dc:creator>Rabbil Yasar Sajal </dc:creator>
      <pubDate>Wed, 02 Jun 2021 14:06:14 +0000</pubDate>
      <link>https://dev.to/rabbilyasar/how-to-deploy-django-app-to-heroku-the-simple-way-21mh</link>
      <guid>https://dev.to/rabbilyasar/how-to-deploy-django-app-to-heroku-the-simple-way-21mh</guid>
      <description>&lt;p&gt;Have you ever tried uploading your django app to heroku but felt it was too complicated? If so, in this segment we will look at how you can upload your app to heroku. Don't worry, it is going to be short and to the point.&lt;/p&gt;

&lt;p&gt;For this tutorial, I will be assuming you have an app built, so the next step you will be delving into, is on how to deploy your app to heroku.&lt;/p&gt;

&lt;p&gt;Keeping that in mind, let's dive in and see how we can setup the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prepare your app
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Requirements.txt
&lt;/h3&gt;

&lt;p&gt;If you are already working in a &lt;code&gt;virtualenv&lt;/code&gt; you can easily run to create your &lt;code&gt;requirements.txt&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip freeze &amp;gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to manually write the version of the package just go to pypi website and find the latest version.&lt;br&gt;
It should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;asgiref==3.3.4
Django==3.2.3
gunicorn==20.1.0
Pillow==8.2.0
pytz==2021.1
sqlparse==0.4.1
django-heroku==0.3.1
whitenoise==5.2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Procfile
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a file with name &lt;code&gt;Procfile&lt;/code&gt; (Make sure to not have any extension). The &lt;code&gt;Procfile&lt;/code&gt; should be in the same directory as your &lt;code&gt;manage.py&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Install gunicorn.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install gunicorn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Make sure to add gunicorn to your &lt;code&gt;requirements.txt&lt;/code&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the following line to your &lt;code&gt;Procfile&lt;/code&gt;. The app name is basically the folder name where you have your &lt;code&gt;wsgi.py&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: gunicorn &amp;lt;app_name&amp;gt;.wsgi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  settings.py
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Next we will be installing a package called &lt;code&gt;django-heroku&lt;/code&gt;, after installing make sure to add it on the &lt;code&gt;requirements.txt&lt;/code&gt; file.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install django-heroku
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now go to your &lt;code&gt;settings.py&lt;/code&gt; and import it on top.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import django_heroku
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and paste this at the bottom of the file or else you are going to get a &lt;code&gt;KeyError&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;django_heroku.settings(locals())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Django Static
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Now to setup the static assets.
&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="n"&gt;BASE_DIR&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="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&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="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&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="nf"&gt;abspath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="c1"&gt;# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
&lt;/span&gt;&lt;span class="n"&gt;STATIC_ROOT&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="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;staticfiles&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;STATIC_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/static/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will basically tell django where to look for the static files and which folder to look for when &lt;code&gt;collectstatic&lt;/code&gt; is run.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install WhiteNoise and update your &lt;code&gt;requirements.txt&lt;/code&gt; file.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install whitenoise
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Next, install WhiteNoise into your Django application. This is done in settings.py‘s middleware section (at the top):
&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="n"&gt;MIDDLEWARE_CLASSES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;# Simplified static file serving.
&lt;/span&gt;    &lt;span class="c1"&gt;# https://warehouse.python.org/project/whitenoise/
&lt;/span&gt;    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;whitenoise.middleware.WhiteNoiseMiddleware&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Finally, if you would like gzip functionality enabled, also add the following setting to settings.py.
&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;# Simplified static file serving.
# https://warehouse.python.org/project/whitenoise/
&lt;/span&gt;
&lt;span class="n"&gt;STATICFILES_STORAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;whitenoise.storage.CompressedManifestStaticFilesStorage&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will need to create one more file called &lt;code&gt;runtime.txt&lt;/code&gt;. This will tell heroku which version of python needs to be installed. This step is optional because heroku will use a python version automatically when building but if you want a specific python version you can add it like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python-3.8.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is it for configuring our app for deployment. Now we go to the heroku cli and see how to upload the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;If you don't have heroku installed on your machine follow this &lt;a href="https://devcenter.heroku.com/articles/heroku-cli" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;login to heroku.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After successful login we will be able to create our app directly from the terminal.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a heroku app
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku create
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a heroku app with a random available name. However, if you want to give a name of your choice, just add the name after &lt;code&gt;heroku create&lt;/code&gt;. Make sure the name is unique and available.&lt;/p&gt;

&lt;p&gt;If you already have a heroku app and want to add the app as a Git remote, you need to execute&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku git:remote -a &amp;lt;yourapp&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now add all the files to git and commit.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git add .
git commit -m "deploy heroku"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Push all the files and build.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git push heroku master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once done this will deploy your app. Once deployed, we will need to migrate our database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku run bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give us a quick terminal to control our app. Here you can run all your django commands.&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 migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This will migrate all our files to the database.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;That is all for now. If you have followed all the steps above you should have an app which is now running on heroku. If you have come across any issues please leave a comment or knock-me. I will try to help fix them.&lt;/p&gt;

&lt;p&gt;Best of luck. Happy coding :D&lt;/p&gt;

</description>
      <category>django</category>
      <category>heroku</category>
      <category>github</category>
      <category>git</category>
    </item>
  </channel>
</rss>
