Packages and Modules
Unlock the power of reusable code and streamline your development process with Python's packages and modules.
In this chapter, we'll explore the fundamentals of Python packages and modules, which are essential for organizing and reusing code. You'll learn how to create, import, and manage modules and packages, as well as understand the difference between them. We'll also cover best practices for structuring your projects and using virtual environments to keep dependencies isolated. By the end of this chapter, you'll be equipped to write clean, modular, and maintainable code.
Creating and Using Packages
What is a Python Package?
A Python package is a way of structuring Python’s module namespace by using "dotted module names." Essentially, a package is a directory that contains a special file called __init__.py and other Python modules or sub-packages. This file can be empty, but its presence indicates that the directory should be treated as a package.
How to Create a Python Package
Creating a Python package involves a few straightforward steps:
- 
Create a Directory Structure: Start by creating a directory for your package. Inside this directory, create an
__init__.pyfile. This file can be empty, but it is necessary for Python to recognize the directory as a package.mypackage/ __init__.py module1.py module2.py - 
Add Modules: Add your Python modules (
.pyfiles) to the package directory. Each module can contain functions, classes, and variables that you want to reuse across your project. - 
Organize Sub-packages: If your package grows, you can create sub-packages by adding more directories with their own
__init__.pyfiles.mypackage/ __init__.py module1.py module2.py subpackage/ __init__.py submodule1.py 
Importing Packages
To use a package, you need to import it. Python provides several ways to import packages and modules:
- 
Basic Import: Import the entire package.
import mypackage - 
Import Specific Modules: Import specific modules from the package.
from mypackage import module1 - 
Import Functions or Classes: Import specific functions or classes from a module within the package.
from mypackage.module1 import my_function - 
Import with Aliases: Use aliases to avoid naming conflicts or to simplify long names.
import mypackage as mp from mypackage.module1 import my_function as mf 
Best Practices for Package Structure
Following best practices ensures that your packages are easy to maintain and use:
- Consistent Naming: Use lowercase names for packages and modules to follow Python’s naming conventions.
 - Documentation: Include docstrings in your modules and packages to explain their purpose and usage.
 - Version Control: Use version control systems like Git to manage changes to your packages.
 - Testing: Write unit tests for your modules to ensure they work as expected.
 
Using Virtual Environments
Virtual environments are crucial for managing dependencies and isolating your project’s environment. Here’s how to use them:
- 
Create a Virtual Environment: Use the
venvmodule to create a virtual environment.python -m venv myenv - 
Activate the Virtual Environment:
- 
On Windows:
myenv\Scripts\activate - 
On macOS and Linux:
source myenv/bin/activate 
 - 
 - 
Install Dependencies: Use
pipto install the necessary packages within the virtual environment.pip install requests - 
Deactivate the Virtual Environment: When you’re done, deactivate the virtual environment to return to the global Python environment.
deactivate 
Managing Dependencies
To manage dependencies effectively:
- 
Requirements File: Create a
requirements.txtfile to list all the dependencies for your project.requests==2.25.1 numpy==1.19.5 - 
Install from Requirements File: Use
pipto install all dependencies listed in therequirements.txtfile.pip install -r requirements.txt 
Example: Creating a Simple Package
Let’s create a simple package called math_utils with two modules: arithmetic.py and geometry.py.
- 
Directory Structure:
math_utils/ __init__.py arithmetic.py geometry.py - 
arithmetic.py:
def add(a, b): return a + b def subtract(a, b): return a - b - 
geometry.py:
def circle_area(radius): return 3.14159 * radius * radius def rectangle_area(length, width): return length * width - 
Using the Package:
from math_utils.arithmetic import add, subtract from math_utils.geometry import circle_area, rectangle_area print(add(5, 3)) # Output: 8 print(subtract(10, 4)) # Output: 6 print(circle_area(5)) # Output: 78.53975 print(rectangle_area(4, 6)) # Output: 24 
By following these steps and best practices, you can create well-organized, maintainable, and reusable Python packages. This will not only improve your code quality but also make it easier for others to understand and contribute to your projects.## Modules in GoLang
Understanding Go Modules
Go modules are the standard way to manage dependencies in Go (Golang) projects. Introduced in Go 1.11 and made the default in Go 1.13, modules provide a simple and efficient way to handle versioning and dependency management. A Go module is defined by a go.mod file, which specifies the module's path and its dependencies.
Creating a Go Module
To create a Go module, follow these steps:
- 
Initialize the Module: Navigate to your project directory and run the following command to initialize a new module. Replace
example.com/yourmodulewith your module's path.go mod init example.com/yourmoduleThis command creates a
go.modfile in your project directory. - 
Add Dependencies: Use the
go getcommand to add dependencies to your module. For example, to add thegithub.com/sirupsen/logruspackage, run:go get github.com/sirupsen/logrusThis command updates the
go.modfile with the new dependency and downloads the necessary packages. 
The go.mod File
The go.mod file is the heart of a Go module. It contains the module's path, Go version, and a list of required dependencies. Here's an example of what a go.mod file might look like:
module example.com/yourmodule
go 1.18
require (
    github.com/sirupsen/logrus v1.8.1
    github.com/spf13/cobra v1.1.3
)
- module: Specifies the module's path.
 - go: Specifies the Go version required for the module.
 - require: Lists the module's dependencies and their versions.
 
