```python
"""Unit tests for calculate_discount."""
import pytest
from solution import calculate_discount
class TestCalculateDiscountHappyPath:
"""Happy path scenarios for valid inputs."""
@pytest.mark.parametrize(
"price, percent, expected",
[
(100.0, 10.0, 90.0),
(200.0, 25.0, 150.0),
(50.0, 50.0, 25.0),
(99.99, 10.0, 89.99),
(1000.0, 33.33, 666.7),
],
)
def test_calculate_discount_valid_inputs_returns_expected(
self, price, percent, expected
):
# Act
result = calculate_discount(price, percent)
# Assert
assert result == expected
def test_calculate_discount_rounds_to_two_decimals(self):
# Arrange
price = 10.0
percent = 33.0
# Act
result = calculate_discount(price, percent)
# Assert
assert result == 6.7
# Ensure rounding to 2 decimals (not more precision leaked)
assert round(result, 2) == result
class TestCalculateDiscountBoundaries:
"""Boundary conditions for the valid input ranges."""
def test_calculate_discount_zero_percent_returns_full_price(self):
assert calculate_discount(100.0, 0) == 100.0
def test_calculate_discount_hundred_percent_returns_zero(self):
assert calculate_discount(100.0, 100) == 0.0
def test_calculate_discount_zero_price_returns_zero(self):
assert calculate_discount(0, 50) == 0.0
def test_calculate_discount_zero_price_zero_percent_returns_zero(self):
assert calculate_discount(0, 0) == 0.0
def test_calculate_discount_zero_price_hundred_percent_returns_zero(self):
assert calculate_discount(0, 100) == 0.0
class TestCalculateDiscountInvalidInputs:
"""Error paths for out-of-range inputs."""
@pytest.mark.parametrize("price", [-0.01, -1, -100.0, -1e-9])
def test_calculate_discount_negative_price_raises_value_error(self, price):
with pytest.raises(ValueError, match="Invalid input"):
calculate_discount(price, 10)
@pytest.mark.parametrize("percent", [-0.01, -1, -50.0, 100.01, 101, 1000])
def test_calculate_discount_percent_out_of_range_raises_value_error(
self, percent
):
with pytest.raises(ValueError, match="Invalid input"):
calculate_discount(100.0, percent)
def test_calculate_discount_negative_price_and_invalid_percent_raises(self):
with pytest.raises(ValueError, match="Invalid input"):
calculate_discount(-10.0, 150)
class TestCalculateDiscountReturnType:
"""Return type guarantees."""
def test_calculate_discount_returns_float(self):
result = calculate_discount(100.0, 10.0)
assert isinstance(result, float)
def test_calculate_discount_accepts_int_inputs(self):
# Ints are acceptable since they satisfy the numeric comparisons
result = calculate_discount(100, 25)
assert result == 75.0
```
### Coverage summary
- **Happy paths**: multiple valid `(price, percent)` combinations via `parametrize`, including rounding behavior.
- **Boundary conditions**: `percent = 0`, `percent = 100`, `price = 0`, and combinations thereof.
- **Invalid inputs / exceptions**: negative prices, negative percents, and percents > 100 all raise `ValueError` with the expected message.
- **Return type**: confirms `float` output and that integer arguments are accepted (duck typing on numeric comparisons).
- **Rounding**: asserts result is rounded to 2 decimal places.
### Assumptions
- The source module is named `solution.py` (import path `from solution import calculate_discount`); adjust the import to match your project layout.
- Integer arguments are considered valid because they satisfy the numeric comparisons in the guard clause — the signature's type hints are treated as non-enforcing (standard Python behavior).
- No external dependencies (I/O, time, network) exist, so no mocking is required.
- `ValueError` message `"Invalid input"` is part of the contract worth asserting via `match=`.
Generate Pytest and Unittest Cases with AI
Tested prompts for ai generate unit tests python compared across 5 leading AI models.
BEST BY JUDGE SCORE
Claude Opus 4.7
8/10
The prompt we tested
You are an expert Python test engineer. Generate comprehensive unit tests for the Python code provided below.
Instructions:
Use pytest style by default (unittest only if the source clearly uses it), follow AAA (Arrange-Act-Assert) structure with descriptive test names like test_<function>_<scenario>_<expected>, and use parametrize for multiple input cases and pytest.raises for error paths. Mock external dependencies (I/O, network, time, DB) with unittest.mock, aim for high branch coverage without testing implementation details, and output runnable, PEP8-compliant code in a single fenced Python block.
Python code to test:
def calculate_discount(price: float, percent: float) -> float:
if price < 0 or not 0 <= percent <= 100:
raise ValueError('Invalid input')
return round(price * (1 - percent / 100), 2)
Produce the complete test file, including imports, fixtures or setup, well-named test functions covering happy paths, edge cases, boundary conditions, invalid inputs, and expected exceptions. After the code, include a brief bulleted summary of which behaviors are covered and any assumptions you made.
Model comparison
| Model | Quality | Cost | Latency | Tokens |
|---|---|---|---|---|
| Claude Opus 4.7 | 8 | $0.1230 | 19.0s | 394 / 1561 |
| Claude Haiku 4.5 | 7 | $0.0105 | 10.9s | 270 / 2048 |
| GPT-5.4 | 8 | $0.0119 | 11.2s | 242 / 756 |
| Gemini 2.5 Pro | 2 | $0.0015 | 18.5s | 251 / 238 |
| Grok 4.1 Fast Reasoning | 7 | $0.0004 | 52.3s | 388 / 601 |
Outputs
```python
import pytest
from unittest.mock import patch
from your_module import calculate_discount # Replace 'your_module' with actual module name
class TestCalculateDiscount:
"""Test suite for calculate_discount function."""
# Happy path tests
def test_calculate_discount_with_valid_inputs_returns_correct_discount(self):
"""Test discount calculation with standard valid inputs."""
# Arrange
price = 100.0
percent = 10.0
expected = 90.0
# Act
result = calculate_discount(price, percent)
# Assert
assert result == expected
def test_calculate_discount_with_fifty_percent_discount(self):
"""Test discount calculation with 50% discount."""
# Arrange
price = 200.0
percent = 50.0
expected = 100.0
# Act
result = calculate_discount(price, percent)
# Assert
assert result == expected
def test_calculate_discount_with_zero_percent_returns_original_price(self):
"""Test that 0% discount returns original price."""
# Arrange
price = 75.5
percent = 0.0
expected = 75.5
# Act
result = calculate_discount(price, percent)
# Assert
assert result == expected
def test_calculate_discount_with_hundred_percent_returns_zero(self):
"""Test that 100% discount returns zero."""
# Arrange
price = 100.0
percent = 100.0
expected = 0.0
# Act
result = calculate_discount(price, percent)
# Assert
assert result == expected
# Edge case tests with parametrization
@pytest.mark.parametrize("price,percent,expected", [
(0.0, 0.0, 0.0),
(0.0, 50.0, 0.0),
(0.0, 100.0, 0.0),
(1.0, 1.0, 0.99),
(0.01, 50.0, 0.01),
(999999.99, 0.0, 999999.99),
])
def test_calculate_discount_with_edge_case_values(
self, price, percent, expected
):
"""Test discount calculation with edge case values."""
# Act
result = calculate_discount(price, percent)
# Assert
assert result == expected
# Rounding tests
def test_calculate_discount_rounds_to_two_decimal_places(self):
"""Test that result is rounded to 2 decimal places."""
# Arrange
price = 10.0
percent = 33.0
# 10 * (1 - 33/100) = 10 * 0.67 = 6.7, but should round to 6.7
# Act
result = calculate_discount(price, percent)
# Assert
assert result == 6.7
assert len(str(result).split('.')[-1]) <= 2
def test_calculate_discount_with_floating_point_precision(self):
"""Test rounding with values that cause floating point precision issues."""
# Arrange
price = 19.99
percent = 33.33
# 19.99 * (1 - 0.3333) = 19.99 * 0.6667 = 13.331...
# Act
result = calculate_discount(price, percent)
# Assert
assert isinstance(result, float)
assert result == round(19.99 * (1 - 33.33 / 100), 2)
# Boundary condition tests
@pytest.mark.parametrize("price,percent", [
(1000.0, 0.0),
(1000.0, 0.01),
(1000.0, 99.99),
(1000.0, 100.0),
(0.01, 0.0),
(0.01, 100.0),
])
def test_calculate_discount_at_boundaries(self, price, percent):
"""Test discount at valid boundary values."""
# Act
result = calculate_discount(price, percent)
# Assert
assert isinstance(result, float)
assert result >= 0.0
assert result <= price
# Invalid input tests
def test_calculate_discount_with_negative_price_raises_valueerror(self):
"""Test that negative price raises ValueError."""
# Arrange
price = -10.0
percent = 10.0
# Act & Assert
with pytest.raises(ValueError, match="Invalid input"):
calculate_discount(price, percent)
def test_calculate_discount_with_very_negative_price_raises_valueerror(self):
"""Test that very negative price raises ValueError."""
# Arrange
price = -999999.99
percent = 50.0
# Act & Assert
with pytest.raises(ValueError, match="Invalid input"):
calculate_discount(price, percent)
@pytest.mark.parametrize("price,percent", [
(100.0, -0.01),
(100.0, -1.0),
(100.0, -100.0),
(100.0, 100.01),
(100.0, 101.0),
(100.0, 200.0),
])
def test_calculate_discount_with_out_of_range_percent_raises_valueerror(
self, price, percent
):
"""Test that percent outside 0-100 range raises ValueError."""
# Act & Assert
with pytest.raises(ValueError, match="Invalid input"):
calculate_discount(price, percent)
def test_calculate_discount_with_negative_price_and_percent_raises_valueerror(self):
"""Test that both negative price and invalid percent raise ValueError."""
# Arrange
price = -50.0
percent = 150.0
# Act & Assert
with pytest.raises(ValueError, match="Invalid input"):
calculate_discount(price, percent)
# Type handling (implicit - pytest will handle type errors naturally)
def test_calculate_discount_accepts_integer_inputs(self):
"""Test that function works with integer inputs (implicit conversion)."""
# Arrange
price = 100 # int instead of float
percent = 10 # int instead of float
expected = 90.0
# Act
result = calculate_discount(price, percent)
# Assert
assert result == expected
def test_calculate_discount_with_very_small_positive_values(self):
"""Test with very small positive values."""
# Arrange
price = 0.001
percent = 50.0
# Act
result = calculate_discount(price, percent)
# Assert
assert result == 0.0 # Rounds to 0.0
def test_calculate_discount_with_large_values(self):
"""Test with large price values."""
# Arrange
price = 1000000.0
percent = 25.0
expected = 750000.0
# Act
result = calculate_discount(price, percent)
# Assert
assert result == expected
# Return type validation
def test_calculate_discount_returns_float(self):
"""Test that function always returns a float."""
# Act
result = calculate_discount(100.0, 10.0)
# Assert
assert isinstance(result, float)
def test_calculate_discount_result_is_non_negative(self):
"""Test that result is never negative."""
# Arrange
test_cases = [
(100.0, 0.0),
(100.0, 50.0),
(100.0, 100.0),
(0.0, 100.0),
]
# Act & Assert
for price, percent in test_cases:
result = calculate_discount(price, percent)
assert result >= 0.0
def test_calculate_discount_result_does_not_exceed_original_price(self):
"""Test that discounted price never exceeds original price
```python
import pytest
from your_module import calculate_discount
@pytest.mark.parametrize(
("price", "percent", "expected"),
[
(100.0, 0.0, 100.0),
(100.0, 10.0, 90.0),
(100.0, 100.0, 0.0),
(49.99, 25.0, 37.49),
(19.995, 50.0, 10.0),
(0.0, 50.0, 0.0),
(1234.56, 12.5, 1080.24),
],
)
def test_calculate_discount_valid_inputs_returns_rounded_discounted_price(
price, percent, expected
):
# Arrange
# Act
result = calculate_discount(price, percent)
# Assert
assert result == expected
@pytest.mark.parametrize(
("price", "percent"),
[
(-0.01, 10.0),
(-100.0, 0.0),
(100.0, -0.01),
(100.0, -5.0),
(100.0, 100.01),
(100.0, 150.0),
(-50.0, -10.0),
(-50.0, 150.0),
],
)
def test_calculate_discount_invalid_inputs_raises_value_error(price, percent):
# Arrange
# Act / Assert
with pytest.raises(ValueError, match="Invalid input"):
calculate_discount(price, percent)
@pytest.mark.parametrize(
("price", "percent", "expected"),
[
(50.0, 0.0, 50.0),
(50.0, 100.0, 0.0),
(0.0, 0.0, 0.0),
(0.0, 100.0, 0.0),
],
)
def test_calculate_discount_boundary_values_returns_expected_result(
price, percent, expected
):
# Arrange
# Act
result = calculate_discount(price, percent)
# Assert
assert result == expected
def test_calculate_discount_rounds_result_to_two_decimal_places():
# Arrange
price = 10.0
percent = 33.333
# Act
result = calculate_discount(price, percent)
# Assert
assert result == 6.67
assert result == round(result, 2)
@pytest.mark.parametrize(
("price", "percent", "expected"),
[
(80, 25, 60.0),
(200, 12.5, 175.0),
],
)
def test_calculate_discount_accepts_numeric_inputs_and_returns_float(
price, percent, expected
):
# Arrange
# Act
result = calculate_discount(price, percent)
# Assert
assert result == expected
assert isinstance(result, float)
```
- Covers valid discount calculations for typical values, zero discount, full discount, zero price, fractional percentages, and rounding behavior.
- Covers boundary conditions for `percent` at `0` and `100`, and `price` at `0`.
- Covers invalid inputs where `price` is negative or `percent` is outside the inclusive `0..100` range, asserting `ValueError` with the expected message.
- Assumption: replace `from your_module import calculate_discount` with the actual module path where `calculate_discount` is defined.
Of course. As a Python test engineer, here are the comprehensive unit tests for the provided code, following best practices.
```python
# test_discount_calculator.py
import pytest
from unittest.mock import patch # Though not needed for this simple function, it's good practice to import
# Assume the function is in a file named 'discount_calculator.py'
from discount_calculator import calculate_discount
@pytest.mark.parametrize(
"price, percent, expected",
[
# --- Happy Path Scenarios ---
(100.0, 10.0, 90.00), # Basic integer discount
(99.99, 10.0, 89.99), # Floating point price
(120.0, 12.5, 105.00), # Floating point percentage
(10, 25, 7.50), # Integer inputs
# --- Rounding Behavior ---
(10.55,
```python
import pytest
def calculate_discount(price: float, percent: float) -> float:
if price < 0 or not 0 <= percent <= 100:
raise ValueError('Invalid input')
return round(price * (1 - percent / 100), 2)
@pytest.mark.parametrize(
"price, percent, expected",
[
(100.0, 10.0, 90.0), # standard discount
(100.0, 0.0, 100.0), # zero discount
(100.0, 100.0, 0.0), # full discount
(0.0, 50.0, 0.0), # zero price
(10.5, 20.0, 8.4), # decimal price and percent
(1.015, 0.0, 1.02), # rounding up (half to even)
(1.025, 0.0, 1.02), # rounding stays (half to even)
(50.0, 50.0, 25.0), # even discount
],
)
def test_calculate_discount_valid_inputs_returns_rounded_discount(price, percent, expected):
# Arrange: inputs provided via parametrization
# Act
result = calculate_discount(price, percent)
# Assert
assert result == expected
@pytest.mark.parametrize(
"price, percent",
[
(-0.1, 50.0), # negative price
(-100.0, 10.0), # large negative price
(10.0, -0.1), # negative percent (price valid)
(10.0, 101.0), # percent over 100 (price valid)
(0.0, 100.1), # percent slightly over (price boundary)
],
)
def test_calculate_discount_invalid_inputs_raises_valueerror(price, percent):
# Arrange: invalid inputs provided via parametrization
# Act & Assert
with pytest.raises(ValueError, match='Invalid input'):
calculate_discount(price, percent)
```
- **Covered behaviors**: Happy paths (valid price >= 0, percent in [0,100], including boundaries, zero price/full discount, decimal calculations, rounding cases including half-even behavior); error paths (price < 0, percent < 0, percent > 100, covering both sides of the `or` condition for branch coverage); exact ValueError message.
- **Assumptions**: Function inputs are finite floats (no NaN/inf testing as unspecified); float equality `==` safe post-rounding to 2 decimals with chosen inputs; no external dependencies to mock; high branch/statement coverage achieved without implementation-specific tests (e.g., no mocking `round`).
Related queries
Try it with a real tool
Run this prompt in one of these tools. Affiliate links help keep Gridlyx free.