Test-Driven Development with Python. Obey the Testing Goat: Using Django, Selenium, and JavaScript. 3rd Edition - Helion

ISBN: 9781098148676
stron: 712, Format: ebook
Data wydania: 2025-10-30
Księgarnia: Helion
Cena książki: 211,65 zł (poprzednio: 267,91 zł)
Oszczędzasz: 21% (-56,26 zł)
The third edition of this trusted guide demonstrates the practical advantages of test-driven development (TDD) with Python and describes how to develop a real web application. You'll learn how to write and run tests before building each part of your app and then develop the minimum amount of code required to pass those tests. The result? Clean code that works.
In the process, author Harry Percival teaches software and web developers the basics of Django, Selenium, Git, JavaScript, and Mock libraries, along with current web development techniques. This book—updated for Python 3.14 and Django 4—clearly demonstrates how TDD encourages simple designs and inspires confidence.
Fully updated, this third edition addresses:
- The TDD workflow, including the unit test/code cycle and refactoring
- Unit tests for classes and functions and functional tests for user interactions within the browser
- Mock objects and the pros and cons of isolated versus integrated tests
- Testing and automation of deployments with a staging server
- Tests applied to the third-party plug-ins you integrate into your site
- Automatic tests using a continuous integration environment
- Using TDD to build a REST API with a JavaScript frontend interface
Osoby które kupowały "Test-Driven Development with Python. Obey the Testing Goat: Using Django, Selenium, and JavaScript. 3rd Edition", wybierały także:
- Jak zhakowa 125,00 zł, (10,00 zł -92%)
- Biologika Sukcesji Pokoleniowej. Sezon 3. Konflikty na terytorium 126,36 zł, (13,90 zł -89%)
- Power BI Desktop. Kurs video. Wykorzystanie narzędzia w analizie i wizualizacji danych 335,45 zł, (36,90 zł -89%)
- Windows Media Center. Domowe centrum rozrywki 66,67 zł, (8,00 zł -88%)
- Podręcznik startupu. Budowa wielkiej firmy krok po kroku 92,67 zł, (13,90 zł -85%)
Spis treści
Test-Driven Development with Python. Obey the Testing Goat: Using Django, Selenium, and JavaScript. 3rd Edition eBook -- spis treści
- Preface
- Why I Wrote a Book About Test-Driven Development
- Aims of This Book
- Outline
- Conventions Used in This Book
- Submitting Errata
- Using Code Examples
- OReilly Online Learning
- How to Contact Us
- Companion Video
- License for the Free Edition
- Preface to the Third Edition: TDD in the Age of AI
- AI Is Both Insanely Impressive and Incredibly Unreliable
- Mitigations for AIs Shortcomings Sure Look a Lot Like TDD
- Leaky Abstractions and the Importance of Experience
- My Own Experiences with AI
- The AI-Enabled Workflow of the Future
- Prerequisites and Assumptions
- Python 3 and Programming
- How HTML Works
- Django
- JavaScript
- Required Software Installations
- Installing Firefox
- Setting Up Your Virtualenv
- Activating and Deactivating the Virtualenv
- Installing Django and Selenium
- Some Error Messages Youre Likely to See When You Inevitably Fail to Activate Your Virtualenv
- Acknowledgments
- Additional Thanks for the Second Edition
- Additional Thanks for the Third Edition
- I. The Basics of TDD and Django
- 1. Getting Django Set Up Using a Functional Test
- Obey the Testing Goat! Do Nothing Until You Have a Test
- Getting Django Up and Running
- Starting a Git Repository
- 2. Extending Our Functional Test Using the unittest Module
- Using a Functional Test to Scope Out a Minimum Viable App
- The Python Standard Librarys unittest Module
- Commit
- 3. Testing a Simple Home Page with Unit Tests
- Our First Django App and Our First Unit Test
- Unit Tests, and How They Differ from Functional Tests
- Unit Testing in Django
- Djangos MVC, URLs, and View Functions
- Unit Testing a View
- At Last! We Actually Write Some Application Code!
- The Unit-Test/Code Cycle
- Our Functional Tests Tell Us Were Not Quite Done Yet
- Reading Tracebacks
- urls.py
- 4. What Are We Doing with All These Tests? (And, Refactoring)
- Programming Is Like Pulling a Bucket of Water Up from a Well
- Using Selenium to Test User Interactions
- The Dont Test Constants Rule, and Templates to the Rescue
- Refactoring to Use a Template
- Revisiting Our Unit Tests
- Test Behaviour, Not Implementation
- On Refactoring
- A Little More of Our Front Page
- Recap: The TDD Process
- Double-Loop TDD
- 5. Saving User Input: Testing the Database
- Wiring Up Our Form to Send a POST Request
- Testing the Contract Between Frontend and Backend
- Debugging Functional Tests
- Debugging with time.sleep
- Processing a POST Request on the Server
- Passing Python Variables to Be Rendered in the Template
- An Unexpected Failure
- Improving Error Messages in Tests
- Three Strikes and Refactor
- The Django ORM and Our First Model
- Our First Database Migration
- The Test Gets Surprisingly Far
- A New Field Means a New Migration
- Saving the POST to the Database
- Redirect After a POST
- Better Unit Testing Practice: Each Test Should Test One Thing
- Rendering Items in the Template
- Creating Our Production Database with migrate
- Recap
- 6. Improving Functional Tests: Ensuring Isolation and Removing Magic Sleeps
- Ensuring Test Isolation in Functional Tests
- Running Just the Unit Tests
- On Implicit and Explicit Waits, and Magic time.sleeps
- Ensuring Test Isolation in Functional Tests
- 7. Working Incrementally
- Small Design When Necessary
- Not Big Design Up Front
- YAGNI!
- REST-ish
- Implementing the New Design Incrementally Using TDD
- Ensuring We Have a Regression Test
- Iterating Towards the New Design
- Taking a First, Self-Contained Step: One New URL
- Separating Out Our Home Page and List View Functionality
- A new view function
- The FTs Detect a Regression
- Getting Back to a Working State as Quickly as Possible
- Green? Refactor
- Separating Out Our Home Page and List View Functionality
- Another Small Step: A Separate Template for Viewing Lists
- A Third Small Step: A New URL for Adding List Items
- A Test Class for New List Creation
- A URL and View for New List Creation
- Removing Now-Redundant Code and Tests
- A Regression! Pointing Our Forms at the New URL
- Debugging in DevTools
- Biting the Bullet: Adjusting Our Models
- A Foreign Key Relationship
- Adjusting the Rest of the World to Our New Models
- Each List Should Have Its Own URL
- Capturing Parameters from URLs
- Adjusting new_list to the New World
- The Functional Tests Detect Another Regression
- One More URL to Handle Adding Items to an Existing List
- The Last New urls.py Entry
- The Last New View
- Testing Template Context Directly
- A Final Refactor Using URL includes
- Can You Believe It?
- Small Design When Necessary
- 8. Prettification: Layout and Styling, and What to Test About It
- Testing Layout and Style
- Prettification: Using a CSS Framework
- Django Template Inheritance
- Integrating Bootstrap
- Rows and Columns
- Static Files in Django
- Switching to StaticLiveServerTestCase
- Using Bootstrap Components to Improve the Look of the Site
- Jumbotron!
- Large Inputs
- Table Styling
- Optional: Dark Mode
- A Semi-Decent Page
- Parsing HTML for Less Brittle Tests of Key HTML Content
- What We Glossed Over: collectstatic and Other Static Directories
- A Few Things That Didnt Make It
- II. Going to Production
- 9. Containerization aka Docker
- Docker, Containers, and Virtualization
- Why Not Just Use a Virtualenv?
- Docker and Your CV
- As Always, Start with a Test
- Making a src Folder
- Installing Docker
- Building a Docker Image and Running a Docker Container
- A First Cut of a Dockerfile
- Docker Build
- Docker Run
- Installing Django in a Virtualenv in Our Container Image
- Successful Run
- Using the FT to Check That Our Container Works
- Debugging Container Networking Problems
- Debugging Web Server Connectivity with curl
- Running Code Inside the Container with docker exec
- Docker Port Mapping
- Essential Googling the Error Message
- Database Migrations
- Should We Run migrate Inside the Dockerfile? No.
- Mounting Files Inside the Container
- Docker, Containers, and Virtualization
- 10. Making Our App Production-Ready
- What We Need to Do
- Switching to Gunicorn
- The FTs Catch a Problem with Static Files
- Serving Static Files with WhiteNoise
- Using requirements.txt
- Using Environment Variables to Adjust Settings for Production
- Setting DEBUG=True and SECRET_KEY
- Setting Environment Variables Inside the Dockerfile
- Setting Environment Variables at the Docker Command Line
- ALLOWED_HOSTS Is Required When Debug Mode Is Turned Off
- Collectstatic Is Required when Debug Is Turned Off
- Switching to a Nonroot User
- Making the Database Filepath Configurable
- Using UIDs to Set Permissions Across Host/Container Mounts
- Configuring Logging
- Provoking a Deliberate Error
- Exercise for the Reader: Using the Django check Command
- Wrap-Up
- 11. Getting a Server Ready for Deployment
- Manually Provisioning a Server to Host Our Site
- Choosing Where to Host Our Site
- Spinning Up Our Own Server
- Getting a Domain Name
- Configuring DNS for Staging and Live Domains
- Ansible
- Ansible Versus SSH: How Well Talk to Our Server
- Start by Making Sure We Can SSH In
- Debugging Issues with SSH
- Debugging network connectivity
- Debugging SSH auth issues
- Installing Ansible
- Checking Ansible Can Talk to Our Server
- Debugging Issues with SSH
- Manually Provisioning a Server to Host Our Site
- 12. Infrastructure as Code: Automated Deployments with Ansible
- A First Cut of an Ansible Playbook for Deployment
- SSHing Into the Server and Viewing Container Logs
- Allowing Rootless Docker Access
- Getting Our Image Onto the Server
- Taking a Look Around Manually
- Docker logs
- Taking a Look Around Manually
- Setting Environment Variables and Secrets
- Manually Checking Environment Variables for Running Containers
- docker exec env
- docker inspect
- Manually Checking Environment Variables for Running Containers
- Running FTs to Check on Our Deploy
- Manual Debugging with curl Against the Staging Server
- Mounting the Database on the Server and Running Migrations
- It Workssss
- Deploying to Prod
- git tag the Release
- Tell Everyone!
- Further Reading
- III. Forms and Validation
- 13. Splitting Our Tests into Multiple Files, and a Generic Wait Helper
- Start on a Validation FT: Preventing Blank Items
- Skipping a Test
- Splitting Functional Tests Out into Many Files
- Running a Single Test File
- A New FT Tool: A Generic Explicit Wait Helper
- Finishing Off the FT
- Refactoring Unit Tests into Several Files
- Start on a Validation FT: Preventing Blank Items
- 14. Validation at the Database Layer
- Model-Layer Validation
- The self.assertRaises Context Manager
- Django Model Constraints and Their Interaction with Databases
- Inspecting Our Constraints at the Database Level
- Testing Django Model Validation
- A Django Quirk: Model Save Doesnt Run Validation
- Surfacing Model Validation Errors in the View
- Checking That Invalid Input Isnt Saved to the Database
- Adding an Early Return to Our FT to Let Us Refactor Against Green
- Django Pattern: Processing POST Requests in the Same View That Renders the Form
- Refactor: Transferring the new_item Functionality into view_list
- Enforcing Model Validation in view_list
- Refactor: Removing Hardcoded URLs
- The {% url %} Template Tag
- Using get_absolute_url for Redirects
- Model-Layer Validation
- 15. A Simple Form
- Moving Validation Logic Into a Form
- Exploring the Forms API with a Unit Test
- Switching to a Django ModelForm
- Testing and Customising Form Validation
- Attempting to Use the Form in Our Views
- Using the Form in a View with a GET Request
- The Trade-offs of Django ModelForms: The Frontend Is Coupled to the Database
- A Big Find-and-Replace
- Backing Out Our Changes and Getting to a Working State
- Renaming the name Attribute
- Renaming the id Attribute
- A Second Attempt at Using the Form in Our Views
- Using the Form in a View That Takes POST Requests
- Using the Form to Display Errors in the Template
- Get Back to a Working State
- A Helper Method for Several Short Tests
- Using the Form in the Existing Lists View
- Using the Form to Pass Errors to the Template
- Refactoring the View to Use the Form Fully
- An Unexpected Benefit: Free Client-Side Validation from HTML5
- A Pat on the Back
- But Have We Wasted a Lot of Time?
- Using the ModelForms Own Save Method
- Moving Validation Logic Into a Form
- 16. More Advanced Forms
- Another FT for Duplicate Items
- Preventing Duplicates at the Model Layer
- Rewriting the Old Model Test
- Integrity Errors That Show Up on Save
- Experimenting with Duplicate Item Validation at the Views Layer
- A More Complex Form to Handle Uniqueness Validation
- Using the Existing List Item Form in the List View
- Customising the Save Method on Our New Form
- The FTs Pick Up an Issue with Bootstrap Classes
- Conditionally Customising CSS Classes for Invalid Forms
- A Little Digression on Queryset Ordering and String Representations
- On the Trade-offs of Django ModelForms, and Frameworks in General
- Moving Presentation Logic Back into the Template
- Tidying Up the Forms
- Switching Back to Simple Forms
- Wrapping Up: What Weve Learned About Testing Django
- Another FT for Duplicate Items
- IV. More Advanced Topics in Testing
- 17. A Gentle Excursion into JavaScript
- Starting with an FT
- A Quick Spike
- A Simple Inline Script
- Using the Browser DevTools
- Choosing a Basic JavaScript Test Runner
- An Overview of Jasmine
- Setting Up Our JavaScript Test Environment
- Our First Smoke Test: Describe, It, Expect
- Running the Tests via the Browser
- Testing with Some DOM Content
- Building a JavaScript Unit Test for Our Desired Functionality
- Fixtures, Execution Order, and Global State: Key Challenges of JavaScript Testing
- console.log for Debug Printing
- Using an Initialize Function for More Control Over Execution Time
- Deliberately Breaking Our Code to Force Ourselves to Write More Tests
- Red/Green/Refactor: Removing Hardcoded Selectors
- Does it Work?
- Testing Integration with CSS and Bootstrap
- Columbo Says: Wait for Onload
- JavaScript Testing in the TDD Cycle
- 18. Deploying Our New Code
- The Deployment Checklist
- A Full Test Run Locally
- Quick Test Run Against Docker
- Staging Deploy and Test Run
- Production Deploy
- What to Do If You See a Database Error
- How to Delete the Database on the Staging Server
- Wrap-Up: git tag the New Release
- 19. User Authentication, Spiking, and De-Spiking
- Passwordless Auth with Magic Links
- A Somewhat Larger Spike
- Starting a Branch for the Spike
- Frontend Login UI
- Sending Emails from Django
- Email Server Config for Django
- Another Secret, Another Environment Variable
- Storing Tokens in the Database
- Custom Authentication Models
- Finishing the Custom Django Auth
- De-Spiking
- Making a Plan
- Wring an FT Against the Spiked Code
- Reverting Our Spiked Code
- A Minimal Custom User Model
- Tests as Documentation
- A Token Model to Link Emails with a Unique ID
- 20. Using Mocks to Test External Dependencies
- Before We Start: Getting the Basic Plumbing In
- Mocking Manuallyaka Monkeypatching
- The Python Mock Library
- Using unittest.patch
- Getting the FT a Little Further Along
- Testing the Django Messages Framework
- Adding Messages to Our HTML
- Starting on the Login URL
- Checking That We Send the User a Link with a Token
- De-Spiking Our Custom Authentication Backend
- One if = One More Test
- The get_user Method
- 21. Using Mocks for Test Isolation
- Using Our Auth Backend in the Login View
- Straightforward Non-Mocky Test for Our View
- Combinatorial Explosion
- The Car Factory Example
- Using Mocks to Test Parts of Our System in Isolation
- Mocks Can Also Let You Test the Implementation, When It Matters
- Starting Again: Test-Driving Our Implementation with Mocks
- Using mock.return_value
- Using .return_value During Test Setup
- UnDONTifying
- Deciding Which Tests to Keep
- The Moment of Truth: Will the FT Pass?
- It Works in Theory! Does It Work in Practice?
- Using Our New Environment Variable, and Saving It to .env
- Finishing Off Our FT: Testing Logout
- Using Our Auth Backend in the Login View
- 22. Test Fixtures and a Decorator for Explicit Waits
- Skipping the Login Process by Pre-creating a Session
- Checking That It Works
- Our Final Explicit Wait Helper: A Wait Decorator
- Skipping the Login Process by Pre-creating a Session
- 23. Debugging and Testing Server Issues
- The Proof Is in the Pudding: Using Docker to Catch Final Bugs
- Inspecting the Docker Container Logs
- Another Environment Variable in Docker
- mail.outbox Wont Work Outside Djangos Test Environment
- Deciding How to Test Real Email Sending
- An Alternative Method for Setting Secret Environment Variables on the Server
- Debugging with SQL
- Managing Fixtures in Real Databases
- A Django Management Command to Create Sessions
- Getting the FT to Run the Management Command on the Server
- Running Commands Using Docker Exec and (Optionally) SSH
- Recap: Creating Sessions Locally Versus Staging
- Locally:
- Against Docker locally:
- Against Docker on the server:
- Testing the Management Command
- Test Database Cleanup
- Wrap-Up
- 24. Finishing My Lists: Outside-In TDD
- The Alternative: Inside-Out
- Why Prefer Outside-In?
- The FT for My Lists
- The Outside Layer: Presentation and Templates
- Moving Down One Layer to View Functions (the Controller)
- Another Pass, Outside-In
- A Quick Restructure of Our Template Composition
- An Early Return So Were Refactoring Against Green
- Factoring Out Two Template includes
- Designing Our API Using the Template
- Moving Down to the Next Layer: What the View Passes to the Template
- The Next Requirement from the Views Layer: New Lists Should Record Owner
- A Decision Point: Whether to Proceed to the Next Layer with a Failing Test
- Moving Down to the Model Layer
- Final Step: Feeding Through the .name API from the Template
- 25. CI: Continuous Integration
- CI in Modern Development Workflows
- Choosing a CI Service
- Getting Our Code into GitLab
- Signing Up
- Starting a Project
- Pushing Our Code Up Using Git Push
- Setting Up a First Cut of a CI Pipeline
- First Build! (and First Failure)
- Trying to Reproduce a CI Error Locally
- Enabling Debug Logs for Selenium/Firefox/Webdriver
- Enabling Headless Mode for Firefox
- A Common Bugbear: Flaky Tests
- Taking Screenshots
- Saving Build Outputs (or Debug Files) as Artifacts
- If in Doubt, Try Bumping the Timeout!
- A Successful Python Test Run
- Running Our JavaScript Tests in CI
- Installing Node.js
- Installing and Configuring the Jasmine Browser Runner
- Adding a Build Step for JavaScript
- Tests Now Pass
- Some Things We Didnt Cover
- Defining a Docker Image for CI
- Caching
- Automated Deployment, aka Continuous Delivery (CD)
- 26. The Token Social Bit, the Page Pattern, and an Exercise for the Reader
- An FT with Multiple Users, and addCleanup
- The Page Pattern
- Extend the FT to a Second User, and the My Lists Page
- An Exercise for the Reader
- Step-by-Step Guide
- 27. Fast Tests, Slow Tests, and Hot Lava
- Why Do We Test? Our Desiderata for Effective Tests
- Confidence and Correctness (Preventing Regression)
- A Productive Workflow
- Driving Better Design
- Were Our Unit Tests Integration Tests All Along? What Is That Warm Glow Coming from the Database?
- Weve Been in the Sweet Spot
- What Is a True Unit Test? Does it Matter?
- Integration and Functional Tests Get Slower Over Time
- Were Not Getting the Full Potential Benefits of Testing
- The Ideal of the Test Pyramid
- Avoiding Mock Hell
- The Actual Solutions Are Architectural
- Ports and Adapters/Hexagonal/Onion/Clean Architecture
- Functional Core, Imperative Shell
- The Central Conceit: These Architectures Are Better
- The Hardest Part: Knowing When to Make the Switch
- Wrap-Up
- Further Reading
- Why Do We Test? Our Desiderata for Effective Tests
- Obey the Testing Goat!
- Testing Is Hard
- Keep Your CI Builds Green
- Take Pride in Your Tests, as You Do in Your Code
- Remember to Tip the Bar Staff
- Dont Be a Stranger!
- Testing Is Hard
- Bibliography
- A. Cheat Sheet
- Initial Project Setup
- The Basic TDD Workflow: Red/Green/Refactor
- Moving Beyond Dev-Only Testing
- General Testing Best Practices
- Selenium/Functional Testing Best Practices
- Outside-In
- The Test Pyramid
- B. What to Do Next
- Switch to Postgres
- Run Your Tests Against Different Browsers
- The Django Admin Site
- Write Some Security Tests
- Test for Graceful Degradation
- Caching and Performance Testing
- JavaScript Frameworks
- Async and Websockets
- Switch to Using pytest
- Check Out coverage.py
- Client-Side Encryption
- Your Suggestion Here
- C. Source Code Examples
- Full List of Links for Each Chapter
- Using Git to Check Your Progress
- Downloading a ZIP File for a Chapter
- Dont Let it Become a Crutch!
- Index





