AWS DevOps & Developer Productivity Blog
Testing your applications with Amazon Q Developer
Testing code is a fundamental step in the field of software development. It ensures that applications are reliable, meet quality standards, and work as intended. Automated software tests help to detect issues and defects early, reducing impact to end-user experience and business. In addition, tests provide documentation and prevent regression as code changes over time.
In this blog post, we show how the integration of generative AI tools like Amazon Q Developer can further enhance unit testing by automating test scenarios and generating test cases.
Amazon Q Developer helps developers and IT professionals with all of their tasks across the software development lifecycle – from coding, testing, and upgrading, to troubleshooting, performing security scanning and fixes, optimizing AWS resources, and creating data engineering pipelines. It integrates into your Integrated Development Environment (IDE) and aids with providing answers to your questions. Amazon Q Developer supports you across the Software Development Lifecycle (SLDC) by enriching feature and test development with step-by-step instructions and best practices. It learns from your interactions and training itself over time to output personalized, and tailored answers.
Solution overview
In this blog, we show how to use Amazon Q Developer to:
- learn about software testing concepts and frameworks
- identify unit test scenarios
- write unit test cases
- refactor test code
- mock dependencies
- generate sample data
Note: Amazon Q Developer may generate an output different from this blog post’s examples due to its nondeterministic nature.
Using Amazon Q Developer to learn about software testing frameworks and concepts
As you start gaining experience with testing, Amazon Q Developer can accelerate your learning through conversational Q&A directly within the AWS Management Console or the IDE. It can explain topics, provide general advice and share helpful resources on testing concepts and frameworks. It gives personalized recommendations on resources which makes the learning experience more interactive and accelerates the time to get started with writing unit tests. Let’s introduce an example conversation to demonstrate how you can leverage Amazon Q Developer for learning before attempting to write your first test case.
Example – Select and install frameworks
A unit testing framework is a software tool used for test automation, design and management of test cases. Upon starting a software project, you may be faced with the selection of a framework depending on the type of tests, programming language, and underlying technology. Let’s ask for recommendations around unit testing frameworks for Python code running in an AWS Lambda function.
In Figure 1, Amazon Q Developer answers with popular frameworks (pytest, unittest, Moto, AWS SAM Command Line Interface, and Powertools for AWS Lambda) including a brief description for each of them. It provides a reference to the sources of its response at the bottom and suggested follow up questions. As a next step, you may want to refine what you are looking for with a follow-up question. Amazon Q Developer uses the context from your previous questions and answers to give more precise answers as you continue the conversation. For example, one of the frameworks it suggested using was pytest. If you don’t know how to install that locally, you can ask something like “What are the different options to install pytest on my Linux machine?”. As shown in Figure 2, Amazon Q Developer provides installation recommendation using Python.
Example – Explain concepts
Amazon Q Developer can also help you to get up to speed with testing concepts, such as mocking service dependencies. Let’s ask another follow up question to explain the benefits of mocking AWS services.
The above conversation in Figure 3 shows how Amazon Q Developer can help to understand concepts. Let’s learn more about Moto.
In Figure 4, Amazon Q Developer gives more details about the Moto framework and provides a short example code snippet for mocking an AWS Lambda service.
Best Practice – Write clear prompts
Writing clear prompts helps you to get the desired answers from Amazon Q. A lack of clarity and topic understanding may result in unclear questions and irrelevant or off-target responses. Note how those prompts contain specific description of what the answer should provide. For example, Figure 1 includes the programming language (Python) and service (AWS Lambda) to be considered in the expected answer. If unfamiliar with a topic, leverage Amazon Q Developer as part of your research, to better understand that topic.
Using Amazon Q Developer to identify unit test cases
Understanding the purpose and intended functionality of the code is important for developing relevant test cases. We introduce an example use case in Python, which handles payroll calculation for different hour rates, hours worked, and tax rates.
"""
This module provides a Payroll class to calculate the net pay for an employee.
The Payroll class takes in the hourly rate, hours worked, and tax rate, and
calculates the gross pay, tax amount, and net pay.
"""
class Payroll:
"""
A class to handle payroll calculations.
"""
def __init__(self, hourly_rate: float, hours_worked: float, tax_rate: float):
self._validate_inputs(hourly_rate, hours_worked, tax_rate)
self.hourly_rate = hourly_rate
self.hours_worked = hours_worked
self.tax_rate = tax_rate
def _validate_inputs(self, hourly_rate: float, hours_worked: float, tax_rate: float) -> None:
"""
Validate the input values for the Payroll class.
Args:
hourly_rate (float): The employee's hourly rate.
hours_worked (float): The number of hours the employee worked.
tax_rate (float): The tax rate to be applied to the employee's gross pay.
Raises:
ValueError: If the hourly rate, hours worked, or tax rate is not a positive number,
or if the tax rate is not between 0 and 1.
"""
if hourly_rate <= 0:
raise ValueError("Hourly rate must be a non-negative number.")
if hours_worked < 0:
raise ValueError("Hours worked must be a non-negative number.")
if tax_rate < 0 or tax_rate >= 1:
raise ValueError("Tax rate must be between 0 and 1.")
def gross_pay(self) -> float:
"""
Calculate the employee's gross pay.
Returns:
float: The employee's gross pay.
"""
return self.hourly_rate * self.hours_worked
def tax_amount(self) -> float:
"""
Calculate the tax amount to be deducted from the employee's gross pay.
Returns:
float: The tax amount.
"""
return self.gross_pay() * self.tax_rate
def net_pay(self) -> float:
"""
Calculate the employee's net pay after deducting taxes.
Returns:
float: The employee's net pay.
"""
return self.gross_pay() - self.tax_amount()
The example shows how Amazon Q Developer can be used to identify test scenarios before writing the actual cases. Let’s ask Amazon Q Developer to suggest test cases for the Payroll class.
In Figure 5, Amazon Q Developer provides a list of different scenarios specific to the Payroll class, including valid, error, and edge cases.
Using Amazon Q Developer to write unit tests
Developers can have a collaborative conversation with Amazon Q Developer, which helps to unpack the code and think through testing cases to check that important test cases are captured but also edge cases are identified. This section focuses on how to facilitate the quick generation of unit test cases, based on the cases recommended in the previous section. Let’s start with a question around best practices when writing unit tests with pytest.
In Figure 6, Amazon Q Developer provides a list of best practices for writing effective unit tests. Let’s follow up by asking to generate one of the suggested test cases.
Amazon Q Developer includes code in its response which you can copy or insert directly into your file by choosing Insert at cursor. Figure 7 displays valid unit tests covering some of the suggested scenarios and best practices, such as being simple and holding descriptive naming. It also states how to run the test using a command for the terminal.
Best Practice – Provide context
Context allows Amazon Q Developer to offer tailored responses that are more in sync with the conversation. In the chat interface, the flow of the ongoing conversation and past interactions are a critical contextual element. Other ways to provide context are selecting the code-under-test, keeping any relevant files, such as test examples, open in the editor and leveraging conversation context such as asking for best practices and example test scenarios before writing the test cases.
Using Amazon Q Developer to refactor unit tests
To improve code quality, Amazon Q Developer can be used to recommend improvements and refactor parts of the code base. To illustrate Amazon Q Developer refactoring functionality, we prepared test cases for the Payroll class which deviate from some of the suggested best practices.
Example – Send to Amazon Q Refactor
Let’s follow up by asking to refactor the code built-in Amazon Q > Refactor functionality.
In Figure 8, the refactoring renamed the function and variable names to be more descriptive and therefore removed the comments. The recommendation is inserted in the second file to verify it runs correctly.
Best Practice – Apply human judgement and continuously interact with Amazon Q Developer
Note that code generations should always be reviewed and adjusted before used in your projects. Amazon Q Developer can provide you with initial guidance, but you might not get a perfect answer. A developer’s judgement should be applied to value usefulness of the generated code and iterations should be used to continuously improve the results.
Using Amazon Q Developer for mocking dependencies and generating sample data
More complex application architectures may require developers to mock dependencies and use sample data to test specific functionalities. The second code example contains a save_to_dynamodb_table function that writes a job_posting object into a specific Amazon DynamoDB table. This function references the TABLE_NAME environment variable to specify the name of the table in which the data should be saved.
We break down the tasks for Amazon Q Developer into three smaller steps for testing: Generate a fixture for mocking the TABLE_NAME environment variable name, generate instances of the given class to be used as test data, and generate the test.
Example – Generate fixtures
Pytest provides the capability to define fixtures to set defined, reliable, and consistent context for tests. Let’s ask Amazon Q Developer to write a fixture for the TABLE_NAME environment variable.
The result in Figure 9 shows that Amazon Q Developer generated a simple fixture for the TABLE_NAME environment variable. It provides code showing how to use the fixture in the actual test case with additional comments for its content.
Example – Generate data
Amazon Q Developer provides capabilities that can help you generate input data for your tests based on a schema, data model, or table definition. The save_to_dynamodb_table saves an instance of the job posting class to the table. Let’s ask Amazon Q Developer to create a sample instance based on this definition.
The answer shows a valid instance of the class in Figure 10 containing common example values for the fields.
Example – Generate unit test cases with context
The code being tested relies on an external library, boto3. To make sure that this dependency is included, we leave a comment specifying that boto3 should be mocked using the Moto library. Additionally, we tell Amazon Q Developer to consider the test instance named job_posting and the fixture named mock_table_name for reference. Developers can now provide a prompt to generate the test case using the context from previous tasks or use comments as inline prompts to generate the test within the test file itself.
Figure 11 shows the recommended code using inline prompts, which can be accepted as the unit test for the save_to_dynamodb_table function.
Best Practice – Break down larger tasks into smaller ones
For cases where Amazon Q Developer does not have much context or example code to refer to, such as writing unit tests from scratch, it is helpful to break down the tasks into smaller tasks. Amazon Q Developer will get more context with each step and can result in more effective responses.
Conclusion
Amazon Q Developer is a powerful tool that simplifies the process of writing and executing unit tests for your application. The examples provided in this post demonstrated that it can be a helpful companion throughout different stages of your unit test process. From initial learning to investigation and writing of test cases, the Chat, Generate, and Refactor capabilities allow you to speed up and improve your test generation. Using clear and concise prompts, context, an iterative approach, and small scoped tasks to interact with Amazon Q Developer improves the generated answers.
To learn more about Amazon Q Developer, see the following resources:
- Getting started with Amazon Q Developer
- Amazon Q Developer
- Best Practices for Prompt Engineering with Amazon CodeWhisperer
- Accelerate your Software Development Lifecycle with Amazon Q
About the authors