Managing Dependencies
Effective dependency management is crucial for maintaining a healthy Go project. Here are some best practices:
- Use Semantic Versioning: Follow semantic versioning (semver) to manage dependency versions. This helps in understanding the impact of version changes.
 - Regularly Update Dependencies: Keep your dependencies up-to-date to benefit from the latest features and security patches. Use tools like 
go list -u -m allto list outdated dependencies. - Vendor Dependencies: Use the 
go mod vendorcommand to create avendordirectory containing all dependencies. This ensures that your project is self-contained and can be built without accessing external networks. 
Using Go Modules in Your Project
To use Go modules in your project, follow these steps:
- 
Import Packages: Import the required packages in your Go files. For example:
import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) - 
Build and Run: Use the
go buildandgo runcommands to build and run your project. Go will automatically use the dependencies specified in thego.modfile.go build go run main.go 
Best Practices for Go Modules
Following best practices ensures that your Go modules are robust and maintainable:
- Consistent Module Paths: Use consistent and meaningful module paths. Avoid using local paths or paths that include version numbers.
 - Documentation: Include clear documentation in your 
go.modfile and module code to explain the purpose and usage of your module. - Testing: Write comprehensive tests for your module to ensure its functionality and reliability.
 - Security: Regularly check for security vulnerabilities in your dependencies using tools like 
go list -f '{{.Path}} @ {{.Version}}' -m allandgo get -uto update dependencies. 
Example: Creating a Simple Go Module
Let's create a simple Go module called greetings that provides a function to generate greetings.
- 
Directory Structure:
greetings/ go.mod greetings.go - 
Initialize the Module:
go mod init example.com/greetings - 
greetings.go:
package greetings import "fmt" // Hello returns a greeting message. func Hello(name string) string { return fmt.Sprintf("Hello, %s!", name) } - 
Using the Module:
Create another module that uses the
greetingsmodule.main/ go.mod main.gogo mod init example.com/main go get example.com/greetingspackage main import ( "fmt" "example.com/greetings" ) func main() { message := greetings.Hello("World") fmt.Println(message) } 
By following these steps and best practices, you can create well-organized, maintainable, and reusable Go modules. This will not only improve your code quality but also make it easier for others to understand and contribute to your projects.## Dependency Management
Effective dependency management is crucial for maintaining robust and scalable software projects. Whether you're working with Python or GoLang, understanding how to manage dependencies ensures that your projects remain secure, up-to-date, and easy to maintain. This section delves into the best practices and tools for dependency management in both Python and GoLang.
Dependency Management in Python
Python offers several tools and best practices for managing dependencies, ensuring that your projects remain stable and secure.
Using requirements.txt
The requirements.txt file is a standard way to list the dependencies for a Python project. This file specifies the packages and their versions required for your project to run.
Creating a requirements.txt File:
- 
Generate Requirements: Use the
pip freezecommand to generate arequirements.txtfile that lists all installed packages and their versions.pip freeze > requirements.txt - 
Install from
requirements.txt: Use thepip install -r requirements.txtcommand to install all dependencies listed in the file.pip install -r requirements.txt 
Example requirements.txt:
requests==2.25.1
numpy==1.19.5
pandas==1.1.5
Using pipenv
pipenv is a tool that aims to bring the best of all packaging worlds (bundled, unbundled, and virtualized) to the Python world. It automatically creates and manages a virtual environment for your projects, as well as a Pipfile to manage dependencies.
Installing pipenv:
pip install pipenv
Creating a Pipfile:
- 
Initialize Pipenv: Navigate to your project directory and run
pipenv installto create aPipfile.pipenv install - 
Add Dependencies: Use
pipenv install <package>to add dependencies to yourPipfile.pipenv install requests - 
Install Dependencies: Use
pipenv installto install all dependencies listed in thePipfile.pipenv install 
Example Pipfile:
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[packages]
requests = "==2.25.1"
numpy = "==1.19.5"
pandas = "==1.1.5"
Using poetry
poetry is another dependency management tool that focuses on simplicity and consistency. It uses a pyproject.toml file to manage dependencies and project metadata.
Installing poetry:
curl -sSL https://install.python-poetry.org | python3 -
Creating a pyproject.toml File:
- 
Initialize Poetry: Navigate to your project directory and run
poetry initto create apyproject.tomlfile.poetry init - 
Add Dependencies: Use
poetry add <package>to add dependencies to yourpyproject.tomlfile.poetry add requests - 
Install Dependencies: Use
poetry installto install all dependencies listed in thepyproject.tomlfile.poetry install 
Example pyproject.toml:
[tool.poetry]
name = "myproject"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.8"
requests = "^2.25.1"
numpy = "^1.19.5"
pandas = "^1.1.5"
Dependency Management in GoLang
GoLang provides a robust system for managing dependencies through Go modules. Understanding how to use Go modules effectively is essential for maintaining clean and efficient Go projects.
Using go.mod
The go.mod file is the cornerstone of Go modules. It specifies the module's path, Go version, and required dependencies.
Creating a go.mod File:
- 
Initialize the Module: Navigate to your project directory and run
go mod initto create ago.modfile.go mod init example.com/yourmodule - 
Add Dependencies: Use
go getto add dependencies to yourgo.modfile.go get github.com/sirupsen/logrus 
Example go.mod:
module example.com/yourmodule
go 1.18
require (
    github.com/sirupsen/logrus v1.8.1
    github.com/spf13/cobra v1.1.3
)
Managing Dependencies
Effective dependency management in GoLang involves several best practices:
- Use Semantic Versioning: Follow semantic versioning (semver) to manage dependency versions. This helps in understanding the impact of version changes.
 - Regularly Update Dependencies: Keep your dependencies up-to-date to benefit from the latest features and security patches. Use tools like 
