Skip to content

Commit

Permalink
Sample code for the article on creating test with ChatGPT (#511)
Browse files Browse the repository at this point in the history
* Sample code for the article on creating test with ChatGPT

* Fix format issues

* Fix format issues, round two

* More fix attempts

* More fixes

* TR updates, first round

* Fix linter issues

* More linter issues

* Update chatgpt-unit-tests-python/test_fizzbuzz_pytest.py

Co-authored-by: Martin Breuss <[email protected]>

---------

Co-authored-by: Martin Breuss <[email protected]>
Co-authored-by: brendaweles <[email protected]>
Co-authored-by: Geir Arne Hjelle <[email protected]>
  • Loading branch information
4 people authored Apr 2, 2024
1 parent f5fd81e commit 806f307
Show file tree
Hide file tree
Showing 12 changed files with 355 additions and 0 deletions.
3 changes: 3 additions & 0 deletions chatgpt-unit-tests-python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Write Unit Tests for Your Python Code With ChatGPT

This folder provides the code examples for the Real Python tutorial [Write Unit Tests for Your Python Code With ChatGPT](https://realpython.com/chatgpt-unit-tests-python/).
36 changes: 36 additions & 0 deletions chatgpt-unit-tests-python/calculations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from collections import Counter


def add(x, y):
return x + y


def subtract(x, y):
return x - y


def multiply(x, y):
return x * y


def divide(x, y):
if y == 0:
raise ValueError("Cannot divide by zero.")
return x / y


def mean(data):
return sum(data) / len(data)


def median(data):
n = len(data)
index = n // 2
if n % 2:
return sorted(data)[index]
return sum(sorted(data)[index - 1 : index + 1]) / 2


def mode(data):
c = Counter(data)
return [k for k, v in c.items() if v == c.most_common(1)[0][1]]
9 changes: 9 additions & 0 deletions chatgpt-unit-tests-python/fizzbuzz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
def fizzbuzz(number):
if number % 15 == 0:
return "fizz buzz"
elif number % 3 == 0:
return "fizz"
elif number % 5 == 0:
return "buzz"
else:
return number
38 changes: 38 additions & 0 deletions chatgpt-unit-tests-python/fizzbuzz_doctest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
def fizzbuzz(number):
"""Solves the FizzBuzz problem.
Return "fizz" for numbers divisible by 3,
"buzz" for numbers divisible by 5,
"fizz buzz" for numbers divisible by both,
and the number itself otherwise.
>>> fizzbuzz(3)
'fizz'
>>> fizzbuzz(5)
'buzz'
>>> fizzbuzz(15)
'fizz buzz'
>>> fizzbuzz(4)
4
>>> fizzbuzz(30)
'fizz buzz'
>>> fizzbuzz(9)
'fizz'
>>> fizzbuzz(10)
'buzz'
"""

if number % 15 == 0:
return "fizz buzz"
elif number % 3 == 0:
return "fizz"
elif number % 5 == 0:
return "buzz"
else:
return number


if __name__ == "__main__":
import doctest

doctest.testmod()
22 changes: 22 additions & 0 deletions chatgpt-unit-tests-python/interval.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import pytest


def in_interval(number: int, start: int, end: int) -> bool:
return start <= number <= end


@pytest.mark.parametrize(
"number, start, end, expected",
[
(5, 1, 10, True), # Test case inside the interval
(1, 1, 10, True), # Edge case: number equals start
(10, 1, 10, True), # Edge case: number equals end
(0, 1, 10, False), # Number below the interval
(11, 1, 10, False), # Number above the interval
(5, 5, 5, True), # Edge case: start equals end equals number
(-1, -5, 5, True), # Test case with negative numbers
(-6, -5, 5, False), # Number below the interval with negatives
],
)
def test_in_interval(number, start, end, expected):
assert in_interval(number, start, end) == expected
32 changes: 32 additions & 0 deletions chatgpt-unit-tests-python/prime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import pytest


def is_prime(number: int) -> bool:
if number <= 1:
return False
for i in range(2, int(number**0.5) + 1):
if number % i == 0:
return False
return True


@pytest.mark.parametrize(
"number, expected",
[
(2, True), # Smallest prime
(3, True), # Prime
(4, False), # Composite (2*2)
(5, True), # Prime
(11, True), # Prime
(12, False), # Composite (2*6)
(13, True), # Prime
(25, False), # Composite (5*5)
(29, True), # Prime
(1, False), # Not prime by definition
(0, False), # Not prime
(-1, False), # Negative number
(-11, False), # Negative number
],
)
def test_is_prime(number, expected):
assert is_prime(number) == expected
10 changes: 10 additions & 0 deletions chatgpt-unit-tests-python/readers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import json


class JSONReader:
def __init__(self, filename):
self.filename = filename

def read(self):
with open(self.filename, encoding="utf-8") as file:
return json.load(file)
59 changes: 59 additions & 0 deletions chatgpt-unit-tests-python/test_calculations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import unittest

from calculations import add, divide, mean, median, mode, multiply, subtract


class TestArithmeticOperations(unittest.TestCase):
def test_add(self):
self.assertEqual(add(10, 5), 15)
self.assertEqual(add(-1, 1), 0)
self.assertEqual(add(-1, -1), -2)

def test_subtract(self):
self.assertEqual(subtract(10, 5), 5)
self.assertEqual(subtract(-1, 1), -2)
self.assertEqual(subtract(-1, -1), 0)

def test_multiply(self):
self.assertEqual(multiply(10, 5), 50)
self.assertEqual(multiply(-1, 1), -1)
self.assertEqual(multiply(-1, -1), 1)

def test_divide(self):
self.assertEqual(divide(10, 5), 2)
self.assertEqual(divide(-1, 1), -1)
self.assertEqual(divide(-1, -1), 1)
with self.assertRaises(ValueError):
divide(10, 0)


class TestStatisticalOperations(unittest.TestCase):
def test_mean(self):
self.assertEqual(mean([1, 2, 3, 4, 5]), 3)
self.assertEqual(mean([1, 2, 3, 4, 5, 6]), 3.5)

def test_median_odd(self):
self.assertEqual(median([1, 3, 3, 6, 7, 8, 9]), 6)

def test_median_even(self):
self.assertEqual(median([1, 2, 3, 4, 5, 6, 8, 9]), 4.5)

def test_median_unsorted(self):
self.assertEqual(median([7, 1, 3, 3, 2, 6]), 3)

def test_mode_single(self):
self.assertEqual(mode([1, 2, 2, 3, 4, 4, 4, 5]), [4])

def test_mode_multiple(self):
self.assertEqual(set(mode([1, 1, 2, 3, 4, 4, 5, 5])), {1, 4, 5})


def load_tests(loader, tests, pattern):
suite = unittest.TestSuite()
# suite.addTests(loader.loadTestsFromTestCase(TestArithmeticOperations))
suite.addTests(loader.loadTestsFromTestCase(TestStatisticalOperations))
return suite


if __name__ == "__main__":
unittest.main()
41 changes: 41 additions & 0 deletions chatgpt-unit-tests-python/test_fizzbuzz_pytest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import pytest

from fizzbuzz import fizzbuzz

# def test_fizzbuzz_with_number_divisible_by_15():
# assert fizzbuzz(30) == "fizz buzz"


# def test_fizzbuzz_with_number_divisible_by_3():
# assert fizzbuzz(9) == "fizz"


# def test_fizzbuzz_with_number_divisible_by_5():
# assert fizzbuzz(10) == "buzz"


# def test_fizzbuzz_with_number_not_divisible_by_3_or_5():
# assert fizzbuzz(4) == 4


# def test_fizzbuzz_with_zero():
# assert fizzbuzz(0) == "fizz buzz"


@pytest.mark.parametrize(
"input,expected",
[
(30, "fizz buzz"), # Divisible by 15
(9, "fizz"), # Divisible by 3
(10, "buzz"), # Divisible by 5
(4, 4), # Not divisible by 3 or 5
(0, "fizz buzz"), # Edge case: 0 (divisible by 15)
(33, "fizz"), # Additional case: Divisible by 3
(55, "buzz"), # Additional case: Divisible by 5
(98, 98), # Additional case: Not divisible by 3 or 5
],
)
def test_fizzbuzz(input, expected):
assert (
fizzbuzz(input) == expected
), f"Expected {expected} for input {input}"
32 changes: 32 additions & 0 deletions chatgpt-unit-tests-python/test_fizzbuzz_unittest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import unittest

from fizzbuzz import fizzbuzz


class TestFizzBuzz(unittest.TestCase):
def test_fizz(self):
"""Test numbers divisible by 3 but not by 5"""
self.assertEqual(fizzbuzz(3), "fizz")
self.assertEqual(fizzbuzz(6), "fizz")
self.assertNotEqual(fizzbuzz(15), "fizz")

def test_buzz(self):
"""Test numbers divisible by 5 but not by 3"""
self.assertEqual(fizzbuzz(5), "buzz")
self.assertEqual(fizzbuzz(10), "buzz")
self.assertNotEqual(fizzbuzz(15), "buzz")

def test_fizz_buzz(self):
"""Test numbers divisible by both 3 and 5"""
self.assertEqual(fizzbuzz(15), "fizz buzz")
self.assertEqual(fizzbuzz(30), "fizz buzz")

def test_neither(self):
"""Test numbers not divisible by 3 or 5"""
self.assertEqual(fizzbuzz(1), 1)
self.assertEqual(fizzbuzz(2), 2)
self.assertEqual(fizzbuzz(4), 4)


if __name__ == "__main__":
unittest.main()
38 changes: 38 additions & 0 deletions chatgpt-unit-tests-python/test_readers_pytest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import json

import pytest

# Assuming JSONReader is defined in 'your_module.py'
from readers import JSONReader


@pytest.fixture
def setup_json_file(tmp_path):
# Create a temporary JSON file
data = {"name": "John Doe", "age": 30}
file_path = tmp_path / "test_data.json"
with open(file_path, "w", encoding="utf-8") as file:
json.dump(data, file)
return file_path


def test_read_json_correctly(setup_json_file):
# Test that JSONReader reads the file correctly
reader = JSONReader(setup_json_file)
data = reader.read()
assert data == {
"name": "John Doe",
"age": 30,
}, "Should correctly read JSON content"


def test_file_not_found():
# Test for handling of file not found exception
reader = JSONReader("non_existent_file.json")
with pytest.raises(FileNotFoundError):
reader.read()


# Additional tests can be added to cover more scenarios,
# such as reading empty files,
# files with invalid JSON content, etc.
35 changes: 35 additions & 0 deletions chatgpt-unit-tests-python/test_readers_unittest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import json
import os
import tempfile
import unittest

from readers import JSONReader


class TestJSONReader(unittest.TestCase):
def setUp(self):
# Create a temporary file and write some JSON data to it
self.temp_file, self.temp_file_path = tempfile.mkstemp(suffix=".json")
self.test_data = {"name": "Test", "value": 123}
with os.fdopen(self.temp_file, "w", encoding="utf-8") as file:
json.dump(self.test_data, file)

def tearDown(self):
# Clean up by removing the temporary file
os.remove(self.temp_file_path)

def test_read_json(self):
# Test reading the JSON data
reader = JSONReader(self.temp_file_path)
data = reader.read()
self.assertEqual(data, self.test_data)

def test_file_not_found(self):
# Test file not found error handling
reader = JSONReader("non_existent_file.json")
with self.assertRaises(FileNotFoundError):
_ = reader.read()


if __name__ == "__main__":
unittest.main()

0 comments on commit 806f307

Please sign in to comment.