Python SDLC Stages and Models Guide
Python SDLC Stages and Models Guide
The Software Development Life Cycle (SDLC) is a structured process followed during the
development of software applications. It is independent of the programming language used,
including Python. However, Python's flexibility and tools make implementing SDLC stages
efficient. Below is an outline of SDLC stages in the context of Python development:
1. Planning
2. Requirement Analysis
3. Design
● Create high-level and detailed software designs, including architecture, database schema,
and UI/UX designs.
● Python Tools:
○ UML Tools like PyUML for creating design diagrams.
○ Libraries like SQLAlchemy for designing database schemas.
● Deliverables: Design documents, wireframes, and database schema.
4. Implementation (Coding)
5. Testing
6. Deployment
7. Maintenance
● Monitor and update the software to fix bugs, improve performance, or add features.
● Python Tools:
○ logging library for application monitoring.
○ APScheduler or Celery for scheduling maintenance tasks.
○ pip-tools for managing dependencies.
● Deliverables: Patches, updates, and documentation.
● Agile SDLC in Python: Python's readability and modularity align well with Agile
methodologies for iterative development.
● Documentation: Use tools like Sphinx to generate clear project documentation.
● Code Quality: Tools like flake8, pylint, and black ensure adherence to Python best
practices.
By following the SDLC in Python, developers can ensure efficient, high-quality software
development that meets user requirements and industry standards.
2. SDLC Models
The Software Development Life Cycle (SDLC) Models are frameworks that guide the
development process of software. Python, being versatile, supports the implementation of
various SDLC models. Below is an overview of popular SDLC models and how they apply to
Python projects:
1. Waterfall Model
● Description: Sequential process where each phase must be completed before moving to
the next.
● When to Use: For projects with well-defined and unchanging requirements.
● Python in Waterfall:
○ Tools like Jupyter Notebooks or Google Docs can document requirements and
designs.
○ Python's structured testing libraries (e.g., unittest) ensure proper testing at the
testing phase.
○ Deployment handled by tools like Fabric or Docker.
2. Agile Model
3. Iterative Model
● Description: A variation of the Waterfall model with a strong focus on testing at every
stage.
● When to Use: For high-reliability systems (e.g., healthcare or defense).
● Python in V-Model:
○ Automate unit tests with unittest.
○ Perform system and integration tests with selenium or pytest.
○ Use mock to simulate components during validation.
5. Spiral Model
7. DevOps Model
Each model aligns well with Python's simplicity, readability, and rich ecosystem of libraries and
tools. Selecting the right SDLC model depends on project needs and constraints.
3. Waterfall vs Agile
Python's versatility makes it suitable for both Waterfall and Agile methodologies. Here's a
comparison of the two methodologies in the context of Python development:
1. Process Flow
Waterfall
● Linear and Sequential: Each phase (e.g., planning, design, development, testing) is
completed before moving to the next.
● Rigid Structure: Changes after a phase is completed are costly and time-consuming.
Agile
● Waterfall: Python's simplicity and readability help ensure the design and implementation
follow a predetermined plan without rework.
● Agile: Python’s dynamic nature and rich library ecosystem enable quick prototyping and
iterative updates.
2. Development Speed
Waterfall
Agile
● Faster development due to parallel phases (e.g., coding and testing occur in the same
sprint).
● Continuous feedback speeds up improvements.
3. Flexibility
Waterfall
Agile
● Highly flexible: Requirements can evolve, and priorities can shift mid-project.
● Ideal for projects with uncertain or changing requirements.
Python's Role:
● Agile workflows leverage Python's dynamic typing and ability to handle changes quickly,
while Waterfall benefits from Python's robustness for well-defined tasks.
4. Team Collaboration
Waterfall
Agile
5. Testing Approach
Waterfall
Agile
6. Deployment
Waterfall
Agile
7. Use Cases
Waterfall
Agile
● Use Waterfall: If the project has fixed requirements, minimal risk of change, and strict
deadlines.
● Use Agile: If the project requires rapid iterations, frequent feedback, and continuous
improvement.
Python’s readability, extensive libraries, and tools like pytest, Flask, Docker, and GitHub make
it an excellent choice for both methodologies.
The Software Testing Life Cycle (STLC) is a systematic process followed to ensure the quality
of software. Python, with its vast ecosystem of libraries and tools, provides robust support for
implementing all stages of the STLC. Here's how Python integrates with the STLC:
1. Requirement Analysis
2. Test Planning
● Objective: Define the test strategy, test plan, and resource allocation.
● Activities:
○ Identify tools, frameworks, and timelines.
○ Allocate roles and responsibilities to the testing team.
○ Estimate testing effort and costs.
● Python Tools:
○ Jira-Python API: Manage test plans and tasks.
○ Python-based automation tools like Selenium for planning automation efforts.
5. Test Execution
6. Test Closure
1. Readability: Python's simple syntax allows for clear test scripts and documentation.
2. Automation-Friendly: Python integrates seamlessly with automation frameworks like
Selenium, Appium, and Robot Framework.
3. Rich Ecosystem:
○ Testing libraries: pytest, unittest, doctest.
○ Performance testing: Locust, Taurus.
○ Security testing: Bandit, OWASP ZAP API.
4. Cross-Platform: Python works across Windows, macOS, and Linux, ensuring consistent
testing environments.
Test Case Create and automate test cases unittest, pytest, Faker, mock
Development
Test Execution Execute tests and log defects pytest, Selenium, Jira API
Requirement Gathering and Analysis is the initial phase of the Software Development Life
Cycle (SDLC) where the functional, non-functional, and business requirements are collected,
analyzed, and documented. Python can facilitate this phase with its powerful libraries and tools.
1. Gathering Requirements
This involves collecting information from stakeholders through interviews, surveys, and
document analysis.
● Activities:
○ Conduct interviews or meetings with stakeholders.
○ Use questionnaires or surveys to gather user expectations.
○ Analyze existing systems and documentation.
● Python Tools and Libraries:
○ OpenPyXL or Pandas: To parse and analyze Excel sheets or tabular data
containing requirements.
○ NLTK (Natural Language Toolkit): For processing textual data from interviews or
survey results.
○ SpeechRecognition: To convert spoken requirements into text from recorded
meetings or discussions.
Example:
import speech_recognition as sr
recognizer = [Link]()
with [Link]('meeting_audio.wav') as source:
audio = [Link](source)
text = recognizer.recognize_google(audio)
print("Transcribed Text: ", text)
2. Organizing Requirements
This step involves structuring the collected data for clarity and traceability.
● Activities:
○ Categorize requirements as functional, non-functional, or business.
○ Prioritize requirements based on stakeholder needs.
● Python Tools and Libraries:
○ Pandas: To create and manage requirement traceability matrices.
○ Matplotlib or Seaborn: To visualize priority or category-based distributions.
Example:
import pandas as pd
data = {
"Requirement": ["Login", "Data Encryption", "Generate
Reports"],
"Category": ["Functional", "Non-Functional", "Functional"],
"Priority": ["High", "Medium", "Low"]
}
df = [Link](data)
print(df)
3. Analyzing Requirements
This involves assessing the feasibility, completeness, and clarity of the requirements.
● Activities:
○ Identify ambiguities or conflicts in requirements.
○ Check the technical feasibility of each requirement.
○ Validate requirements against business goals.
● Python Tools and Libraries:
○ spaCy or TextBlob: For analyzing textual requirements to identify ambiguities.
○ NumPy: For performing feasibility calculations on numerical data.
Example:
4. Documenting Requirements
● Activities:
○ Create Software Requirement Specification (SRS) documents.
○ Include use cases, diagrams, and acceptance criteria.
● Python Tools and Libraries:
○ docx: To automate the creation of SRS documents.
○ matplotlib or Graphviz: To generate visualizations like flowcharts or UML
diagrams.
Example:
doc = Document()
doc.add_heading("Software Requirement Specification (SRS)",
level=1)
doc.add_paragraph("1. Introduction")
doc.add_paragraph("The system will provide a secure and
user-friendly experience.")
[Link]("[Link]")
5. Managing Requirements
● Activities:
○ Manage requirement changes.
○ Maintain a history of modifications.
● Python Tools and Libraries:
○ SQLite: To store and track requirement versions.
○ GitPython: For version control of requirement documents.
Example:
import sqlite3
conn = [Link]('[Link]')
cursor = [Link]()
[Link]('''
CREATE TABLE IF NOT EXISTS requirements (
id INTEGER PRIMARY KEY,
description TEXT,
category TEXT,
priority TEXT
)
''')
[Link]('''
INSERT INTO requirements (description, category, priority)
VALUES ('Login Feature', 'Functional', 'High')
''')
[Link]()
[Link]()
1. Automation: Python can automate repetitive tasks, like transcription, analysis, and report
generation.
2. Data Processing: Libraries like Pandas and NumPy streamline data manipulation.
3. Visualization: Tools like Matplotlib and Seaborn provide insights through graphs.
4. Integration: Python can interface with APIs and tools like Jira or Trello for efficient
tracking.
By leveraging Python’s tools and libraries, the requirement gathering and analysis phase
becomes more efficient, organized, and adaptable, ensuring clear and actionable project
deliverables.
Module 2 - Introduction to Manual Testing
1. Introduction to Software Testing
Software testing in Python is a critical part of the software development process that ensures the
quality and reliability of your code. Python provides a variety of tools and frameworks to support
both manual and automated testing. Here's an introduction to key concepts and testing
approaches in Python:
1. Types of Testing
Example:
import unittest
class TestAddition([Link]):
def test_add(self):
[Link](add(1, 2), 3)
if __name__ == "__main__":
[Link]()
Example:
def add(a, b):
return a + b
def test_add():
assert add(1, 2) == 3
● nose2: A testing framework that extends unittest with additional features, including
test discovery and plugins.
● doctest: Checks if the documentation examples in Python docstrings are correct by
running them as tests.
● In TDD, you write tests before writing the code that satisfies them.
● This approach encourages small, incremental changes and helps ensure that code meets
specifications from the outset.
● Example cycle:
1. Write a test.
2. Run the test (it should fail).
3. Write just enough code to pass the test.
4. Refactor code and rerun tests.
4. Mocking
● Mocking helps isolate parts of the code that interact with external systems (like
databases, APIs) during testing.
● Python’s [Link] module allows for creating mock objects.
Example:
from [Link] import MagicMock
def get_user_data(api):
return api.fetch_data()
mock_api = MagicMock()
mock_api.fetch_data.return_value = {'name': 'John Doe'}
assert get_user_data(mock_api) == {'name': 'John Doe'}
5. Test Coverage
● Test coverage measures how much of the code is tested by your test cases.
● Tools like [Link] can help track this metric and generate reports.
Example command:
coverage run -m unittest discover
coverage report
● Tools like Travis CI, GitHub Actions, or Jenkins can be integrated with your testing
suite to automatically run tests on every code push or pull request.
● This ensures that code quality is maintained over time.
Conclusion
Software testing is a fundamental practice to ensure the correctness, reliability, and quality of
your Python applications. By leveraging the power of Python testing libraries such as
unittest, pytest, and nose2, along with best practices like TDD, mocking, and test
coverage, you can significantly reduce the risk of defects and improve the maintainability of
your code.
2. Role of a Tester
The role of a tester is crucial in the software development process, as they are responsible for
ensuring that the software meets the required standards of quality, functionality, and performance
before it is released. Testers help identify bugs, usability issues, and areas where the software can
be improved. Here's a breakdown of a tester's role:
1. Test Planning
● Test Case Development: Based on the requirements, a tester creates test cases that
describe specific scenarios to verify the software's functionality.
● Test Data Preparation: Prepare the necessary data for testing, including valid, invalid,
boundary, and edge cases.
● Test Coverage: Ensure that the tests cover all aspects of the system, including positive
and negative scenarios.
3. Test Execution
● Manual Testing: Perform testing manually, interacting with the system directly to verify
that it behaves as expected. This includes UI testing, functional testing, and exploratory
testing.
● Automated Testing: Write and execute automated test scripts to validate functionality,
especially for repetitive tasks like regression testing or load testing.
● Regression Testing: Re-run existing test cases whenever there is a change in the software
(e.g., after new features are added or bugs are fixed) to ensure that existing functionality
is not broken.
● Defect Reporting: When a defect or bug is identified, the tester logs it with clear and
detailed steps to reproduce, along with the expected vs. actual behavior.
● Bug Tracking: Use bug tracking tools (e.g., Jira, Bugzilla) to manage and track defects
and ensure they are resolved.
● Test Results Reporting: Provide detailed reports on the testing results, including the
number of passed and failed test cases, severity of defects, and test coverage.
5. Collaboration
7. Performance Testing
● Load and Stress Testing: Test how the system behaves under heavy loads or stress to
ensure that it performs well under expected user traffic.
● Scalability Testing: Ensure that the system can handle increased usage and data over
time.
● Usability Testing: Ensure that the software is user-friendly and intuitive. Testers often
perform manual testing on the user interface to check if it aligns with the design and user
expectations.
● Accessibility Testing: Verify that the software is accessible to users with disabilities
(e.g., through screen readers or keyboard navigation).
● Automated Test Scripts: Create and maintain automated tests to speed up the testing
process, especially for repetitive tasks.
● Tool Usage: Familiarity with test automation tools like Selenium, Appium, or
TestComplete helps testers build robust automated test suites.
10. Continuous Improvement
● Test Process Improvement: Review and refine the testing processes to make them more
efficient and effective. This can involve adopting new tools, practices, or methodologies
like Agile or DevOps.
● Learning and Upskilling: Stay updated with new testing techniques, tools, and industry
trends to improve the overall quality of testing and contribute to the growth of the team.
● Analytical Skills: Ability to analyze requirements and identify potential issues or edge
cases.
● Attention to Detail: Spotting inconsistencies, discrepancies, or flaws in functionality.
● Communication Skills: Writing clear test cases, defect reports, and test results, and
effectively communicating with stakeholders.
● Technical Proficiency: Knowledge of programming languages (e.g., Python, Java) for
automation and understanding the technical architecture of the software.
● Knowledge of Testing Tools: Familiarity with testing frameworks (e.g., Selenium, JUnit,
TestNG), bug tracking tools (e.g., Jira), and CI/CD systems.
● Problem-Solving: Ability to identify, isolate, and troubleshoot issues in complex
software systems.
Conclusion:
A tester plays an essential role in ensuring the quality of software by performing comprehensive
testing activities, reporting defects, and working closely with developers and stakeholders. They
are responsible for preventing defects, verifying functionality, and ensuring the system works as
intended for end users. Testers help improve software reliability, usability, and performance,
ultimately contributing to the success of a product.
3. What is defect, Quality, expected result = actual result
In the context of software testing, the terms defect, quality, and expected result = actual result
have specific meanings. Here's how these concepts relate to Python testing:
1. Defect
● Definition: A defect is an issue or bug in the software where it does not behave as
expected or fails to meet the specified requirements. It can arise from incorrect code,
miscommunication in requirements, or unexpected interactions between different
components of the system.
● In Python Testing: During Python testing (using libraries like unittest, pytest,
etc.), a defect is often identified when the actual result of a test case doesn't match the
expected result.
Example:
def add(a, b):
return a + b
def test_add():
assert add(1, 2) == 3 # This passes, no defect
2. Quality
● Definition: Quality in software refers to how well the software meets the requirements,
user needs, and industry standards. It involves the correctness, performance, security,
usability, and maintainability of the software.
● In Python Testing: Quality is measured through successful tests and by ensuring that the
software behaves correctly, is easy to use, performs efficiently, and is free of defects.
High-quality code often results from comprehensive unit tests, integration tests, and
thorough bug fixing.
○ Python tools like pytest can help ensure quality by running tests efficiently and
allowing for assertions that check various conditions in the software.
Example:
def multiply(a, b):
return a * b
def test_multiply():
assert multiply(2, 3) == 6 # This helps ensure the function
works correctly
assert multiply(-2, 3) == -6 # Checks negative numbers
● Definition: In software testing, the expected result is what you anticipate the software
will do under certain conditions. The actual result is what the software actually does
when executed. A defect occurs if the expected result does not match the actual result.
● In Python Testing: When writing tests in Python, you define the expected result and
compare it with the actual result generated by the function. If they match, the test passes;
if they don't, the test fails and a defect is identified.
Example:
def subtract(a, b):
return a - b
def test_subtract():
expected_result = 5
actual_result = subtract(10, 5)
def test_subtract():
expected_result = 5
actual_result = subtract(10, 5)
Summary:
● Defect: When the actual result does not match the expected result.
● Quality: The overall condition of the software, typically validated through testing to
ensure correctness, performance, security, and user experience.
● Expected Result = Actual Result: In testing, this is the goal. When the expected and
actual results match, the software works as expected, indicating no defects. If they don't
match, it points to a defect that needs to be addressed.
In Python, tools like unittest, pytest, and assertions help automate this process to ensure
that the software functions as expected and meets quality standards.
4. Seven Principles of Testing
The Seven Principles of Testing are fundamental guidelines that help ensure effective and
efficient software testing. These principles apply regardless of the programming language used,
including Python. Here's an overview of the Seven Principles of Testing with examples from
Python testing:
● Principle: Testing can only show the presence of defects, not their absence. No matter
how many tests you run, you cannot prove that there are no defects; you can only detect
defects that are present.
● In Python: When running tests, if they pass, it shows that the code works correctly for
the tested scenarios, but it doesn't guarantee that there are no defects in other untested
parts of the code.
Example:
def multiply(a, b):
return a * b
def test_multiply():
assert multiply(2, 3) == 6 # No defect found in this test
case
# Even though this test passes, there could be defects in other
parts of the software.
Example:
def add(a, b):
return a + b
def test_add():
assert add(0, 0) == 0 # Testing edge case (zero)
assert add(-1, 1) == 0 # Testing negative and positive
number
# Testing all combinations of a and b is impractical
3. Early Testing
● Principle: The earlier testing begins, the cheaper it is to fix defects. Ideally, testing
should start as soon as the first piece of code is available, even before the final code is
completed.
● In Python: By practicing Test-Driven Development (TDD), tests are written before
code, allowing early detection of defects and ensuring that the software meets
requirements from the start.
Example (TDD):
# Step 1: Write a failing test first
def test_add():
assert add(1, 2) == 3 # This will fail if 'add' is not
implemented yet
4. Defects Clustering
● Principle: Defects tend to be clustered in certain areas of the software, such as complex
logic or new code. A few modules may have the majority of defects.
● In Python: Focus more intensive testing on high-risk areas, such as newly developed
features or complex parts of the codebase.
Example:
def complex_function(x, y):
return x ** y # Complex calculation
def test_complex_function():
assert complex_function(2, 3) == 8 # Focus testing on more
complex parts
● Principle: The approach to testing depends on the context, such as the software's
intended use, development methodology, and environment. Testing for a web application
differs from testing for embedded systems.
● In Python: Choose testing approaches based on the type of software you are working on
(e.g., web apps, APIs, or machine learning models). For example, pytest may be great for
unit tests in Python, but Selenium is used for testing web applications.
def test_login():
driver = [Link]()
[Link]("[Link]
assert [Link] == "Login Page"
[Link]()
● Principle: Just because no errors are detected during testing doesn't mean the software is
functioning correctly. It could still fail under certain conditions that have not been tested.
● In Python: If all tests pass, it only means the code works for the specific cases tested.
Additional testing is needed to ensure comprehensive coverage, and manual tests may
still uncover issues not addressed by automated tests.
Example:
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
def test_divide():
assert divide(6, 2) == 3 # Test case passes
# Absence of errors doesn't prove the code is correct for
all edge cases
assert divide(6, 0) # Raises ValueError, but no tests for
negative numbers or large inputs
7. Random Testing
● Principle: Random testing involves providing random inputs to the software and
observing its behavior. This can sometimes uncover defects that are not immediately
obvious.
● In Python: Tools like random or fuzz testing frameworks can be used to randomly test
different input combinations to help discover defects.
def random_test():
a = [Link](1, 100)
b = [Link](1, 100)
assert add(a, b) == a + b # Random values for input
1. Testing shows the presence of defects: Testing can only identify defects, not prove their
absence.
2. Exhaustive testing is impossible: It's impossible to test every scenario, so focus on
critical paths and edge cases.
3. Early testing: Start testing early in the development process to detect defects sooner.
4. Defects clustering: Most defects tend to be concentrated in specific areas of the software.
5. Testing is context-dependent: The approach to testing should vary depending on the
software's type and context.
6. Absence of errors is not proof of correctness: Just because no errors are found doesn't
mean the software is working correctly in all cases.
7. Random testing: Random inputs can sometimes reveal defects that might not be
identified by regular test cases.
By applying these principles to your Python testing efforts, you can improve your testing
processes and ensure more reliable, high-quality software.
5. Verification vs Validation
In software testing, verification and validation are two distinct processes that help ensure the
software meets its requirements and performs as expected. Both are crucial to software quality,
and while they overlap in some ways, they serve different purposes. Here's a breakdown of the
two concepts in the context of Python testing:
1. Verification
● Definition: Verification is the process of evaluating whether the software meets the
specified requirements and design specifications. It ensures that the system is built
according to the intended design and that the code performs its intended functions
correctly in terms of logic and structure.
● Key Question: Are we building the product right?
● In Python Testing: Verification involves checking if the software's behavior aligns with
the technical specifications, such as running unit tests, checking for code correctness, and
ensuring that the code follows the defined structure.
● Example:
Writing unit tests in Python to ensure that individual functions behave as expected:
def add(a, b):
return a + b
def test_add():
assert add(2, 3) == 5 # This is a verification test for
correctness of the 'add' function
○ Ensuring that the code follows the design principles, for example, checking if the
functions or methods are implemented according to the design document or
specification.
2. Validation
● Definition: Validation is the process of ensuring that the software meets the business
needs and the requirements of the end-users. It checks if the software fulfills its intended
purpose and if it works in real-world scenarios.
● Key Question: Are we building the right product?
● In Python Testing: Validation involves testing the system as a whole, ensuring that the
software behaves correctly in real-world conditions. This might include integration tests,
acceptance tests, and manual or automated testing to simulate user behavior and confirm
that the system meets the user's needs.
● Example:
Running integration tests or acceptance tests to validate that the Python application as a whole
works correctly in a real-world context:
def test_system_integration():
# Simulate a real-world scenario, such as user login or data
retrieval
user = login_user('user@[Link]', 'password123')
assert user is not None # Validating that the login
functionality meets user expectations
def test_acceptance():
# Verify end-to-end flow of a user registering and
submitting a form
assert form_submission('user@[Link]', 'password123') ==
'Success'
Key Differences Between Verification and Validation:
Definition Ensuring the software is built correctly Ensuring the software meets business
based on specifications. needs and user expectations.
Focus Focuses on the correctness of the code and Focuses on the software's functionality
design. from the user's perspective.
Stage in Performed during development, often early. Performed after development, before or
Development after deployment.
Method Code reviews, unit testing, static analysis, User acceptance testing (UAT), system
inspection. testing, integration testing.
Objective To ensure the product is being built To ensure the product is solving the right
correctly. problem and meeting user needs.
Examples Unit tests, code reviews, static analysis, User acceptance testing, system testing,
integration checks. end-to-end testing.
Examples in Python:
Verification Example (Checking if the implementation is correct):
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
def test_divide():
assert divide(6, 3) == 2 # Verify the logic is correct
(Verification)
assert divide(6, 0) # This would raise ValueError, which is
expected behavior
Validation Example (Ensuring the software meets user needs):
def test_user_login():
user = login_user('user@[Link]', 'password123')
assert user is not None # Validate that user login works
from the user's perspective
def test_registration_form():
response = submit_registration_form('user@[Link]',
'password123', 'John Doe')
assert response == "Success" # Validate that registration
meets user expectations
Summary:
● Verification is about ensuring that the software was built correctly according to the
design specifications and coding standards. It involves activities like unit testing, code
reviews, and static analysis.
● Validation is about ensuring that the software meets the business requirements and works
as expected from the end-user’s perspective. It includes activities like user acceptance
testing, system testing, and real-world scenario testing.
Both verification and validation are crucial for ensuring that a software product is both
functional and reliable, especially in Python-based applications where automated testing is a
powerful tool for both activities.
6. Flow of Manual Testing and Automation Testing
Both manual testing and automation testing follow systematic processes to ensure that
software functions as expected. Here's a breakdown of the flow for each testing approach,
specifically in the context of Python.
Manual testing involves human testers executing test cases without the assistance of automated
tools. It is primarily used for exploratory, usability, or ad-hoc testing. The flow is as follows:
Automation testing involves using scripts and tools to automatically execute test cases, often
saving time and ensuring repeatability. The flow is as follows:
def test_login():
driver = [Link]()
[Link]("[Link]
driver.find_element_by_id("username").send_keys("user@[Link]
m")
driver.find_element_by_id("password").send_keys("password123")
driver.find_element_by_id("login_button").click()
assert [Link] == "Dashboard" # Validate login success
[Link]()
Test Design Test cases written and executed Test scripts written and executed
manually. automatically.
Test Execution Time-consuming and may require Faster, especially for repetitive tasks.
Time repetition.
Flexibility More flexible for exploratory and Best for repetitive, regression, and
ad-hoc testing. performance testing.
Cost High cost in terms of time and effort Initial high cost for script creation but low
for repetitive tasks. cost for subsequent runs.
Maintenance Test cases need to be manually Test scripts need maintenance as the
updated. application changes.
Conclusion:
● Manual Testing is suitable for smaller, exploratory tests or scenarios where human
intuition is needed. It’s slower but useful for scenarios where automation would be too
costly or unnecessary.
● Automation Testing is ideal for repetitive, large-scale, and regression tests. It’s faster,
more scalable, and helps with continuous integration, but it requires an upfront
investment in creating test scripts and maintaining them as the software evolves.
For Python, automation tools like pytest, Selenium, and unittest are commonly used
for streamlining the testing process, while manual testing is used for specific cases where human
involvement is critical.
Module 3 - Types of Testing
1. Testing techniques
a) Reviews and Types
1. Testing Techniques
Testing techniques help ensure software quality by verifying that code behaves as expected.
Some common types:
a. Unit Testing
import unittest
def add(a, b):
return a + b
class TestMathOperations([Link]):
def test_add(self):
[Link](add(2, 3), 5)
[Link](add(-1, 1), 0)
if __name__ == "__main__":
[Link]()
b. Integration Testing
c. Functional Testing
d. Regression Testing
e. Load/Performance Testing
Reviews are static testing techniques where the code is examined without execution.
Types of Reviews
● a. Informal Reviews
○ Purpose: Quick checks for obvious issues.
○ Example: Pair programming discussions.
● b. Walkthrough
○ Purpose: Explains code to a team for feedback.
○ Example: Reviewing a new algorithm with peers.
● c. Technical Reviews
○ Purpose: Conducted by technical experts.
○ Example: Reviewing architecture documents or modules.
● d. Inspection
○ Purpose: A formal process with defined roles and checklists.
○ Example: Analyzing code for compliance with standards.
a. pytest
● Versatile and widely used.
● Supports parameterized testing and fixtures.
● Example:
import pytest
@[Link]("a, b, expected", [(1, 2, 3), (-1, -1,
-2)])
def test_add(a, b, expected):
assert a + b == expected
b. unittest
c. behave
d. mock
database = MagicMock()
database.get_data.return_value = {"name": "Test"}
assert database.get_data() == {"name": "Test"}
e. nose2
Summary
White box testing involves testing the internal structures, logic, and implementation of the code.
Below are the main techniques with Python examples:
1. Statement Coverage
Example:
def check_positive(number):
if number > 0:
return "Positive"
else:
return "Not Positive"
Using a coverage tool like [Link], you can measure statement coverage:
2. Decision Coverage
● Definition: Ensures all possible branches in decision points (like if or switch) are
tested.
● Objective: Verify that each decision (true/false) is executed.
Example:
def decide(number):
if number > 0:
return "Positive"
elif number == 0:
return "Zero"
else:
return "Negative"
3. Conditional Coverage
● Definition: Ensures all possible outcomes of each condition in a decision are tested.
● Objective: Test each condition independently to achieve full coverage.
Example:
Where:
Example:
def process(number):
if number > 0:
if number % 2 == 0:
return "Positive Even"
else:
return "Positive Odd"
else:
return "Non-Positive"
1. Equivalence Partitioning
● Definition: Divides input data into equivalent partitions, where each partition is expected
to produce similar outputs. Test cases are designed to cover each partition, reducing the
number of test cases without sacrificing coverage.
● Objective: Minimize the number of test cases while ensuring all possible input scenarios
are tested.
Example:
def categorize_age(age):
if age < 18:
return "Minor"
elif 18 <= age <= 65:
return "Adult"
else:
return "Senior"
# Equivalence partitions:
# Partition 1: age < 18 (Minor)
# Partition 2: 18 <= age <= 65 (Adult)
# Partition 3: age > 65 (Senior)
● Definition: Focuses on testing the boundaries of input ranges, as errors are often found at
the boundaries rather than the middle of input ranges.
● Objective: Identify potential edge cases, particularly where input data approaches the
limits.
Example:
def check_age_range(age):
if age < 18:
return "Minor"
elif 18 <= age <= 65:
return "Adult"
else:
return "Senior"
# Boundary values:
# Boundary 1: age = 17 (just before 18)
# Boundary 2: age = 18 (start of Adult range)
# Boundary 3: age = 65 (end of Adult range)
# Boundary 4: age = 66 (just after 65)
Example:
# Decision table:
# | is_member | is_sale | Expected Discount |
# |-----------|---------|------------------|
# | True | True | 50% |
# | True | False | 10% |
# | False | True | 20% |
# | False | False | 0% |
● Definition: Tests the system's behavior based on different states and transitions. This
technique is used when the system can be in one of many states and transitions between
states occur based on certain inputs.
● Objective: Verify that state transitions occur correctly.
Example:
class TrafficLight:
def __init__(self):
[Link] = "Red"
def change(self):
if [Link] == "Red":
[Link] = "Green"
elif [Link] == "Green":
[Link] = "Yellow"
elif [Link] == "Yellow":
[Link] = "Red"
5. Error Guessing
● Definition: Involves making educated guesses about where errors might occur based on
experience or domain knowledge. Test cases are created based on these guesses.
● Objective: Identify potential error-prone areas in the application.
Example:
6. Exploratory Testing
● Definition: Involves exploring the system without predefined test cases. Testers try
different inputs and actions based on their understanding of the application and its
potential edge cases.
● Objective: Discover defects through unscripted testing based on tester intuition.
Example:
Equivalence Partitioning Divide inputs into valid and Categorizing age groups (Minor,
invalid partitions. Adult, Senior).
Boundary Value Analysis Test boundaries of input ranges. Age ranges for minors, adults, and
seniors.
Decision Table Testing Test combinations of inputs and Discount calculation based on
corresponding outputs. membership and sales.
State Transition Testing Test system behavior across Traffic light state transitions (Red,
different states and transitions. Green, Yellow).
Exploratory Testing Test the system with an open mind Manually interacting with
and flexible approach. application features.
Module 4 - STLC
1. Test Planning
In the Software Testing Life Cycle (STLC), Test Planning is a crucial phase where a detailed
approach for testing activities is outlined. During this phase, the testing objectives, strategy,
scope, resources, schedule, and deliverables are defined. When applying this to a Python project,
test planning can be tailored to the specific testing framework (such as unittest, pytest, or
nose), tools, and methods used in the project.
1. Test Objectives
● Define the testing objectives: What is the primary purpose of testing? (e.g., verifying
functionality, ensuring non-regression, performance, security, etc.)
2. Test Scope
3. Test Strategy
● Define the types of testing to be conducted (e.g., unit tests, integration tests, system tests,
acceptance tests).
● Select the testing framework (unittest, pytest, nose).
4. Resources
● Identify the resources needed for testing: hardware, software, test environments, and
tools.
● Assign roles: who will be responsible for writing tests, executing tests, and reviewing
results.
5. Test Environment
● Define the test environment setup (e.g., database, API mockups, file systems).
● Ensure that dependencies (e.g., Python version, external libraries) are managed via
[Link] or a virtual environment.
6. Test Deliverables
● List the expected deliverables, such as test cases, test scripts, test execution reports,
defect reports, and coverage reports.
7. Test Schedule
8. Risk Mitigation
● Identify any potential risks in the project and plan for their mitigation (e.g., missing tests,
inadequate test coverage).
9. Exit Criteria
● Define when the testing phase can be considered complete (e.g., all critical tests passed,
sufficient test coverage achieved).
import unittest
import my_module # Module to be tested
class TestMyModule([Link]):
def test_add_function(self):
"""Test the add function of my_module"""
result = my_module.add(3, 5)
[Link](result, 8)
def test_subtract_function(self):
"""Test the subtract function of my_module"""
result = my_module.subtract(10, 3)
[Link](result, 7)
def test_multiply_function(self):
"""Test the multiply function of my_module"""
result = my_module.multiply(4, 6)
[Link](result, 24)
def test_divide_function(self):
"""Test the divide function of my_module"""
result = my_module.divide(10, 2)
[Link](result, 5)
def test_divide_by_zero(self):
"""Test divide by zero exception"""
with [Link](ZeroDivisionError):
my_module.divide(10, 0)
# Test Execution
if __name__ == "__main__":
[Link]()
Key Components:
2. Testing Metrics
In Python testing, testing metrics are used to evaluate the effectiveness and quality of the testing
process. These metrics help identify areas that need improvement and ensure that the testing
efforts are aligned with the project’s goals. Some common testing metrics include code coverage,
test execution, defect density, and others.
Here are the key testing metrics and how you can use them in Python testing:
1. Code Coverage
● Definition: Code coverage measures the percentage of the codebase that is executed
during testing. It helps identify untested or under-tested code.
● Tools: [Link], pytest-cov, unittest
● Metrics:
○ Line Coverage: Percentage of lines of code covered by tests.
○ Branch Coverage: Percentage of code branches (if statements) covered by tests.
○ Function Coverage: Percentage of functions covered by tests.
The result will show how well the test suite covers your code. Aim for higher coverage, but keep
in mind that 100% coverage does not guarantee bug-free code.
● Definition: The amount of time taken to run the tests. It helps in identifying slow tests
and optimizing test execution.
● Tools: pytest, unittest
● Definition: The percentage of tests that pass out of the total tests executed.
● Formula: Pass Rate=(Number of Passed TestsTotal Number of Tests)×100\text{Pass
Rate} = \left( \frac{\text{Number of Passed Tests}}{\text{Total Number of Tests}}
\right) \times 100Pass Rate=(Total Number of TestsNumber of Passed Tests)×100
● Tools: pytest, unittest, nose
Example:
This will help you track how many tests pass or fail and measure the overall quality of your tests.
4. Defect Density
● Definition: The number of defects found per unit of code, often measured per thousand
lines of code (KLOC).
● Formula: Defect Density=Number of DefectsKLOC\text{Defect Density} =
\frac{\text{Number of Defects}}{\text{KLOC}}Defect Density=KLOCNumber of
Defects
● Tools: Bug tracking tools, e.g., JIRA, GitHub issues
This metric helps track the quality of the code and how effective your testing is in identifying
defects.
● Definition: The percentage of test cases that find defects or failures. This metric helps in
evaluating whether the tests are designed to identify bugs.
● Formula: Test Case Effectiveness=(Number of Defects DetectedTotal Number of Test
Cases Run)×100\text{Test Case Effectiveness} = \left( \frac{\text{Number of Defects
Detected}}{\text{Total Number of Test Cases Run}} \right) \times 100Test Case
Effectiveness=(Total Number of Test Cases RunNumber of Defects Detected)×100
If most test cases pass without detecting defects, the test suite may be inadequate.
● Definition: The rate at which defects are discovered during testing. This metric is useful
to understand how quickly defects are identified during the test process.
● Formula: Defect Discovery Rate=Defects FoundTime Taken\text{Defect Discovery
Rate} = \frac{\text{Defects Found}}{\text{Time Taken}}Defect Discovery Rate=Time
TakenDefects Found
Tracking the defect discovery rate helps you understand how quickly the quality of the product is
being assessed during testing.
● Definition: This metric tracks how often tests are executed during the software
development lifecycle.
● Tools: CI/CD tools like Jenkins, GitLab CI, CircleCI
Frequent test runs indicate an active and thorough testing process. The tests can be run after
every code commit or in scheduled intervals.
8. Test Stability
● Definition: Stability of tests refers to how consistently tests pass when the codebase
remains unchanged. Unstable tests often fail randomly due to environmental issues,
timing problems, or dependencies.
● Tools: pytest, unittest
Monitoring test stability can help identify flaky tests that need to be addressed for a more reliable
test suite.
You can combine multiple tools like pytest and [Link] to generate detailed metrics:
This will:
Conclusion:
By tracking these testing metrics, you can continuously improve your testing process and ensure
that the software is reliable, maintainable, and of high quality. These metrics can be incorporated
into your testing pipeline to gain insights into both the effectiveness of your tests and the quality
of your code.
3. Effective Test Case Writing
Effective test case writing is essential to ensure that your Python code is thoroughly tested and
that bugs or issues are detected early in the development cycle. Here are some tips and guidelines
for writing effective test cases in Python using frameworks like unittest, pytest, or nose.
● Purpose: Before writing any test case, you should clearly understand the functionality
you are testing. Review the requirements, user stories, and code specifications to identify
the expected behavior of the system.
● Test Types: Decide the type of test (unit test, integration test, functional test, etc.) based
on the requirements.
Each test case should be written in a consistent and structured way. A typical test case structure
includes the following:
● Unit Testing: Each test case should test a small, isolated unit of the program (e.g., a
single function or method). This makes it easier to track down problems when a test fails.
● Example: Testing a simple function for adding two numbers:
# Function to be tested
def add(a, b):
return a + b
class TestMathOperations([Link]):
def test_add(self):
# Test case to check the add function
result = add(3, 5)
[Link](result, 8)
if __name__ == "__main__":
[Link]()
● Edge cases are situations that might not happen often but can cause problems. These
could be:
○ Inputs at the boundary of acceptable values.
○ Empty inputs or null values.
○ Large inputs, like very large numbers or large strings.
● Example: Edge case of adding two negative numbers:
def test_add_negative_numbers(self):
result = add(-3, -5)
[Link](result, -8)
Always test how the code behaves with invalid inputs. This is crucial for ensuring robustness and
stability.
● Example: What happens if you pass strings instead of numbers:
def test_add_invalid_input(self):
with [Link](TypeError):
add("three", 5)
A good test name should describe the behavior or scenario being tested. It should be easy to
understand what the test is doing just by looking at the name.
● Example: Instead of a generic name like test_func1, use something descriptive like
test_add_function_with_valid_numbers.
● Each test case should be independent of others. A test should not rely on the success or
failure of other tests.
● Example: Don’t rely on the outcome of test_add when testing other functions. Each
test should set up its own environment.
If your code interacts with external systems like databases, APIs, or file systems, use mocking to
simulate those dependencies in your tests. This makes tests more predictable and faster.
def get_user_from_db(user_id):
# Imagine this connects to a database to fetch a user
pass
class TestDatabase([Link]):
@patch('module_name.get_user_from_db')
def test_get_user(self, mock_get_user):
mock_get_user.return_value = {'id': 1, 'name': 'John
Doe'}
result = get_user_from_db(1)
[Link](result, {'id': 1, 'name': 'John Doe'})
● Assertions are used to compare the expected and actual outputs. Common assertions
include:
○ assertEqual(a, b): Verifies that a is equal to b.
○ assertNotEqual(a, b): Verifies that a is not equal to b.
○ assertTrue(a): Verifies that a is True.
○ assertFalse(a): Verifies that a is False.
○ assertRaises(Exception): Verifies that an exception is raised.
● Example:
[Link](add(1, 2), 3)
[Link](ValueError, function_that_raises_value_error)
● Run your tests frequently, especially when making code changes, to ensure that the
system still works as expected. Consider using Continuous Integration (CI) tools like
Jenkins, GitLab CI, or Travis CI to automate test runs.
11. Maintain Good Test Coverage
● Ensure that your test suite covers a wide range of scenarios, including typical use cases,
edge cases, and error cases. Tools like [Link] can help track test coverage.
If performance is critical, write tests that check how the application behaves under load, such as
how fast a function executes or how it handles large datasets.
● Write simple, clear tests that are easy to maintain. Avoid complex logic in your test
cases—tests should validate behavior, not contain logic that could be tested elsewhere.
Example:
# Instead of testing the implementation in a complicated way,
just test the expected behavior
def test_add_simple(self):
[Link](add(1, 2), 3)
By following these practices, you’ll be able to write effective, reliable, and maintainable test
cases for your Python code, improving the overall quality of your software.
4. Test Plan
Creating a Test Plan in Python involves documenting the strategy, scope, resources, and
schedule for testing a software application. The Test Plan provides a comprehensive guide to the
testing activities for a particular project and outlines the objectives, approach, and scope of
testing.
Here's an overview of how to create a test plan in Python, including its key components and
structure.
1. Introduction
2. Test Objectives
3. Test Scope
4. Test Criteria
○ Entry Criteria
○ Exit Criteria
5. Testing Strategy
6. Testing Tools
7. Test Deliverables
8. Test Schedule
9. Resources and Responsibilities
10.Risk and Contingencies
11.Approvals
1. Introduction
● Purpose: This section outlines the purpose of the Test Plan. It should define what will be
tested and why it is important.
Example:
This Test Plan covers the testing activities for the Python project "User Registration
System." The objective is to ensure the application works as intended and meets the
quality standards for functionality, performance, and security.
2. Test Objectives
● What is being tested?: Identify the key areas of the software that need testing
(e.g., business logic, database interactions, user interface, etc.).
Example:
The objectives of this test plan are:
○ Validate the correctness of user registration functionality.
○ Ensure input validation is handled properly.
○ Test the handling of edge cases for user input.
○ Validate the interaction with the database for storing user data.
3. Test Scope
4. Test Criteria
● Entry Criteria: Conditions that must be met before testing can begin.
● Exit Criteria: Conditions that must be met before testing can be considered
complete.
Example:
Entry Criteria:
○ Test environment setup completed (e.g., database and server setup).
○ Test cases written and reviewed.
● Exit Criteria:
○ All critical test cases have passed.
○ No high-priority defects remain unresolved.
5. Testing Strategy
● Test Types: Specify the types of testing to be performed (unit tests, integration tests,
system tests, acceptance tests).
● Test Levels: Define the test levels (e.g., unit testing, integration testing,
system testing, regression testing).
Example:
Test Types:
○ Unit Testing: Each function (e.g., email validation, password
strength checker) will be tested independently using unittest
framework.
○ Integration Testing: Integration between the registration form and
the database will be tested.
○ System Testing: End-to-end registration flow will be tested.
○ Regression Testing: Ensure new changes have not broken existing
functionality.
6. Testing Tools
● Identify the tools you will use for writing and executing the tests.
Example:
○ Test Framework: unittest (Python’s built-in testing framework)
○ Test Coverage Tool: [Link]
○ Mocking Tool: [Link] to mock external dependencies
such as database or API calls
○ CI/CD Tool: Jenkins or GitLab for automated test execution
7. Test Deliverables
● List the items that will be delivered at the end of the testing process.
Example:
○ Test cases document
○ Test execution reports
○ Defect reports
○ Test coverage reports
8. Test Schedule
● Provide a timeline for testing activities. Include test execution start and end
dates, as well as deadlines for reviewing and delivering test results.
Example:
○ Test Case Development: January 5 - January 10
○ Test Execution: January 11 - January 15
○ Defect Fixing: January 16 - January 20
○ Final Test Review: January 21
11. Approvals
● List the individuals or teams responsible for approving the test plan and the
test execution results.
Example:
○ Test Plan Approval: John Doe (Test Manager)
○ Test Results Approval: Jane Smith (Project Manager)
Here’s a sample Test Plan document for a hypothetical Python project, "User Registration
System."
Conclusion
A Test Plan is a critical document that sets the foundation for all testing activities and ensures
that the testing process is organized, efficient, and aligned with the project’s objectives. By
clearly outlining the test objectives, scope, strategies, and tools, a good Test Plan helps in
achieving comprehensive test coverage and ultimately delivering a high-quality product.
Creating a Test Plan Documentation for a Python project involves detailed planning for all
aspects of the testing process. The documentation provides a structured approach to testing,
outlining the goals, scope, resources, timelines, and procedures. Here's a step-by-step guide on
how to prepare Test Plan Documentation specifically for a Python-based project.
1. Introduction
○ Purpose: Describe the purpose of the test plan and what the testing process will
achieve.
○ Scope: Define what features of the project will be tested and what will not be
tested.
○ References: Provide any references such as design documents, requirements, or
standards that the test plan follows.
Example:
## 1. Introduction
### Scope
This plan covers the validation of the registration
functionality, including input validations and database
interaction. Non-functional testing, such as UI/UX design, is
out of scope for this plan.
### References
- User Registration System - Requirements Document
- Python Project Setup - [Link]
Example:
## 2. Test Objectives
The main objectives of this testing effort are:
- Ensure that the user registration form correctly validates
email, username, and password fields.
- Validate that the system handles edge cases (empty fields,
invalid email format, etc.).
- Verify that user data is correctly inserted into the database.
- Ensure the system gracefully handles invalid inputs (e.g.,
invalid email, weak passwords).
Example:
## 3. Test Scope
### In Scope
- Functional tests for registration form submission.
- Input field validation (email, password, and username).
- Interaction with the database to store user information.
Example:
## 4. Test Criteria
Example:
## 5. Testing Strategy
Example:
## 6. Testing Tools
Example:
## 7. Test Deliverables
- Test cases document.
- Test execution logs.
- Defect reports and resolution logs.
- Test coverage reports.
Example:
## 8. Test Schedule
Example:
## 9. Resources and Responsibilities
Example:
## 10. Risk and Contingencies
**Risks**:
- Unavailability of the test environment or dependencies.
- Unresolved issues from the previous versions affecting new
tests.
**Contingencies**:
- Backup environments will be set up to mitigate delays.
- Additional resources will be allocated to handle critical
defects.
11.Approvals
○ Identify the individuals or teams that must approve the Test Plan and the final test
results.
Example:
## 11. Approvals
Here is how the final Test Plan might look in Markdown format for a Python project:
## 1. Introduction
The purpose of this Test Plan is to outline the testing
strategy, objectives, scope, and resources required for the
"User Registration System" written in Python. This plan will
ensure that the registration process functions correctly, input
data is validated, and user data is accurately stored in the
database.
### Scope
This plan covers the validation of the registration
functionality, including input validations and database
interaction. Non-functional testing, such as UI/UX design, is
out of scope for this plan.
### References
- User Registration System - Requirements Document
- Python Project Setup - [Link]
## 2. Test Objectives
The main objectives of this testing effort are:
- Ensure that the user registration form correctly validates
email, username, and password fields.
- Validate that the system handles edge cases (empty fields,
invalid email format, etc.).
- Verify that user data is correctly inserted into the database.
- Ensure the system gracefully handles invalid inputs (e.g.,
invalid email, weak passwords).
## 3. Test Scope
### In Scope
- Functional tests for registration form submission.
- Input field validation (email, password, and username).
- Interaction with the database to store user information.
## 4. Test Criteria
## 5. Testing Strategy
## 6. Testing Tools
- **Test Framework**: `unittest`
- **Mocking**: `[Link]`
- **Test Coverage**: `[Link]`
- **CI/CD**: Jenkins
## 7. Test Deliverables
- Test cases document.
- Test execution logs.
- Defect logs.
- Test coverage reports.
## 8. Test Schedule
- **Test Case Development**: January 5 - January 10
- **Test Execution**: January 11 - January 15
- **Defect Fixing**: January 16 - January 20
- **Final Test Review**: January 21
## 11. Approvals
- **Test Plan Approval**: John Doe
- **Test Results Approval**: Jane Smith
This documentation will provide a comprehensive plan for your Python testing efforts, ensuring
that all aspects of the project are thoroughly tested.
6. Test Development
Test Development in Python refers to the process of creating and executing test cases to
validate the functionality, performance, and reliability of your Python application. Here's a
structured approach to test development in Python:
The first step in test development is selecting the appropriate testing framework for your project.
Common frameworks for Python include:
For most projects, pytest is a popular choice because of its simplicity and advanced features.
Test cases are written to ensure the different parts of your application function as expected. Each
test case should check one unit of functionality, such as a function or method.
Example:
import unittest
# Example function to test
def add(a, b):
return a + b
class TestMathOperations([Link]):
def test_add_positive_numbers(self):
result = add(1, 2)
[Link](result, 3)
def test_add_negative_numbers(self):
result = add(-1, -1)
[Link](result, -2)
def test_add_mixed_numbers(self):
result = add(-1, 2)
[Link](result, 1)
if __name__ == '__main__':
[Link]()
In this example, three tests are written for the add function:
def test_add_negative_numbers():
assert add(-1, -1) == -2
def test_add_mixed_numbers():
assert add(-1, 2) == 1
pytest automatically detects the test functions by looking for functions that start with test_
and can handle assertions without needing assertEqual like in unittest.
3. Running Tests
For unittest, you can run tests using the command:
python -m unittest discover
For pytest, you can run tests using the command:
pytest
Both frameworks will automatically detect all test cases in your project and run them.
Mocking is essential when testing code that depends on external systems or APIs (such as
databases or web services). Python provides several ways to mock external dependencies.
Example:
import unittest
from [Link] import patch
def get_user_data(user_id):
# Simulating a call to an external API
response = external_api_call(user_id)
return response
class TestExternalApi([Link]):
@patch('module_name.external_api_call')
def test_get_user_data(self, mock_external_api):
# Mocking the external API call response
mock_external_api.return_value = {'user_id': 1, 'name':
'John Doe'}
result = get_user_data(1)
if __name__ == '__main__':
[Link]()
In this example, external_api_call is mocked to avoid making real API calls during
testing.
5. Test Coverage
Test coverage measures how much of the code is covered by tests. Python tools like [Link]
can be used to analyze your test coverage.
Running [Link]:
Install the tool:
pip install coverage
Run tests with coverage:
coverage run -m unittest discover
Report coverage:
coverage report
View coverage details in HTML:
coverage html
6. Parameterized Tests
Sometimes, you want to run the same test with multiple inputs. This is where parameterized tests
come in handy. In pytest, you can use @[Link] to pass different
inputs to a test function.
Example:
import pytest
@[Link]("a, b, expected", [(1, 2, 3), (-1, -1,
-2), (-1, 2, 1)])
def test_add(a, b, expected):
assert add(a, b) == expected
This approach allows testing the add function with multiple sets of inputs and expected results.
7. Test Automation and CI/CD
Once the tests are developed, automating them ensures they are run frequently to catch any
issues early. Integrate your tests into a CI/CD pipeline using tools like GitHub Actions,
Jenkins, or GitLab CI.
● Write small, focused tests: Each test should verify only one thing.
● Name tests clearly: Test method names should describe the behavior being tested.
● Use setup/teardown methods: setUp() and tearDown() in unittest to prepare
and clean up the test environment.
● Avoid writing tests for trivial code: Focus on testing the complex, critical parts of your
application.
● Use assertions effectively: Ensure the assertions are meaningful and reflect the expected
behavior.
Conclusion
Suppose you have a simple Flask application that provides an API for adding two numbers:
app = Flask(__name__)
@[Link]('/add', methods=['GET'])
def add_numbers():
a = int([Link]('a'))
b = int([Link]('b'))
return jsonify({'result': a + b})
if __name__ == '__main__':
[Link](debug=True)
Test Scenario 2: Test the Addition API with a Positive and Negative Number
1. Clear Structure: Test scenarios provide a clear structure for testing and ensure that all
parts of an application are covered.
2. Reusability: Scenarios can be reused across different tests, especially when testing
similar functionality.
3. Risk Mitigation: By defining scenarios, you ensure that potential edge cases and
negative scenarios are covered.
4. Improved Collaboration: Test scenarios help in better communication among testers,
developers, and stakeholders since they clearly define what will be tested.
Conclusion
Test scenarios are an important tool in the software testing process, as they help outline the
expected behavior of features in a broader sense. By breaking down each functionality into
scenarios, you can ensure comprehensive test coverage and validate your application’s behavior
under different conditions. Test scenarios also help maintain clarity and consistency across
testing efforts, especially for large or complex applications.
Test Case Design in Python refers to the process of creating individual test cases to verify that a
specific piece of functionality in your Python code behaves as expected. It is part of a structured
testing process to ensure that the code is reliable, bug-free, and meets the specified requirements.
Let's say you have the following Python function, which adds two numbers:
import unittest
class TestMathOperations([Link]):
def test_add_two_positive_numbers(self):
result = add(3, 5)
[Link](result, 8)
if __name__ == '__main__':
[Link]()
class TestMathOperations([Link]):
def test_add_two_negative_numbers(self):
result = add(-2, -3)
[Link](result, -5)
if __name__ == '__main__':
[Link]()
def test_add_positive_and_negative(self):
result = add(5, -3)
[Link](result, 2)
if __name__ == '__main__':
[Link]()
class TestMathOperations([Link]):
def test_add_zero(self):
result = add(0, 5)
[Link](result, 5)
if __name__ == '__main__':
[Link]()
class TestMathOperations([Link]):
def test_add_non_integer_input(self):
with [Link](TypeError):
add("a", 5)
if __name__ == '__main__':
[Link]()
Test Case Design Best Practices
1. Cover Different Input Types: Test with a variety of valid and invalid inputs to ensure
robust error handling.
2. Test Boundary Conditions: Test edge cases, such as adding very large or very small
numbers, or zero values.
3. Be Specific and Focused: Each test case should test only one specific behavior or edge
case.
4. Use Descriptive Test Case Names: The test case ID and description should clearly
indicate the test's purpose.
5. Maintain Consistency: Ensure that test cases follow a consistent structure for easier
maintenance and readability.
6. Automate Test Execution: Use a test framework like unittest, pytest, or others to
automate test execution and reporting.
7. Ensure Isolated Tests: Each test case should be independent, with no reliance on other
tests.
Conclusion
Test case design is a critical process to ensure the quality and reliability of your Python code. By
creating well-structured, focused test cases, you can verify that your application behaves as
expected across a variety of input scenarios, including edge cases and error conditions.
Automated testing frameworks like unittest and pytest make it easier to implement and
execute test cases, contributing to better code quality and faster development cycles.
9. Levels of testing
In Python, as in other software development environments, levels of testing refer to the different
stages or granularities at which software is tested. These levels help ensure that various aspects
of the system are verified independently and as part of the overall solution. The levels of testing
generally include:
1. Unit Testing
● Purpose: Test individual units or components of the code (e.g., functions or methods).
● Scope: Focuses on a single function or method.
● Objective: Ensure that each small piece of the application works as intended.
● Tools in Python:
○ unittest
○ pytest
○ nose
import unittest
class TestMathOperations([Link]):
def test_add(self):
[Link](add(3, 4), 7)
if __name__ == '__main__':
[Link]()
2. Integration Testing
● Purpose: Test the interaction between multiple units or components to ensure they work
together.
● Scope: Involves integrating components, such as checking if a function interacts correctly
with external systems or databases.
● Objective: Identify issues related to the integration of different components.
● Tools in Python:
○ pytest (can be used for integration testing by configuring fixtures)
○ unittest (can also be used for integration testing when test suites involve
multiple modules)
If you have a function that integrates with an external API, you can mock the API call and test
how it integrates with the rest of your system.
import unittest
from [Link] import patch
class TestAPIIntegration([Link]):
@patch('[Link]')
def test_api_integration(self, mock_get):
# Mock the API response
mock_get.return_value.json.return_value = {'data':
'value'}
3. System Testing
● Purpose: Test the complete system as a whole, ensuring that all components work
together as expected.
● Scope: The entire application is tested in an environment similar to production.
● Objective: Ensure the software meets the requirements and behaves as expected in a full
deployment scenario.
● Tools in Python:
○ pytest
○ unittest
○ Selenium (for testing web applications)
○ Pytest-Django (for Django applications)
You might simulate user interaction in a web app using Selenium to verify that the app behaves
as expected in a real-world scenario.
def test_login():
driver = [Link]()
[Link]("[Link]
username_input = driver.find_element_by_id("username")
password_input = driver.find_element_by_id("password")
username_input.send_keys("user")
password_input.send_keys("password")
login_button = driver.find_element_by_id("login_button")
login_button.click()
4. Acceptance Testing
● Purpose: Validate that the software satisfies the business requirements and is ready for
release.
● Scope: Focuses on ensuring the system meets user needs and specifications.
● Objective: Ensure the application is ready for deployment from a user's perspective,
confirming that all requirements are met.
● Tools in Python:
○ pytest (especially with fixtures)
○ Behave (a BDD framework for Python)
Acceptance tests are often written in a BDD (Behavior Driven Development) style, where test
scenarios are written in plain language.
[Link].find_element_by_id('username').send_keys('user')
[Link].find_element_by_id('password').send_keys('passwo
rd')
5. Regression Testing
● Purpose: Ensure that new changes, such as bug fixes or feature additions, have not
introduced new issues or broken existing functionality.
● Scope: Focuses on verifying that previously working features still function as expected.
● Objective: Prevent regression issues where new changes negatively affect existing
features.
● Tools in Python:
○ pytest
○ unittest
○ Selenium (for web-based regression testing)
Running previously defined unit tests after adding a new feature to ensure no functionality is
broken.
$ pytest test_math_operations.py
6. Performance Testing
def test_function_performance(benchmark):
result = benchmark(add, 3, 5)
assert result == 8
7. Smoke Testing
● Purpose: Verify that the most critical functionalities of the system work as expected.
● Scope: A subset of tests, usually covering the main features.
● Objective: Perform an initial check to ensure the system is stable enough for more
detailed testing.
● Tools in Python:
○ pytest
○ unittest
Testing the core functionality, such as ensuring the main page of a web application loads
successfully.
def test_homepage():
response = [Link]('/')
assert response.status_code == 200
● Purpose: Alpha testing is performed by the development team, and beta testing is done
by a small group of real users outside the development team.
● Scope: Both tests focus on identifying bugs before the software is released to a larger
audience.
● Objective: Get real-world feedback and identify issues that were missed during previous
testing levels.
● Tools in Python:
○ Beta and alpha testing might not directly involve tools but are more about
feedback and manual testing.
Conclusion
The different levels of testing in Python help ensure comprehensive verification and validation of
software. Each level focuses on different aspects of the system, from individual components (unit
testing) to the entire system's behavior in a production-like environment (system testing). Using
the appropriate tools and techniques at each level allows you to build high-quality, reliable
Python applications.
In Python, testing types refer to the different approaches used to verify and validate the behavior
of the software during its development. Each testing type focuses on different aspects of the
application to ensure its quality, performance, and correctness. Here are the key testing types in
Python:
1. Unit Testing
● Purpose: Verify the functionality of individual units (e.g., functions, methods, classes) of
the code in isolation.
● Scope: Tests a small, isolated piece of functionality, such as a single function or method.
● Tools:
○ unittest
○ pytest
○ nose
class TestMathOperations([Link]):
def test_add(self):
[Link](add(3, 4), 7)
if __name__ == '__main__':
[Link]()
2. Integration Testing
● Purpose: Test the interaction between different modules, services, or components of the
system to ensure they work together as expected.
● Scope: Focuses on the flow of data and control between components/modules.
● Tools:
○ pytest
○ unittest
○ requests (for testing APIs)
○ Mocking libraries like [Link]
Example: Testing a function that fetches data from an external API and processes it.
import unittest
from [Link] import patch
class TestAPIIntegration([Link]):
@patch('[Link]')
def test_integration_with_api(self, mock_get):
mock_get.return_value.json.return_value = {'data':
'value'}
result = fetch_data_from_api()
[Link](result, {'data': 'value'})
if __name__ == '__main__':
[Link]()
3. System Testing
● Purpose: Test the complete system in a production-like environment to ensure the entire
application functions correctly as a whole.
● Scope: Includes the entire system, including integrated components and external services.
● Tools:
○ pytest
○ unittest
○ Selenium (for web applications)
○ Appium (for mobile applications)
Example: Testing a web application's login page by simulating user interaction with the browser
using Selenium.
from selenium import webdriver
def test_login():
driver = [Link]()
[Link]("[Link]
driver.find_element_by_id("username").send_keys("user")
driver.find_element_by_id("password").send_keys("password")
driver.find_element_by_id("login_button").click()
assert "Welcome" in driver.page_source
[Link]()
4. Acceptance Testing
● Purpose: Ensure the application meets the business requirements and is ready for release.
Acceptance tests are often written from the perspective of the end user.
● Scope: Verifies that the system fulfills user stories or specific business requirements.
● Tools:
○ Behave (for Behavior-Driven Development - BDD)
○ pytest
Example: Writing scenarios in natural language (e.g., using Gherkin syntax) to define the
expected behavior of a feature.
Feature: Login functionality
[Link].find_element_by_id('username').send_keys('user')
[Link].find_element_by_id('password').send_keys('passwo
rd')
5. Regression Testing
● Purpose: Ensure that new changes (bug fixes, enhancements) have not negatively
impacted existing features.
● Scope: Tests all critical functionality to confirm it is still working after updates.
● Tools:
○ pytest
○ unittest
○ Selenium (for web apps)
Example: Running previously written tests after a code change to ensure existing features are
unaffected.
$ pytest
6. Performance Testing
● Purpose: Test the application's performance, scalability, and responsiveness under load.
● Scope: Evaluate how the system performs under different conditions, including high
traffic and large data volumes.
● Tools:
○ Locust (load testing)
○ Pytest-benchmark
7. Smoke Testing
● Purpose: Conduct a preliminary check to verify that the most critical features of the
application are working.
● Scope: A quick, basic check to ensure the build is stable enough for more detailed
testing.
● Tools:
○ unittest
○ pytest
8. Stress Testing
● Purpose: Test the application's ability to handle extreme conditions, such as an unusually
high load or unexpected inputs.
● Scope: Ensure the system can handle stressful or unexpected conditions gracefully.
● Tools:
○ Locust
○ Apache JMeter (for more complex scenarios)
● Example: Testing how the system performs under a sudden, large spike in traffic.
9. Usability Testing
● Purpose: Evaluate how user-friendly and intuitive the application is for its end users.
● Scope: Focuses on user experience and interface design.
● Tools:
○ Selenium (to automate UI testing)
○ PyAutoGUI (for automating GUI-based interactions)
● Example: Testing the ease with which a user can navigate a form and submit data.
● Purpose: Identify and address vulnerabilities, ensuring the application is secure and
resistant to attacks.
● Scope: Tests against common security threats like SQL injection, cross-site scripting
(XSS), and more.
● Tools:
○ OWASP ZAP (for web security testing)
○ Bandit (Python security testing tool)
Example: Testing for SQL injection vulnerabilities by sending specially crafted requests.
import requests
response =
[Link]('[Link]
234')
● Purpose: Alpha testing is done by the development team, while beta testing is performed
by a limited set of end users outside the development team.
● Scope: Gather feedback and identify defects that were missed in earlier testing phases.
● Tools: Not necessarily testing tools, but user feedback and bug reporting tools.
● Example: Releasing a beta version of an app to a select group of users for feedback.
Conclusion
Each testing type plays a crucial role in verifying the behavior, functionality, and quality of your
application. By using the appropriate testing types for different stages of development, you can
ensure that your Python application is robust, secure, and ready for production. You can use tools
like unittest, pytest, Selenium, Behave, and others to implement these testing types
efficiently.
A Requirement Traceability Matrix (RTM) is a document used to ensure that all requirements
of a project are being tested and verified. It links requirements to test cases and helps in tracking
the coverage of requirements throughout the testing process.
In Python, you can implement an RTM by maintaining a mapping between the requirements and
their associated test cases. Below is a general approach for creating an RTM in Python, which
can be customized to your project.
1. Define the Requirements and Test Cases
You can structure the data using Python dictionaries or data structures like lists and pandas
DataFrames to maintain and track the mapping between requirements and test cases.
# RTM Structure
rtm = [
{"Requirement ID": "REQ-001", "Requirement Description":
"User must be able to log in", "Test Case ID": "TC-001", "Test
Case Description": "Test login functionality", "Status":
"Passed"},
{"Requirement ID": "REQ-002", "Requirement Description":
"System should send an email on registration", "Test Case ID":
"TC-002", "Test Case Description": "Test email sending after
registration", "Status": "Failed"},
{"Requirement ID": "REQ-003", "Requirement Description":
"User profile should be editable", "Test Case ID": "TC-003",
"Test Case Description": "Test profile editing", "Status": "Not
Executed"},
{"Requirement ID": "REQ-004", "Requirement Description":
"System must handle 1000 concurrent users", "Test Case ID":
"TC-004", "Test Case Description": "Test load capacity of the
system", "Status": "Passed"},
]
# Example usage
print_rtm(rtm)
● RTM Data Structure: The RTM is represented as a list of dictionaries where each
dictionary holds the details of one requirement and its associated test case(s).
● print_rtm Function: This function is used to print the RTM in a tabular format.
● update_test_status Function: This function allows you to update the test case
status for a particular requirement. It takes the Requirement ID and the new status as
input.
4. Output Example
When running the above code, the output will look like this:
If the requirements and test cases are large, or if you want to perform more advanced operations
like filtering or exporting the RTM to CSV, using pandas can be very effective.
import pandas as pd
# Create a DataFrame
df = [Link](data)
Conclusion
Using a Requirement Traceability Matrix (RTM) in Python helps maintain the integrity of the
project requirements and ensures that all functional and non-functional requirements are tested
appropriately. Python tools like unittest, pytest, and pandas make it easy to build,
update, and maintain an RTM, enhancing the efficiency of the testing process.
Test Closure refers to the final phase of the software testing lifecycle where testing activities are
concluded, and results are documented. It involves closing the testing process by ensuring that all
necessary activities are completed and the testing objectives are met. In Python, you can
automate parts of the test closure process, such as generating test reports, ensuring that all test
cases have been executed, and summarizing the testing activities.
Make sure that all planned test cases have been executed, and the test results are documented.
This involves:
In Python, you can use tools like unittest, pytest, or nose to generate these reports.
You can generate a test report in a variety of formats, such as HTML, JUnit, or JSON.
Once the tests are complete, you can open the HTML report using Python (if necessary):
import webbrowser
def generate_test_summary(test_results):
passed = len([result for result in test_results if
result['status'] == 'Passed'])
failed = len([result for result in test_results if
result['status'] == 'Failed'])
not_executed = len([result for result in test_results if
result['status'] == 'Not Executed'])
summary = {
"Total Test Cases": len(test_results),
"Passed": passed,
"Failed": failed,
"Not Executed": not_executed,
"Pass Percentage": (passed / len(test_results)) * 100 if
test_results else 0
}
return summary
# Generate summary
summary = generate_test_summary(test_results)
print("Test Closure Summary:")
print(f"Total Test Cases: {summary['Total Test Cases']}")
print(f"Passed: {summary['Passed']}")
print(f"Failed: {summary['Failed']}")
print(f"Not Executed: {summary['Not Executed']}")
print(f"Pass Percentage: {summary['Pass Percentage']}%")
4. Test Deliverables
def generate_test_closure_report(test_results):
passed = len([result for result in test_results if
result['status'] == 'Passed'])
failed = len([result for result in test_results if
result['status'] == 'Failed'])
not_executed = len([result for result in test_results if
result['status'] == 'Not Executed'])
report = f"""
Test Closure Report
--------------------
Total Test Cases: {len(test_results)}
Passed: {passed}
Failed: {failed}
Not Executed: {not_executed}
Final Conclusion:
Test execution completed. {passed} test(s) passed, {failed}
test(s) failed, and {not_executed} test(s) were not executed.
"""
return report
5. Post-Test Analysis
After the tests are completed and the closure report is generated, the testing team can:
● Analyze Test Results: Evaluate which areas need improvement or have caused defects.
● Learn from the Failures: Identify common causes for failure and suggest improvements
to avoid future issues.
● Verify Unresolved Issues: Make sure unresolved defects are documented and tracked for
future releases.
Conclusion
Test Closure in Python involves the completion of test execution, generation of test reports,
summarizing results, documenting defects, and delivering the final reports. Using tools like
pytest for test execution and pandas or simple Python functions for result processing and
report generation, you can automate and manage the test closure process efficiently.
4o mini
Module 5 - Test Case Execution
1. Test Environment
To execute test cases in a test environment using Python, you typically follow these steps:
● Install Dependencies: Ensure all required libraries are installed in the test environment.
● Configure the Environment: Set up configurations (e.g., environment variables,
database connections, or test-specific settings).
● Choose a Test Framework: Popular options include unittest, pytest, and nose2.
import pytest
def test_add_positive_numbers():
assert add(3, 5) == 8
def test_add_negative_numbers():
assert add(-3, -5) == -8
3. Run Tests
pytest test_file.py
4. Analyze Results
test_file.py .. [100%]
● Use tools like tox or CI/CD pipelines to run tests in a dedicated test environment.
6. Advanced Testing
● Mocking: Use libraries like [Link] to simulate external dependencies.
2. Test Execution
When executing test cases in Python, the results are typically categorized as Passed, Failed, or
Blocked. Managing these outcomes and handling test case dependencies can be streamlined with
test frameworks and custom logic.
● Passed: The test case completes successfully, and the actual outcome matches the
expected outcome.
● Failed: The test case completes, but the actual outcome does not match the expected
outcome.
● Blocked: The test case cannot be executed due to unmet dependencies (e.g., missing data,
environment setup issues, or failed prerequisite tests).
Using pytest
Example Code:
import pytest
# Mock function under test
def multiply(a, b):
if a is None or b is None:
raise ValueError("Inputs cannot be None")
return a * b
# Test cases
@[Link]()
def test_setup_environment():
# Simulating environment setup
assert True # Simulate successful setup
@[Link](depends=["test_setup_environment"])
def test_multiply_valid_inputs():
assert multiply(2, 3) == 6
@[Link](depends=["test_setup_environment"])
def test_multiply_invalid_inputs():
with [Link](ValueError):
multiply(None, 3)
@[Link](depends=["test_setup_environment"])
def test_multiply_blocked():
[Link]("Dependency unmet, skipping test")
Run Command:
pytest test_file.py
3. Handling Dependencies
Use the pytest-dependency plugin for managing dependent test cases.
pip install pytest-dependency
● Example:
○ test_multiply_valid_inputs and
test_multiply_invalid_inputs depend on
test_setup_environment.
○ If test_setup_environment fails, the dependent tests are marked as
Blocked.
5. Enhancing Reporting
b) Use fixtures in pytest to set up shared resources for dependent tests.
Example:
@[Link](scope="module")
def setup_environment():
return "Environment Ready"
def test_uses_environment(setup_environment):
1. Defect Tracking
Defect tracking involves maintaining a record of all the defects identified during a project. These
defects can be logged, updated, and closed as they progress through their life cycle.
Python Implementation
# Defect Class
class Defect:
def __init__(self, defect_id, title, description, severity,
priority):
self.defect_id = defect_id
[Link] = title
[Link] = description
[Link] = severity
[Link] = priority
[Link] = STATUS_NEW
self.created_at = [Link]()
self.updated_at = self.created_at
def display(self):
print(f"Defect ID: {self.defect_id}")
print(f"Title: {[Link]}")
print(f"Description: {[Link]}")
print(f"Severity: {[Link]}")
print(f"Priority: {[Link]}")
print(f"Status: {[Link]}")
print(f"Created At: {self.created_at}")
print(f"Updated At: {self.updated_at}")
print("-" * 40)
def display_all_defects(self):
for defect in [Link]():
[Link]()
# Example Usage
if __name__ == "__main__":
# Initialize Defect Manager
manager = DefectManager()
# Create Defects
defect1 = Defect(1, "Login Issue", "User unable to login",
SEVERITY_HIGH, PRIORITY_HIGH)
defect2 = Defect(2, "UI Bug", "Button alignment issue",
SEVERITY_LOW, PRIORITY_LOW)
# Add Defects
manager.add_defect(defect1)
manager.add_defect(defect2)
# Display Defects
manager.display_all_defects()
# Update Status
manager.update_defect_status(1, STATUS_OPEN)
2. Severity
3. Priority
This implementation supports the creation, tracking, and updating of defects, ensuring a
structured approach to defect management.
Module 7 - Testing Tools
1. Testing Tools in Jira and BUG Zilla
To integrate testing tools in Jira and Bugzilla with Python, follow these steps. This involves
understanding their APIs, setting up Python environments, and writing scripts to interact with
these platforms for managing and automating bug tracking and test management.
● Both Jira and Bugzilla provide REST APIs for programmatic interaction.
● Documentation:
○ Jira REST API
○ Bugzilla REST API
# Jira credentials
jira_server = '[Link]
api_token = 'your-api-token'
email = 'your-email@[Link]'
# Connect to Jira
jira = JIRA(server=jira_server, basic_auth=(email, api_token))
import requests
# Bugzilla credentials
bugzilla_url = '[Link]
api_key = 'your-api-key'
● Run the scripts to ensure they work with your Jira and Bugzilla accounts.
● Verify that the issues/bugs created appear in their respective dashboards.
7. Automating Tests
● Integrate these scripts with your testing frameworks (e.g., unittest, pytest).
Example:
import unittest
class TestJiraIntegration([Link]):
def test_create_issue(self):
# Add code to test issue creation
pass
if __name__ == "__main__":
[Link]()
8. Best Practices
Writing test cases in Jira and Bugzilla involves defining a set of inputs, execution conditions, and
expected results to validate that a feature or functionality works as intended. Here’s how you can
create and manage test cases in these platforms.
Jira doesn’t natively support test case management, but you can use:
● Custom Issue Types: Configure Jira to use a "Test Case" issue type.
● Test Management Plugins: Tools like Zephyr for Jira or Xray enhance test case
capabilities.
Bugzilla is primarily a bug-tracking tool and doesn't have dedicated support for test cases.
However, you can manage test cases as bugs or custom fields.
Version 1.0
Severity Trivial
Creating a Test Plan in Jira and Bugzilla involves outlining the testing strategy, scope,
objectives, resources, schedule, and deliverables for a specific project or feature. Since these
tools are primarily designed for issue/bug tracking, they require some customization or plugins to
manage test plans effectively.
Jira doesn’t have a dedicated module for test plans, but you can:
Priority High
Linked Issues TEST-1 (Login Test Case), TEST-2 (Forgot Password Test Case)
Bugzilla does not natively support test plan creation, but you can:
Severity Trivial
If you’re maintaining a separate document for a test plan, it might look like this:
Section Details
4. Hands on -
a) Equivalence Partitioning
b) Boundary Value Analysis
c) State Transition Diagram
d) Decision Table
Equivalence Partitioning involves dividing input data into partitions (valid and invalid) where
all values in a partition are expected to behave similarly.
Steps:
def test_login(input_value):
valid_partition = ['user1', 'user2', 'user3'] # Example
valid users
if input_value in valid_partition:
return "Login Successful"
else:
return "Invalid User"
Jira/Bugzilla Integration:
● Jira: Use a test case template to log each partition as a separate test case and link them to
a user story.
● Bugzilla: File bugs for failed cases, marking them as valid or invalid partitions.
Boundary Value Analysis focuses on testing the edges of input ranges (e.g., minimum,
maximum, just inside/outside boundaries).
Steps:
def validate_age(age):
if 18 <= age <= 65:
return "Valid Age"
else:
return "Invalid Age"
# Test Boundary Values
boundary_values = [17, 18, 19, 64, 65, 66]
for age in boundary_values:
print(f"Input: {age}, Result: {validate_age(age)}")
Jira/Bugzilla Integration:
State Transition Testing validates different states of a system and the transitions between them.
Steps:
# Define transitions
state_transitions = {
'Logged Out': {'login': 'Logged In'},
'Logged In': {'logout': 'Logged Out', 'timeout': 'Session
Expired'},
'Session Expired': {'login': 'Logged In'}
}
Jira/Bugzilla Integration:
Decision Table Testing evaluates combinations of inputs and the corresponding system outputs.
Steps:
Jira/Bugzilla Integration:
● Jira: Use custom issue types for each rule and link cases to a decision table document.
● Bugzilla: Log failed cases with detailed input-output mappings.
# Jira credentials
jira_server = '[Link]
email = 'your-email@[Link]'
api_token = 'your-api-token'
# Connect to Jira
jira = JIRA(server=jira_server, basic_auth=(email, api_token))
# Log issue
test_result = "Boundary Test Failed for Age=66"
new_issue = jira.create_issue(project='TEST', summary='Boundary
Value Failure',
description=test_result,
issuetype={'name': 'Bug'})
print(f"Issue logged in Jira: {new_issue.key}")
# Bugzilla credentials
bugzilla_url = '[Link]
api_key = 'your-api-key'
# Log bug
bug_data = {
'product': 'WebApp',
'component': 'Login',
'summary': 'Boundary Test Failed for Age=66',
'version': '1.0',
'description': 'Age=66 failed during boundary testing.',
'severity': 'normal'
}
response = [Link](f"{bugzilla_url}/bug", json=bug_data,
params={'api_key': api_key})
print(f"Bug logged in Bugzilla: {[Link]().get('id')}")
Best Practices
[Link]
Performance testing is a type of software testing that focuses on assessing how well a system
performs under certain conditions, such as varying workloads, user traffic, or resource
constraints. It helps ensure that the system performs optimally and meets expected performance
standards.
Performance testing involves evaluating the responsiveness, scalability, stability, and speed of a
system, application, or software under load. It helps to identify potential bottlenecks and areas of
improvement by simulating different real-world scenarios. The goal is to ensure that the software
can handle a large number of users, data, and transactions without degrading performance.
1. Response Time: How quickly the system responds to requests under varying loads.
2. Throughput: The number of requests the system can handle within a given time frame.
3. Scalability: The system's ability to scale up or down based on increasing or decreasing
load.
4. Stability: How well the system maintains its performance over extended periods of time
or under sustained usage.
5. Resource Usage: The system’s usage of CPU, memory, disk, and network resources
during load.
6. Error Rates: The number of errors or failures that occur as load increases.
7. Concurrency: The ability of the system to handle multiple users or processes
simultaneously.
● Load Testing: Involves testing the system under a specified expected load, such as the
expected number of concurrent users or transactions. The goal is to verify that the system
can handle the expected load without significant performance degradation.
● Stress Testing: This is performed to determine the system's behavior under extreme or
beyond normal load. It helps to identify the breaking point or failure threshold of the
system, showing how the system behaves when pushed to its limits.
There are various performance testing tools available, including both open-source and
commercial solutions. Below are a few popular ones:
1. Apache JMeter: A widely used open-source tool for performance testing web
applications. It can simulate heavy traffic, measure response times, and analyze
performance metrics.
2. Locust: A Python-based load testing tool that allows you to define user behavior in
Python code, making it highly customizable and flexible.
3. Gatling: A powerful open-source tool designed for load testing of web applications and
services. It provides detailed reports and integrates with continuous integration systems.
4. Artillery: Another popular open-source tool for load testing web applications, APIs, and
microservices. It allows you to define scenarios in YAML format and is well-suited for
modern cloud-native applications.
5. BlazeMeter: A commercial performance testing platform built on JMeter. It allows for
the execution of large-scale performance tests with real-time monitoring and analytics.
class WebsiteUser(HttpUser):
wait_time = between(1, 5)
@task
def load_main_page(self):
[Link]("/")
@task
def load_about_page(self):
[Link]("/about")
@task
def load_contact_page(self):
[Link]("/contact")
if __name__ == "__main__":
import os
[Link]("locust")
Locust provides real-time feedback on test performance, showing response times, failure rates,
and requests per second.
In summary, performance testing is a crucial part of ensuring your system works efficiently
under real-world conditions. By using tools like Locust, you can test and fine-tune your
application to meet performance standards and user expectations.
Module 9 - Introduction to Security Testing
1. Introduction to Security Testing
a) What is security testing
b) Why it is important
c) Types of security testing
Security testing is a type of software testing that ensures that an application or system is
protected against potential threats, vulnerabilities, and attacks. It involves checking the system’s
defenses and its ability to safeguard data, resources, and users against malicious activities.
Security testing is the process of assessing the security features of a software system to identify
any vulnerabilities, weaknesses, and threats. This testing evaluates whether the system has
appropriate safeguards in place to protect data, prevent unauthorized access, and ensure the
integrity of operations.
The primary goal of security testing is to uncover vulnerabilities that could be exploited by
attackers, ensuring the system is resilient against cyber threats such as hacking, malware, data
breaches, and other malicious activities.
1. Protecting Sensitive Data: Security breaches can lead to unauthorized access to sensitive
information such as personal, financial, and business data, which could have severe
consequences, including financial loss, reputation damage, and legal penalties.
2. Ensuring Compliance: Many industries require compliance with regulations such as
GDPR, HIPAA, and PCI-DSS, which mandate the implementation of specific security
measures to protect data.
3. Preventing Attacks: Security testing helps identify and fix vulnerabilities before they are
exploited by attackers, preventing potential cyberattacks like SQL injection, cross-site
scripting (XSS), or cross-site request forgery (CSRF).
4. Maintaining Business Continuity: Ensuring security can protect the business from
downtime, loss of intellectual property, and damage to customer trust.
5. Improving System Resilience: By identifying weaknesses, security testing ensures that
the system is robust and can withstand attempts to compromise it.
There are several types of security testing, each focusing on different aspects of system security:
Python offers a variety of libraries and frameworks that can be leveraged for security testing.
Here are a few examples:
zap = ZAPv2()
target_url = '[Link]
# Start the spider to crawl the target
[Link](target_url)
2. Scapy:
○ Scapy is a Python library for network packet manipulation and analysis. It is used
in security testing for tasks like sniffing packets, crafting packets for network
penetration testing, and testing for network vulnerabilities.
target_url = '[Link]
payload = {'username': 'admin', 'password': "' OR '1'='1"}
response = [Link](target_url, data=payload)
if "Welcome" in [Link]:
print("Potential SQL Injection vulnerability found!")
4. PyCrypto:
○ This library can be used for testing cryptographic security, such as analyzing
encryption algorithms, hashing, and managing keys.
Conclusion
Security testing is a vital aspect of software development and maintenance. It helps protect
applications from malicious attacks, ensures data protection, and ensures compliance with
security standards. By using tools and frameworks like OWASP ZAP, Scapy, and PyCrypto in
Python, security professionals can perform thorough assessments and identify potential
vulnerabilities to make the system secure and resilient.
Module 10 - Introduction to Database Testing
1. Introduction to Database Testing
Database testing is a critical aspect of verifying that your application interacts correctly with its
underlying database. It involves ensuring that the data retrieval, storage, modification, and
deletion operations are working as expected, and that the database performs optimally under
various conditions. In Python, several libraries and frameworks can be used to carry out effective
database testing, especially in systems where databases play a central role.
Database testing involves validating the data integrity, consistency, and correctness of a database
after performing operations like insertion, update, deletion, and retrieval. It aims to ensure that
the database behaves as expected, adheres to business logic, and maintains the integrity of data
even after interactions from the application.
1. Data Integrity: Ensures that data in the database remains accurate, consistent, and
uncorrupted after operations.
2. Performance: Verifies that the database handles the volume of requests efficiently,
providing quick query responses.
3. Security: Ensures that sensitive data is stored and retrieved securely, preventing
unauthorized access.
4. Consistency: Confirms that operations like transactions are properly managed,
maintaining consistency in multi-user or multi-session environments.
5. Functionality: Validates that database functions like stored procedures, triggers, and
views are correctly implemented and return the expected results.
Python provides several libraries and tools for interacting with databases, executing queries, and
testing database functionality. Some commonly used libraries for database testing include:
1. SQLite:
○ A lightweight, serverless database that comes built-in with Python and is ideal for
simple database testing and prototyping.
2. SQLAlchemy:
○ A powerful ORM (Object Relational Mapper) for working with relational
databases. It supports a variety of databases (MySQL, PostgreSQL, SQLite, etc.)
and is highly flexible.
3. PyMySQL:
○ A library for connecting to MySQL databases and performing various database
operations.
4. psycopg2:
○ A popular PostgreSQL adapter for Python that allows interaction with
PostgreSQL databases.
5. unittest or pytest:
○ Python's built-in testing libraries like unittest or pytest can be used to
write automated tests that check for database functionality, using database
connectors like sqlite3 or SQLAlchemy.
Here’s an example of how you can perform basic database testing using Python and SQLite:
import sqlite3
import unittest
def setUp(self):
"""Setup the database for each test"""
[Link], [Link] = setup_database()
def test_insert_data(self):
"""Test inserting data into the database"""
[Link]('INSERT INTO users (name, age)
VALUES (?, ?)', ('Alice', 30))
[Link]()
[Link](user[1], 'Alice')
[Link](user[2], 30)
def test_update_data(self):
"""Test updating data in the database"""
[Link]('INSERT INTO users (name, age)
VALUES (?, ?)', ('Bob', 25))
[Link]()
[Link]('UPDATE users SET age=? WHERE
name=?', (26, 'Bob'))
[Link]()
[Link](user[2], 26)
def test_delete_data(self):
"""Test deleting data from the database"""
[Link]('INSERT INTO users (name, age)
VALUES (?, ?)', ('Charlie', 40))
[Link]()
[Link](user)
def tearDown(self):
"""Close the database connection after each test"""
[Link]()
If you're using a more complex database setup with SQLAlchemy, here’s how you can test
database operations:
def setUp(self):
"""Setup the database for each test"""
[Link], [Link] = setup_database()
def test_insert_user(self):
"""Test inserting a user"""
user = User(name="David", age=30)
[Link](user)
[Link]()
def tearDown(self):
"""Close the session after each test"""
[Link]()
Conclusion
Database testing ensures that your system interacts correctly with its database, maintaining data
integrity, security, and performance. By using Python libraries such as sqlite3,
SQLAlchemy, and unittest, you can automate and streamline database testing to validate
database operations and ensure the correctness of your data-driven applications.
Module 11 - Introduction to Mobile Application Testing
1. Introduction to Mobile Application Testing
a) Type of mobile platform [Android, iOS]
b) Introduction to SDK tools like adb, ddms etc
Mobile application testing ensures that an application performs as expected on mobile platforms,
providing a smooth user experience, correct functionality, and reliable performance. Python can
be used for mobile application testing by leveraging various tools, libraries, and frameworks to
automate testing processes across both Android and iOS platforms.
1. Android:
○ Developed by Google, Android is an open-source operating system for mobile
devices, with a wide variety of devices from different manufacturers running
Android.
○ Most Android apps are written in Java, Kotlin, or C++ and run on the Android
runtime (ART).
2. iOS:
○ Developed by Apple, iOS is a closed-source operating system used exclusively on
Apple devices like iPhones, iPads, and iPod Touch.
○ iOS applications are usually written in Swift or Objective-C and are run on the
iOS runtime.
Python can be used to automate testing for both Android and iOS apps by leveraging tools and
frameworks designed for mobile automation testing. The most popular ones include:
1. Appium:
○ Appium is an open-source, cross-platform mobile automation framework that
supports both Android and iOS. It allows writing tests using Python (and several
other programming languages) and can interact with native and hybrid apps.
2. Selendroid:
○ Selendroid is a test automation framework for Android devices that allows testing
Android apps. While it mainly works with Android, it is compatible with Appium
to enable cross-platform testing.
3. UIAutomator (for Android):
○ UIAutomator is a Google testing framework for Android that allows automated
testing of apps' user interfaces.
4. XCUITest (for iOS):
○ XCUITest is Apple's framework for automating UI tests on iOS applications.
Though primarily for use with Xcode, it can be accessed via Python using
bindings or third-party libraries.
5. Pytest:
○ Pytest is a testing framework that can be used with Appium and other tools for
running tests on mobile apps. It allows organizing test cases, reporting, and test
execution.
In mobile app testing, SDK (Software Development Kit) tools are essential for interacting with
the mobile device, automating tasks, and debugging the app. Some of the key SDK tools used in
mobile testing are:
desired_caps = {
"platformName": "Android",
"platformVersion": "10",
"deviceName": "Android Emulator",
"app": "/path/to/your/[Link]"
}
driver = [Link]("[Link]
desired_caps)
[Link]()
Appium is widely used for mobile application testing. Here’s an example of how to write a test
for an Android app using Appium in Python:
Mobile application testing ensures that an app delivers a smooth, secure, and bug-free experience
for users. Python, with the help of tools like Appium, ADB, and Xcode, allows developers and
testers to automate tests for both Android and iOS applications. By leveraging SDK tools and
testing frameworks, mobile app testing becomes efficient and effective, ensuring apps perform
well under various conditions.
Module 12 - Agile Model
1. Scrum
2. Sprint
3. User Story
Agile is a popular project management and software development methodology that emphasizes
flexibility, collaboration, customer feedback, and rapid delivery of small, incremental changes to
software. The Agile model consists of several frameworks, and Scrum is one of the most widely
used frameworks within Agile. Below, we will explain key concepts such as Scrum, Sprint, and
User Story, along with their application in Python development.
1. Scrum
Scrum is an Agile framework used to manage and complete complex projects. It is based on a
set of roles, events, and artifacts that allow teams to deliver working software frequently and
iteratively. Scrum emphasizes collaboration, flexibility, and transparency within the development
team and stakeholders.
● Roles:
○ Product Owner: Responsible for managing the product backlog, ensuring the
right features are prioritized.
○ Scrum Master: Facilitates Scrum processes, removes obstacles, and ensures that
the team follows Scrum practices.
○ Development Team: A cross-functional team responsible for delivering the
product increment.
● Events:
○ Sprint Planning: A meeting to define the work to be completed during the sprint.
○ Daily Scrum: A daily stand-up meeting to discuss progress, obstacles, and
upcoming work.
○ Sprint Review: A meeting at the end of the sprint to showcase completed work to
stakeholders.
○ Sprint Retrospective: A meeting to reflect on the sprint and identify
improvements for the next sprint.
● Artifacts:
○ Product Backlog: A prioritized list of all desired work for the product.
○ Sprint Backlog: The list of tasks to be completed during the sprint, taken from
the product backlog.
○ Increment: The working software delivered at the end of each sprint.
Python Example (Scrum Board): A Scrum board helps track tasks, their progress, and their
statuses during the sprint. Here’s an example of how we might structure a Scrum board in Python
using a simple dictionary to track tasks:
2. Sprint
A Sprint is a time-boxed iteration (usually 1-4 weeks) in Scrum during which a specific set of
work (items from the product backlog) is completed and turned into a deliverable increment. The
sprint starts with a Sprint Planning meeting and ends with a Sprint Review and Sprint
Retrospective.
Python Example (Simulating a Sprint): We can simulate the process of completing tasks in a
sprint using Python. Below is an example where tasks are completed within a sprint, and their
statuses are updated:
start_sprint(sprint)
print(sprint)
3. User Story
A User Story is a brief description of a feature or functionality written from the perspective of
the end user. It defines the feature's behavior, purpose, and value to the user. In Scrum, user
stories are part of the product backlog, and they are prioritized by the product owner.
● "As a user, I want to log in to my account so that I can access personalized content."
● Acceptance Criteria: Clear conditions that define when a user story is considered
complete and functioning correctly.
● Priority: How important the user story is to the product’s success.
● Story Points: A relative measure of the effort required to implement the user story (often
based on complexity or time).
Python Example (Managing User Stories): In a Python-based system, we can store and
manage user stories with attributes like title, description, and acceptance criteria. Here's how to
manage a few user stories in Python:
class UserStory:
def __init__(self, title, description, acceptance_criteria):
[Link] = title
[Link] = description
self.acceptance_criteria = acceptance_criteria
[Link] = "To Do"
def __str__(self):
return f"User Story: {[Link]}, Status:
{[Link]}"
print("\nAfter completion:")
for us in user_stories:
print(us)
Conclusion
In Agile development using Scrum, the key concepts of Scrum, Sprint, and User Story are
central to ensuring successful project management and timely delivery of features. Python can be
used to automate, manage, and track Scrum processes, including sprint backlogs, user stories,
and task completion. By adopting Agile practices and leveraging tools like Python, teams can
stay organized, responsive to changes, and focused on delivering valuable software
incrementally.
Module 13 - Regression Testing
1. What is regression testing?
2. How to prepare test cases for regression testing
Regression Testing is a type of software testing that ensures that recent changes (such as bug
fixes, new features, or updates) in the software application have not adversely affected the
existing functionality of the application. The goal is to confirm that the new code or functionality
does not introduce new bugs or break the already working parts of the software.
● Ensures stability: Ensures that updates and changes do not negatively impact existing
functionality.
● Automated Testing: Regression tests can often be automated to run quickly after every
change, making it easier to verify the stability of the application.
● Covers wide functionality: While it focuses on checking previously tested functionality,
it may also cover new features that interact with the old functionality.
Regression testing typically involves running a set of test cases that cover:
1. Core functionalities that should work even after the changes.
2. Previously reported bugs to confirm that they are fixed and haven’t reappeared.
3. New features that integrate with existing parts of the software.
Preparing test cases for regression testing is crucial to ensure comprehensive testing of the
existing application while focusing on the areas that might have been impacted by recent
changes.
● Focus on the most critical and frequently used parts of the application that are likely to be
impacted by any change (such as login, data entry, and basic navigation).
● These test cases should be reusable and should ideally cover the most basic and essential
functionality.
● Prioritize test cases based on the importance of the feature and the risk of impact. For
example, a login functionality or payment gateway should be tested more frequently than
other less critical features.
● Consider using techniques like Risk-Based Testing to focus on areas that are more likely
to break.
● A clear and consistent format for regression test cases is essential. Below is a typical
regression test case template:
TC01 Verify Login User has an 1. Open login page User is Passed/Faile
Functionality account 2. Enter valid redirected to d
credentials 3. Click the
on the login button dashboard
● Track recent changes in the application (such as bug fixes, new features, or
enhancements).
● Focus on testing areas that might have been affected by these changes. If a bug is fixed in
one area of the application, run tests on related areas as well to ensure no new issues are
introduced.
● Given that regression tests are often repeated after every change, automating these tests
can save time and resources. Python, combined with testing frameworks like PyTest or
unittest, allows for the automation of regression test cases.
import unittest
def test_valid_login(self):
username = "test_user"
password = "valid_password"
[Link]([Link](username, password))
def test_invalid_login(self):
username = "test_user"
password = "invalid_password"
[Link]([Link](username, password))
● Ensure that your regression test cases are tested across multiple environments, such as
different browsers, devices, operating systems, or different versions of the application.
● Ensure that the core workflows and data integrity remain intact. If there are data-driven
operations in your application (such as adding, updating, or deleting records), make sure
to test these scenarios.
8. Version Control
● Keep track of the version of the test cases to ensure that the right test cases are being
executed after each version release. Keep your test cases updated according to the new
changes in the code.
● Document the results of each regression test carefully. For any failure, provide details
about the issue and steps for resolution. This helps in debugging and tracking known
issues.
Let’s take an example of a regression test case where we verify the login functionality.
● Functionality: Ensure that the login functionality is not broken after recent changes.
● Test Steps:
1. Open the login page.
2. Enter valid login credentials (username and password).
3. Click the login button.
4. Verify that the user is redirected to the dashboard page.
import pytest
# Sample function simulating login process
def login(username, password):
valid_username = "test_user"
valid_password = "password123"
if username == valid_username and password ==
valid_password:
return True
return False
@[Link]
def test_invalid_login():
assert login("test_user", "wrong_password") == False, "Login
should fail with incorrect password"
@[Link]
def test_empty_username():
assert login("", "password123") == False, "Login should fail
with empty username"
@[Link]
def test_empty_password():
assert login("test_user", "") == False, "Login should fail
with empty password"
To run the regression tests using PyTest, you can use the following command:
This will run only the regression tests marked with @[Link].
Conclusion
Regression testing is crucial to ensure that changes in the software do not introduce new defects
and that the existing functionality is preserved. By preparing structured and prioritized test cases,
automating tests using tools like PyTest or unittest, and focusing on key areas of the application,
you can make regression testing more efficient and effective. With Python, you can streamline
the testing process and quickly verify that the software remains stable after each change.
Module 14 - Test Case
1. How to write good quality test case
Writing good quality test cases in Python is essential for ensuring that your code is reliable,
maintainable, and efficient. A well-written test case will make it easier to detect defects early in
the development process, reduce maintenance overhead, and increase the overall quality of the
software.
Here are the key practices for writing high-quality test cases in Python:
A well-structured test case will help you and others quickly understand what is being tested, how
it is tested, and what the expected results are.
Test case names should be clear, descriptive, and follow a consistent naming convention. They
should convey what the test is verifying and under what condition.
For example:
def test_valid_login_with_correct_credentials():
# Test for successful login with valid credentials
pass
def test_invalid_login_with_incorrect_password():
# Test for unsuccessful login with incorrect password
pass
Each test case should be independent of others. It should not rely on the outcome of another test.
This helps avoid cascading failures and makes it easier to identify issues when they arise.
Example:
def test_add_two_numbers():
assert add(1, 2) == 3
def test_subtract_two_numbers():
assert subtract(5, 3) == 2
Each test case should be isolated, and changes in one test should not affect other tests.
A good test case follows the Arrange-Act-Assert (AAA) pattern, which provides a clear
structure for setting up the test, performing the action, and verifying the outcome:
Example:
def test_addition_of_two_numbers():
# Arrange
a = 5
b = 3
expected_result = 8
# Act
result = add(a, b)
# Assert
assert result == expected_result, f"Expected
{expected_result}, but got {result}"
This pattern helps organize the test and makes it easier to understand the flow.
The assert statement is used to verify that the actual result matches the expected result. A
meaningful assertion helps identify the problem quickly when the test fails.
● Good assertion:
● Bad assertion:
assert result
The first example gives a clear explanation in case of failure, making it easier to debug. The
second example is vague and does not provide helpful information.
Ensure that you cover both positive and negative test cases:
● Positive Test Case: Verifies that the system behaves as expected under valid conditions.
● Negative Test Case: Verifies that the system handles invalid input or unexpected
conditions correctly.
Example:
def test_valid_email():
assert is_valid_email("user@[Link]") is True
def test_invalid_email():
assert is_valid_email("user@.com") is False
By testing both valid and invalid scenarios, you ensure that your code can handle all possible
situations.
Make sure you test edge cases, which are often where bugs occur. Edge cases may involve
testing extreme values, large inputs, or boundary conditions.
Example:
def test_large_number_addition():
large_number = 10**12
assert add(large_number, 1) == large_number + 1
def test_empty_string():
assert reverse_string("") == ""
Edge cases might not always be obvious, so carefully consider the inputs that might break the
system.
Each test case should focus on one specific behavior. Avoid writing tests that are too broad or try
to test multiple functionalities in one test case.
For example:
def test_login_with_correct_credentials():
assert login("user", "password") == "Login successful"
def test_login_with_incorrect_credentials():
assert login("user", "wrong_password") == "Login failed"
In this example, each test focuses on a single condition (valid or invalid login), making the test
clear and easy to maintain.
If your code interacts with external systems like databases, APIs, or third-party services, use
mocks and stubs to simulate these interactions during testing. This avoids hitting real external
resources and allows tests to run independently of external systems.
def test_get_user_data():
with patch('module_name.get_data_from_api') as mock_api:
mock_api.return_value = {"id": 1, "name": "John Doe"}
result = get_user_data(1)
assert result == {"id": 1, "name": "John Doe"}
Ensure your tests are maintainable by keeping them simple and clean. If the code changes, the
test should be easy to update. Write test cases that will be reusable in the future and don’t require
frequent changes when the code evolves.
● Avoid hardcoding values: Use variables or constants to make the test more flexible.
● Keep test data organized and externalized where appropriate.
Example:
def test_login():
result = login(valid_user_data["username"],
valid_user_data["password"])
assert result == "Login successful"
class TestMathOperations([Link]):
def test_addition(self):
# Arrange
a = 10
b = 5
expected_result = 15
# Act
result = a + b
# Assert
[Link](result, expected_result)
def test_division(self):
# Arrange
a = 10
b = 2
expected_result = 5
# Act
result = a / b
# Assert
[Link](result, expected_result)
def test_division_by_zero(self):
# Arrange
a = 10
b = 0
By applying these best practices, you can create high-quality test cases that improve the
reliability and maintainability of your software.
Module 15 - Defect
1. How to write a good quality defect?
Writing a good quality defect report (also called a bug report) is crucial for effective
communication between developers, testers, and other stakeholders. A well-written defect report
helps in quickly understanding the issue, reproducing it, and resolving it efficiently.
Here’s how to write a good quality defect report in Python (or for any software project):
1. Defect ID
○ A unique identifier for the defect. This helps in tracking and referencing the issue
across tools or conversations.
Example:
Example:
Example:
Example:
○ Steps to Reproduce:
■ Open the application login page.
■ Enter username: test_user.
■ Enter password: password123.
■ Click the "Login" button.
■ Observe the 500 error displayed.
5. Expected Result
○ What the behavior should be if the software is working correctly. This helps in
determining if the defect is caused by incorrect behavior.
Example:
Example:
○ Actual Result: "Instead of logging in, the page displays a 500 Internal Server
Error."
7. Severity/Priority
○ Severity: The impact of the defect on the system's functionality. Is it a major issue
or a minor issue?
■ High: The defect causes a critical issue (e.g., system crash, data loss).
■ Medium: The defect affects non-critical features.
■ Low: The defect is minor, such as UI glitches.
○ Priority: The urgency of fixing the defect based on its severity and business
needs.
■ High: Needs to be fixed immediately (e.g., blocking major functionality).
■ Medium: Should be fixed soon but does not block core functionality.
■ Low: Can be fixed later.
Example:
○ Severity: High
○ Priority: High
8. Environment/Configuration
○ The environment in which the defect was found. This can include details about
the platform, browser, operating system, software version, or hardware where the
defect occurs.
Example:
○ Environment:
■ OS: Windows 10
■ Browser: Google Chrome (version 91)
■ Application Version: 1.0.0
■ Database: MySQL 8.0
9. Attachments (Logs, Screenshots, or Videos)
○ Include any relevant files that can help in reproducing the defect or diagnosing the
issue. This can include:
■ Screenshots of the error message.
■ Log files or stack traces.
■ Videos showing the issue happening in real-time.
Example:
Example:
○ Possible Root Cause: "The issue may be related to an exception occurring in the
database connection during the authentication process."
11.Additional Notes/Comments
○ Any extra information that may help resolve the defect. This could include:
■ Related issues (e.g., previously reported bugs).
■ Potential workarounds.
■ Information about the frequency or consistency of the defect.
Example:
○ Additional Notes: "This error only occurs when logging in with valid credentials,
not for invalid credentials."
Description: When a user enters valid login credentials (username: test_user, password:
password123), the login page throws a 500 Internal Server Error and prevents the user from
logging in. This issue seems to occur only for valid credentials, and the error message does not
provide any useful information.
Steps to Reproduce:
1. Open the application login page.
2. Enter username: test_user.
3. Enter password: password123.
4. Click the "Login" button.
5. Observe the 500 error displayed.
Expected Result: The user should be redirected to the dashboard upon successful login.
Actual Result: Instead of logging in, the page displays a 500 Internal Server Error.
Severity: High
Priority: High
Environment:
● OS: Windows 10
● Browser: Google Chrome (version 91)
● Application Version: 1.0.0
● Database: MySQL 8.0
Attachments:
Possible Root Cause: The issue may be related to a failed database connection during the
authentication process, causing a server-side exception.
Additional Notes:
● This issue does not occur for invalid login attempts, where the user is correctly shown an
error message for invalid credentials.
● Error appears intermittently for valid logins.
Tips for Writing Good Defect Reports:
● Be Clear and Concise: The defect report should be easy to read and understand. Avoid
jargon or ambiguous statements.
● Reproducibility: Ensure that anyone reading the defect report can easily reproduce the
issue by following the steps outlined.
● Focus on Impact: Describe how the defect impacts the user or system functionality.
● Provide Evidence: Whenever possible, include logs, screenshots, or any other evidence
that can help reproduce or investigate the defect.
● Avoid Assumptions: Do not assume what caused the issue unless you are certain. Stick
to the facts and observable behavior.
By following these guidelines, you will write defect reports that are clear, actionable, and helpful
to the development team, which ultimately speeds up the resolution process.
Mocking improves unit tests by simulating interactions with external systems, such as databases or APIs, making tests more predictable and faster. It allows developers to focus on testing the logic of the code without relying on actual external resources. By using mocking tools like unittest.mock, developers can ensure tests remain independent and accurately verify interactions, reducing potential side-effects from external dependencies during testing .
Ensuring test cases remain effective involves regular review and updates to align with changes in project requirements. Clear and concise documentation, including detailed steps, preconditions, and expected results, is crucial for test management. Additional practices include reviewing test cases after code changes or defects have been addressed, and utilizing test management tools like Jira for organizing and categorizing test cases based on priorities or features. Automation and integration with CI/CD pipelines also help maintain test relevance .
In test management, a Test Case in tools like Jira involves defining a set of conditions and inputs to verify if specific functionalities work as intended, including preconditions, steps, and expected results. Conversely, a Bug Report in Bugzilla focuses on documenting unexpected behaviors, including descriptions, reproducible steps, and the issue's impact. Test Cases ensure functionalities meet requirements, while Bug Reports document discrepancies for resolution .
Test environment setup intersects with risk management by ensuring that testing conditions mimic production environments to accurately identify potential issues. Effective risk management involves anticipating and mitigating risks, such as test environment unavailability or unresolved issues from previous versions. Strategies include setting up backup environments and allocating extra resources to handle critical defects, which helps mitigate delays and improve test reliability .
Parameterized tests in Python allow the execution of a single test with multiple sets of inputs, improving test coverage and efficiency. This method reduces the need to write separate test cases for each input set and ensures consistent test execution across different scenarios. Using decorators like @pytest.mark.parametrize in pytest, developers can test functions with various inputs and expected outcomes, verifying that implementations are robust and handle diverse inputs correctly .
CI/CD pipelines automate the process of software integration and delivery, playing a crucial role in Agile by facilitating frequent releases and deployments. This integration allows developers to consistently merge code changes, run tests, and deploy to production environments, enhancing the ability to quickly respond to feedback and changes. CI/CD supports Agile’s iterative nature by ensuring that deployments are smooth, tested, and aligned with user requirements .
Using descriptive test names enhances test management by clearly communicating the purpose and scope of the test, making it easier for developers to understand functionality coverage at a glance. This practice aids in quickly identifying failed tests, understanding issues, maintaining tests over time, and improving collaboration among team members. Descriptive names contribute to self-documenting tests, fostering maintainability and readability in complex projects .
Continuous testing in Agile allows for early detection of bugs, which reduces costs and improves quality by integrating tests into each sprint. This contrasts with the Waterfall model, where testing is performed at the end of the project, increasing the risk of late discovery of major issues. Agile’s approach enables more frequent and reliable feedback loops, decreasing deployment risks and leading to faster time-to-market .
TDD aligns with Agile methodologies due to its focus on continuous integration and iterative development. In TDD, writing tests before code encourages more frequent testing cycles and immediate feedback, which supports Agile’s iterative nature and frequent releases. This approach minimizes bugs and aligns closely with Agile's goal of rapid responses to changing requirements, ensuring continuous improvement and customer satisfaction .
Python supports various stages of the STLC through its ecosystem of libraries and tools. For Requirement Analysis, libraries like PyPDF2 and SQLite can parse documents and test database-specific requirements. In Test Planning, the Jira-Python API helps manage plans and tasks. Python-based frameworks like unittest and pytest are used for Test Case Development and Execution, enabling both manual and automated testing. Mock or responses can simulate external system interactions, aiding Test Environment Setup. This integrative capability of Python enhances robustness throughout the STLC .