go list -u -m allto list outdated dependencies. - Vendor Dependencies: Use the 
go mod vendorcommand to create avendordirectory containing all dependencies. This ensures that your project is self-contained and can be built without accessing external networks. 
Updating Dependencies:
go get -u
Vendoring Dependencies:
go mod vendor
Using go.sum
The go.sum file is automatically generated by Go and contains checksums for the dependencies specified in the go.mod file. This file ensures the integrity and security of your dependencies.
Example go.sum:
github.com/sirupsen/logrus v1.8.1/go.mod h1:...
github.com/spf13/cobra v1.1.3/go.mod h1:...
Best Practices for Dependency Management
Regardless of the language, following best practices for dependency management is essential for maintaining healthy and secure projects.
- Consistent Versioning: Use consistent versioning strategies to avoid conflicts and ensure compatibility.
 - Regular Audits: Regularly audit your dependencies to identify and address security vulnerabilities.
 - Documentation: Maintain clear documentation for your dependencies, including their purpose and usage.
 - Automated Testing: Implement automated testing to ensure that your dependencies work as expected and do not introduce regressions.
 
By adhering to these best practices and utilizing the right tools, you can effectively manage dependencies in your Python and GoLang projects, ensuring they remain secure, up-to-date, and easy to maintain.## Documenting Your Code
Why Document Your Code?
Documenting your code is a critical practice that enhances code readability, maintainability, and collaboration. Well-documented code serves as a roadmap for developers, making it easier to understand the purpose, functionality, and usage of modules and packages. This is particularly important in large projects where multiple developers may contribute to the codebase.
Types of Code Documentation
Effective code documentation includes several types, each serving a specific purpose:
- Inline Comments: Brief explanations within the code to clarify complex logic or non-obvious decisions.
 - Docstrings: Detailed descriptions of modules, classes, and functions, typically placed at the beginning of the code block.
 - README Files: Comprehensive overviews of the project, including installation instructions, usage examples, and contribution guidelines.
 - API Documentation: Detailed descriptions of the public interfaces, including parameters, return values, and usage examples.
 
