How to use Generative AI for Unit Testing
Getting Started with Unit Testing Using ChatGPT
Are you a student in school, a junior software engineer, or a hobbyist programmer creating your own unit tests? Maybe you’re still “copy & pasting” previous unit tests and then modifying them for your new classes? If so, this article is definitely for you! We will be going over how to use Generative AI, specifically ChatGPT, to develop unit tests in Python. Let’s begin by telling ChatGPT that we require assistance with developing unit tests in Python.
Prompt:
I'd like your help with creating unit tests for a Python class I have.
Response:
Of course, I'd be happy to help you create unit tests for your Python class. Unit tests are a crucial part of ensuring that your code works as expected and that future changes don't introduce new bugs. To get started, I'll need some information about the class you want to test. Specifically, please provide: 1. A brief description of the class and its purpose. 2. The methods you want to test (if there are any specific methods). 3. Any dependencies the class might have on external resources or other classes. Once you provide this information, I can guide you through the process of writing effective unit tests for your Python class.
Unit Testing with Generative AI Case Study
Use generative AI to help create a unit test for Python code. Generative AI can generate unit tests for all of the functions of a Python class.Try it for freeCreating Python unit tests using ChatGPT
The code we will be using in this article will be similar to the one used in our Debugging Python Code with ChatGPT tutorial. We will provide ChatGPT with a simple Calculator class that has the following methods: add()
, subtract()
, multiply()
, and divide()
.
Note: When giving ChatGPT code (in any language), it is recommended to present the code in the form of a code block to distinguish it from regular text. When you include code in a ChatGPT prompt, make sure to format it as we have done in the following prompt.
Prompt:
```py class Calculator: def add(self, left, right): return left + right def subtract(self, left, right): return left - right def multiply(self, left, right): return left * right def divide(self, left, right): return left / right ```py
Response:
Thank you for providing the Calculator class. To create unit tests for this class, we will use the built-in Python unittest framework. Let's start by writing some test cases to cover the functionality of the Calculator class. We'll focus on testing each of the four methods: add, subtract, multiply, and divide. Create a new Python file (e.g., test_calculator.py) and write the following code: ```py import unittest from your_calculator_module import Calculator # Replace 'your_calculator_module' with the actual module name class TestCalculator(unittest.TestCase): def setUp(self): # Create an instance of the Calculator class before each test self.calculator = Calculator() def test_add(self): result = self.calculator.add(2, 3) self.assertEqual(result, 5) def test_subtract(self): result = self.calculator.subtract(5, 3) self.assertEqual(result, 2) def test_multiply(self): result = self.calculator.multiply(4, 3) self.assertEqual(result, 12) def test_divide(self): result = self.calculator.divide(10, 3) self.assertEqual(result, 10 % 3) # The correct result should be 1 if __name__ == '__main__': unittest.main() ```py
ChatGPT returned some test functions for our Calculator
class. It created a TestCalculator
class with 5 functions: setUp()
, test_add()
, test_subtract()
, test_multiply()
, and test_divide()
. For this section, let’s focus on the test functions.
We see the test functions all have a single test case inside of them. This probably doesn’t cover all edge cases that might need to be covered for each function. Instead of trying to come up with every single edge case, let’s ask ChatGPT if there are any edge cases that we have not considered in the current unit tests.
Prompt:
Are there edge cases that are not being tested for?
Response:
Yes, there are several other edge cases that you can consider testing for in your Calculator class. Testing these scenarios will help ensure that your calculator functions correctly and handles various inputs appropriately. Here are some additional edge cases you may want to test for: Negative Numbers: Test operations involving negative numbers, such as addition, subtraction, multiplication, and division. Zero Operand: Test the case where one or both operands are zero for all four basic operations. Large Numbers: Test with large numbers to ensure that your calculator can handle them without overflowing or causing other issues. Floating-Point Precision: For the multiply and divide methods, test with decimal numbers to verify that the precision is retained up to a reasonable number of decimal places. Invalid Inputs: Test for scenarios where invalid input is passed to the calculator methods (e.g., passing non-numeric values). Boundary Values: Test with values that are close to the minimum and maximum values for integer or floating-point data types. Rounding and Precision: Test for scenarios where precision may be lost due to the limitations of floating-point arithmetic. Chained Operations: Test with multiple consecutive operations (e.g., 2 + 3 * 4 - 1) to ensure that the order of operations is correctly handled. Here's an example of how you could add some of these additional tests to the existing TestCalculator class: ```py class TestCalculator(unittest.TestCase): # ... (existing test methods) def test_divide_by_negative(self): result = self.calculator.divide(10, -2) self.assertEqual(result, -5) def test_divide_zero_by_number(self): result = self.calculator.divide(0, 5) self.assertEqual(result, 0) def test_divide_number_by_zero(self): with self.assertRaises(ZeroDivisionError): self.calculator.divide(10, 0) def test_multiply_large_numbers(self): result = self.calculator.multiply(999999999, 999999999) self.assertEqual(result, 999999998000000001) def test_add_decimal_numbers(self): result = self.calculator.add(0.1, 0.2) self.assertAlmostEqual(result, 0.3, places=4) # ... (other additional test methods) if __name__ == '__main__': unittest.main() ```py By including a comprehensive set of test cases, you can gain confidence in the correctness of your Calculator class and ensure that it behaves as expected in various scenarios. Remember, the goal of testing is to uncover potential issues and ensure that your code handles all possible scenarios gracefully.
Great work! We now have a start of test cases to use for our Calculator
class. Additionally, we have some ideas from ChatGPT that could enhance further development of other unit tests.
Customizing and refining unit tests with ChatGPT
Let’s review all of the test functions we currently have.
import unittestfrom calculator import CalculatorclassTestCalculator(unittest.TestCase):defsetUp(self):self.calculator = Calculator()deftest_add(self):result = self.calculator.add(2,3)self.assertEqual(result,5)deftest_subtract(self):result = self.calculator.subtract(5,3)self.assertEqual(result,2)deftest_multiply(self):result = self.calculator.multiply(4,3)self.assertEqual(result,12)deftest_divide(self):result = self.calculator.divide(10,2)self.assertEqual(result,5)with self.assertRaises(ZeroDivisionError):self.calculator.divide(10,0)deftest_divide_by_negative(self):result = self.calculator.divide(10,-2)self.assertEqual(result,-5)deftest_divide_zero_by_number(self):result = self.calculator.divide(0,5)self.assertEqual(result,0)deftest_divide_number_by_zero(self):with self.assertRaises(ZeroDivisionError):self.calculator.divide(10,0)deftest_multiply_large_numbers(self):result = self.calculator.multiply(999999999,999999999)self.assertEqual(result,999999998000000001)deftest_add_decimal_numbers(self):result = self.calculator.add(0.1,0.2)self.assertAlmostEqual(result,0.3, places=4)if __name__ =='__main__':unittest.main()
Before executing the unit tests ChatGPT provides, we’ll ensure that the unit tests abide by 3 rules:
- The tests apply to our application
- There is no repetition across the tests
- Each test function only examines one unit, or function, from the original codebase
Here are the final, corrected unit tests for our Calculator
class.
import unittestfrom calculator import CalculatorclassTestCalculator(unittest.TestCase):defsetUp(self):self.calculator = Calculator()deftest_add(self):result = self.calculator.add(2,3)self.assertEqual(result,5)deftest_subtract(self):result = self.calculator.subtract(5,3)self.assertEqual(result,2)deftest_multiply(self):result = self.calculator.multiply(4,3)self.assertEqual(result,12)deftest_divide(self):result = self.calculator.divide(10,2)self.assertEqual(result,5)deftest_divide_by_negative(self):result = self.calculator.divide(10,-2)self.assertEqual(result,-5)deftest_divide_zero_by_number(self):result = self.calculator.divide(0,5)self.assertEqual(result,0)deftest_divide_number_by_zero(self):with self.assertRaises(ZeroDivisionError):self.calculator.divide(10,0)deftest_multiply_large_numbers(self):result = self.calculator.multiply(999999999,999999999)self.assertEqual(result,999999998000000001)deftest_add_decimal_numbers(self):result = self.calculator.add(0.1,0.2)self.assertAlmostEqual(result,0.3, places=4)if __name__ =='__main__':unittest.main()
Using the setup()
and teardown()
functions
Lastly, let’s consider the setUp()
and tearDown()
functions. As a reminder, the setUp()
function will execute prior to each test. The tearDown()
function will execute after each test. These functions are used to introduce a set of data that might be needed for testing purposes.
Using our current Calculator
class, it doesn’t seem like we need a setUp()
or tearDown()
function. Let’s ask ChatGPT if the current scenario needs these functions, and if so, to build them for us.
Prompt:
Do we need any setup or teardown functions for this test case? If so, can you create an instance to build a `setup()` function and `teardown()` function for this test case?
Response:
Certainly! While the current test case doesn't require a setUp or tearDown method, I can show you an example of how to use them in case you want to add shared resources or setup/cleanup actions in the future. In this example, I'll demonstrate creating an instance of the Calculator class in the setUp method and using the tearDown method to clean up the instance after each test: ```py import unittest from calculator import Calculator class TestCalculator(unittest.TestCase): def setUp(self): # Create an instance of the Calculator class before each test self.calculator = Calculator() def tearDown(self): # Clean up resources or perform actions after each test (if needed) # In this case, we don't have any cleanup to do for the Calculator instance. pass def test_add(self): result = self.calculator.add(2, 3) self.assertEqual(result, 5) if __name__ == '__main__': unittest.main() ```py In this updated version, the `setUp()` method creates an instance of the Calculator class before each test, and the tearDown method is used to perform any necessary cleanup (currently, there is no specific cleanup needed for the Calculator instance). Even though we don't have any cleanup to do in this example, it's still good practice to include the tearDown method when using setUp. As your test suite grows and you start using shared resources or more complex test setups, you can take advantage of these methods to maintain the desired test environment. Remember that you should only add setUp and tearDown methods when you need them. For simple tests like the current ones, it's perfectly fine to leave them out. As your test suite evolves, you can introduce these methods as needed to improve test maintainability and structure.
Although the functions were not required for our Calculator
class, let’s discuss an instance where they could be.
Imagine you are selling books online. When a person is shopping on your online website, they may apply coupons to their purchases. We can run unit tests to ensure that specific coupons apply discounts correctly.
from book import Bookimport unittestclassTestBook(unittest.TestCase):defsetUp(self):print("\nRunning setUp method...")# Book (title, price, discount)self.book_1 = Book('To Kill A Mockingbird'',25,0.1)self.book_2 = Book('Moby Dick'',15,0.2)deftearDown(self):print("Running tearDown method...")deftest_discount(self):print("Running test_discount...")self.assertEqual(self.book_1.apply_discount(),f"${25-25*0.1}")self.assertEqual(self.book_2.apply_discount(),f"${15-15*0.2}")if __name__=='__main__':unittest.main()
Conclusion
In conclusion, we used ChatGPT to build unit tests in Python. We were able to build unit tests for our Calculator
class to test against a variety of edge cases, ensuring that each function, or unit, is individually tested. We modified the unit tests from ChatGPT to ensure no repetition. Lastly, we went over how to use the setUp()
and tearDown()
functions that are used before and after each unit test. To practice using ChatGPT to create unit tests in Python, check out this case study: Unit Testing with Generative AI Case Study
'The Codecademy Team, composed of experienced educators and tech experts, is dedicated to making tech skills accessible to all. We empower learners worldwide with expert-reviewed content that develops and enhances the technical skills needed to advance and succeed in their careers.'
Meet the full teamRelated articles
- Article
Using ChatGPT to Debug Python Code
Learn how to use ChatGPT to identify and debug Python code errors through clear communication, error identification, and code simulation. - Article
Understanding Polymorphism in Python (With Examples)
Learn how to implement polymorphism in Python with practical examples and applications. Master this essential OOP concept to write more flexible, reusable code for your projects. - Article
Testing Types
In this article, you will be introduced to the different types of testing that may be used throughout the various stages of a project from local development to shipping to real users.
Learn more on Codecademy
- Free course
Unit Testing with Generative AI Case Study
Use generative AI to help create a unit test for Python code. Generative AI can generate unit tests for all of the functions of a Python class.Beginner Friendly< 1 hour - Course
Unit Test Development with Generative AI
Discover how Generative AI like ChatGPT can be used to create Python unit tests. Understand the Security Test Pyramid and best practices for AI unit testing.With CertificateIntermediate< 1 hour - Free course
Learn Intermediate Python 3: Exceptions and Unit Testing
Learn to maintain a healthy codebase by creating unit tests using Python's built-in `unittest` framework.Intermediate4 hours