Best Practices for Inline Comments
Inline comments should be concise and focused on explaining why the code does something, rather than what it does. Here are some best practices:
- Be Concise: Keep comments short and to the point.
 - Explain the Why: Focus on the rationale behind the code, not the mechanics.
 - Avoid Obvious Comments: Don't comment on obvious code; focus on complex or non-intuitive parts.
 - Use Consistent Style: Follow a consistent commenting style throughout the codebase.
 
Example:
# Calculate the discount based on the customer's loyalty points
discount = loyalty_points * 0.05
Writing Effective Docstrings
Docstrings are essential for documenting modules, classes, and functions. They provide a clear and concise explanation of what the code does, its parameters, return values, and any exceptions it may raise.
Structure of a Docstring:
- Summary Line: A brief description of the function or class.
 - Blank Line: Separates the summary from the detailed description.
 - Detailed Description: Explains the purpose, parameters, return values, and any exceptions.
 - Examples: Provides usage examples to illustrate how the function or class is used.
 
Example:
def calculate_discount(price, loyalty_points):
    """
    Calculate the discount based on the customer's loyalty points.
    Args:
        price (float): The original price of the item.
        loyalty_points (int): The number of loyalty points the customer has.
    Returns:
        float: The discounted price.
    Raises:
        ValueError: If loyalty_points is negative.
    """
    if loyalty_points < 0:
        raise ValueError("Loyalty points cannot be negative.")
    discount = loyalty_points * 0.05
    return price - discount
Creating Comprehensive README Files
A well-written README file is the first point of contact for anyone interacting with your project. It should provide a clear overview of the project, including its purpose, installation instructions, usage examples, and contribution guidelines.
Structure of a README File:
- Project Title and Description: A brief description of the project.
 - Table of Contents: A list of sections for easy navigation.
 - Installation Instructions: Step-by-step instructions for installing the project.
 - Usage Examples: Examples of how to use the project.
 - Contribution Guidelines: Instructions for contributing to the project.
 - License: Information about the project's license.
 
Example:
# Project Title
A brief description of the project.
## Table of Contents
- [Installation](#installation)
- [Usage](#usage)
- [Contributing](#contributing)
- [License](#license)
## Installation
1. Clone the repository:
   ```bash
   git clone https://github.com/yourusername/yourproject.git
- Navigate to the project directory:
cd yourproject - Install the dependencies:
pip install -r requirements.txt 
Usage
Example of how to use the project:
from yourmodule import your_function
result = your_function(10, 20)
print(result)
Contributing
Contributions are welcome! Please follow these steps:
- Fork the repository.
 - Create a new branch:
git checkout -b feature-branch - Make your changes and commit them:
git commit -m "Add new feature" - Push to the branch:
git push origin feature-branch - Create a pull request.
 
License
This project is licensed under the MIT License.
### Generating API Documentation
API documentation provides detailed information about the public interfaces of your code. Tools like Sphinx (for Python) and godoc (for GoLang) can automatically generate API documentation from docstrings and comments.
**Using Sphinx for Python:**
1. **Install Sphinx**:
   ```bash
   pip install sphinx
- 
Initialize Sphinx:
sphinx-quickstart - 
Configure Sphinx: Edit the
conf.pyfile to include your project's modules. - 
Generate Documentation:
make html - 
View Documentation: Open the
index.htmlfile in your browser to view the generated documentation. 
Using godoc for GoLang:
- 
Install godoc:
go get golang.org/x/tools/cmd/godoc - 
Generate Documentation:
godoc -http=:6060 - 
View Documentation: Open your browser and navigate to
http://localhost:6060/pkg/yourmodule/to view the generated documentation. 
Tools for Code Documentation
Several tools can help streamline the documentation process:
- Sphinx: A powerful documentation generator for Python projects.
 - godoc: A documentation generator for GoLang projects.
 - Doxygen: A versatile documentation generator that supports multiple programming languages.
 - JSDoc: A documentation generator for JavaScript projects.
 
Integrating Documentation into Your Workflow
Integrating documentation into your development workflow ensures that it remains up-to-date and relevant. Here are some tips:
- Document as You Code: Write comments and docstrings as you develop your code.
 - Review Documentation: Include documentation reviews in your code review process.
 - Automate Documentation Generation: Use tools to automatically generate documentation from comments and docstrings.
 - Keep Documentation Updated: Regularly update documentation to reflect changes in the codebase.
 
Example: Documenting a Python Package
Let's document a simple Python package called math_utils with two modules: arithmetic.py and geometry.py.
- 
Directory Structure:
math_utils/ __init__.py arithmetic.py geometry.py README.md - 
arithmetic.py:
def add(a, b): """ Add two numbers. Args: a (float): The first number. b (float): The second number. Returns: float: The sum of the two numbers. """ return a + b def subtract(a, b): """ Subtract the second number from the first. Args: a (float): The first number. b (float): The second number. Returns: float: The difference between the two numbers. """ return a - b - 
geometry.py:
def circle_area(radius): """ Calculate the area of a circle. Args: radius (float): The radius of the circle. Returns: float: The area of the circle. """ return 3.14159 * radius * radius def rectangle_area(length, width): """ Calculate the area of a rectangle. Args: length (float): The length of the rectangle. width (float): The width of the rectangle. Returns: float: The area of the rectangle. """ return length * width - 
README.md:
# Math Utils A collection of utility functions for mathematical operations. ## Table of Contents - [Installation](#installation) - [Usage](#usage) - [Modules](#modules) ## Installation 1. Clone the repository: ```bash git clone https://github.com/yourusername/math_utils.git- Navigate to the project directory:
cd math_utils - Install the dependencies:
pip install -r requirements.txt 
Usage
Example of how to use the
math_utilspackage:from math_utils.arithmetic import add, subtract from math_utils.geometry import circle_area, rectangle_area print(add(5, 3)) # Output: 8 print(subtract(10, 4)) # Output: 6 print(circle_area(5)) # Output: 78.53975 print(rectangle_area(4, 6)) # Output: 24Modules
arithmetic.py: Functions for basic arithmetic operations.geometry.py: Functions for geometric calculations.
 - Navigate to the project directory:
 
By following these best practices and utilizing the right tools, you can create comprehensive and effective documentation for your code. This will not only improve the maintainability and readability of your codebase but also make it easier for others to understand and contribute to your projects.## Testing Your Code
Importance of Code Testing
Testing your code is a critical step in the software development process. It ensures that your modules and packages function as expected, are reliable, and can handle various edge cases. Effective testing helps catch bugs early, improves code quality, and makes your codebase more maintainable. By integrating testing into your development workflow, you can build robust and scalable applications.
Types of Testing
Understanding the different types of testing is essential for creating a comprehensive testing strategy. Here are the primary types of testing you should consider:
- Unit Testing: Tests individual components or functions in isolation to ensure they work correctly.
 - Integration Testing: Tests the interaction between different modules or services to ensure they work together as expected.
 - System Testing: Tests the entire system to verify that it meets the specified requirements.
 - Acceptance Testing: Tests the system from the end-user's perspective to ensure it meets their needs and expectations.
 
Writing Unit Tests in Python
Unit tests are the foundation of a robust testing strategy. In Python, the unittest module provides a framework for writing and running unit tests. Here’s how to write unit tests for your Python modules:
Setting Up Unit Tests
- 
Create a Test Directory: Organize your tests in a separate directory, typically named
tests.mypackage/ __init__.py module1.py module2.py tests/ __init__.py test_module1.py test_module2.py - 
Write Test Cases: Use the
unittestframework to write test cases for your modules.# tests/test_module1.py import unittest from mypackage.module1 import add, subtract class TestModule1(unittest.TestCase): def test_add(self): self.assertEqual(add(5, 3), 8) self.assertEqual(add(-1, 1), 0) self.assertEqual(add(-1, -1), -2) def test_subtract(self): self.assertEqual(subtract(10, 4), 6) self.assertEqual(subtract(-1, 1), -2) self.assertEqual(subtract(-1, -1), 0) if __name__ == '__main__': unittest.main() - 
Run Tests: Use the
unittestcommand to run your tests.python -m unittest discover -s tests 
Using pytest
pytest is a popular testing framework that provides a more flexible and powerful alternative to unittest. Here’s how to use pytest for testing your Python modules:
- 
Install
pytest:pip install pytest - 
Write Test Cases: Use
pytestto write test cases for your modules.# tests/test_module1.py from mypackage.module1 import add, subtract def test_add(): assert add(5, 3) == 8 assert add(-1, 1) == 0 assert add(-1, -1) == -2 def test_subtract(): assert subtract(10, 4) == 6 assert subtract(-1, 1) == -2 assert subtract(-1, -1) == 0 - 
Run Tests: Use the
pytestcommand to run your tests.pytest 
Writing Unit Tests in GoLang
In GoLang, the testing package provides a built-in framework for writing and running unit tests. Here’s how to write unit tests for your Go modules:
Setting Up Unit Tests
- 
Create a Test File: Create a test file with the
_test.gosuffix in the same directory as your module.greetings/ greetings.go greetings_test.go - 
Write Test Cases: Use the
testingpackage to write test cases for your module.// greetings_test.go package greetings import "testing" func TestHello(t *testing.T) { got := Hello("World") want := "Hello, World!" if got != want { t.Errorf("Hello(\"World\") = %q, want %q", got, want) } } - 
Run Tests: Use the
go testcommand to run your tests.go test 
Using Table-Driven Tests
Table-driven tests are a powerful technique for writing concise and maintainable tests in Go. Here’s an example of a table-driven test:
// greetings_test.go
package greetings
import "testing"
func TestHello(t *testing.T) {
   tests := []struct {
       input    string
       expected string
   }{
       {"World", "Hello, World!"},
       {"Go", "Hello, Go!"},
       {"", "Hello, !"},
   }
   for _, tt := range tests {
       got := Hello(tt.input)
       if got != tt.expected {
           t.Errorf("Hello(%q) = %q, want %q", tt.input, got, tt.expected)
       }
   }
}
Best Practices for Code Testing
Following best practices ensures that your tests are effective and maintainable:
- Write Tests Early: Integrate testing into your development process from the beginning. Write tests before or alongside your code to catch issues early.
 - Test Small Units: Focus on testing small, isolated units of code. This makes it easier to identify and fix issues.
 - Use Descriptive Names: Use descriptive names for your test cases and functions to make it clear what is being tested.
 - Automate Testing: Use continuous integration (CI) tools to automate the testing process. This ensures that tests are run consistently and regularly.
 - Cover Edge Cases: Test edge cases and unexpected inputs to ensure your code handles them gracefully.
 - Keep Tests Independent: Ensure that tests are independent of each other. This makes it easier to run tests in parallel and isolate issues.
 
Tools for Code Testing
Several tools can help streamline the testing process:
unittestandpytest: Popular testing frameworks for Python.testingPackage: Built-in testing framework for GoLang.mockito: A mocking framework for Python that helps in testing code with dependencies.testify: A testing framework for GoLang that provides additional assertions and mocking capabilities.JUnit: A popular testing framework for Java.MochaandJest: Popular testing frameworks for JavaScript.
Integrating Testing into Your Workflow
Integrating testing into your development workflow ensures that it remains a priority and is consistently applied. Here are some tips:
- Test-Driven Development (TDD): Adopt TDD to write tests before writing code. This helps in designing code that is testable and reliable.
 - Code Reviews: Include testing in your code review process. Ensure that tests are written for new features and that existing tests are updated as needed.
 - Continuous Integration (CI): Use CI tools to automate the testing process. This ensures that tests are run consistently and regularly.
 - Regular Testing: Make testing a regular part of your development process. Run tests frequently to catch issues early.
 
Example: Testing a Python Package
Let's test the math_utils package with two modules: arithmetic.py and geometry.py.
- 
Directory Structure:
math_utils/ __init__.py arithmetic.py geometry.py tests/ __init__.py test_arithmetic.py test_geometry.py - 
test_arithmetic.py:
import unittest from math_utils.arithmetic import add, subtract class TestArithmetic(unittest.TestCase): def test_add(self): self.assertEqual(add(5, 3), 8) self.assertEqual(add(-1, 1), 0) self.assertEqual(add(-1, -1), -2) def test_subtract(self): self.assertEqual(subtract(10, 4), 6) self.assertEqual(subtract(-1, 1), -2) self.assertEqual(subtract(-1, -1), 0) if __name__ == '__main__': unittest.main() - 
test_geometry.py:
import unittest from math_utils.geometry import circle_area, rectangle_area class TestGeometry(unittest.TestCase): def test_circle_area(self): self.assertAlmostEqual(circle_area(5), 78.53975) self.assertAlmostEqual(circle_area(0), 0) self.assertAlmostEqual(circle_area(-5), 78.53975) # Edge case def test_rectangle_area(self): self.assertEqual(rectangle_area(4, 6), 24) self.assertEqual(rectangle_area(0, 6), 0) self.assertEqual(rectangle_area(-4, 6), 0) # Edge case if __name__ == '__main__': unittest.main() - 
Run Tests:
python -m unittest discover -s tests 
By following these best practices and utilizing the right tools, you can create comprehensive and effective tests for your code. This will not only improve the reliability and maintainability of your codebase but also make it easier for others to understand and contribute to your projects.