1
2
3
4
5
6
7 package com.ucoruh.calculator;
8
9 import static org.junit.jupiter.api.Assertions.assertEquals;
10 import static org.junit.jupiter.api.Assertions.assertThrows;
11
12 import org.junit.jupiter.api.BeforeEach;
13 import org.junit.jupiter.api.DisplayName;
14 import org.junit.jupiter.api.Nested;
15 import org.junit.jupiter.api.Test;
16 import org.junit.jupiter.params.ParameterizedTest;
17 import org.junit.jupiter.params.provider.CsvSource;
18
19
20
21
22
23
24
25
26
27
28 class CalculatorTest {
29
30 private Calculator calculator;
31
32 @BeforeEach
33 void setUp() {
34 calculator = new Calculator();
35 }
36
37 @Nested
38 @DisplayName("add")
39 class Add {
40
41 @Test
42 @DisplayName("adds two positive numbers")
43 void addsPositiveNumbers() {
44 assertEquals(5, calculator.add(2, 3));
45 }
46
47 @Test
48 @DisplayName("adds a negative and a positive number")
49 void addsNegativeAndPositive() {
50 assertEquals(-1, calculator.add(-4, 3));
51 }
52
53 @Test
54 @DisplayName("zero is the additive identity")
55 void addZero() {
56 assertEquals(7, calculator.add(7, 0));
57 }
58 }
59
60 @Nested
61 @DisplayName("subtract")
62 class Subtract {
63
64 @Test
65 @DisplayName("subtracts two positive numbers")
66 void subtractsPositiveNumbers() {
67 assertEquals(1, calculator.subtract(4, 3));
68 }
69
70 @Test
71 @DisplayName("result can be negative")
72 void subtractResultingInNegative() {
73 assertEquals(-5, calculator.subtract(0, 5));
74 }
75 }
76
77 @Nested
78 @DisplayName("multiply")
79 class Multiply {
80
81 @Test
82 @DisplayName("multiplies two positive numbers")
83 void multipliesPositiveNumbers() {
84 assertEquals(12, calculator.multiply(3, 4));
85 }
86
87 @Test
88 @DisplayName("multiplying by zero yields zero")
89 void multiplyByZero() {
90 assertEquals(0, calculator.multiply(123, 0));
91 }
92
93 @Test
94 @DisplayName("multiplying two negatives yields a positive")
95 void multiplyTwoNegatives() {
96 assertEquals(6, calculator.multiply(-2, -3));
97 }
98 }
99
100 @Nested
101 @DisplayName("divide")
102 class Divide {
103
104 @Test
105 @DisplayName("divides two positive numbers exactly")
106 void dividesEvenly() {
107 assertEquals(2.0, calculator.divide(6, 3), 0.0001);
108 }
109
110 @Test
111 @DisplayName("division that does not divide evenly keeps the fraction")
112 void dividesWithRemainder() {
113 assertEquals(1.0 / 3.0, calculator.divide(1, 3), 0.0001);
114 }
115
116 @Test
117 @DisplayName("dividing by zero throws ArithmeticException (invalid input)")
118 void divideByZeroThrows() {
119 ArithmeticException thrown = assertThrows(ArithmeticException.class, () -> calculator.divide(10, 0));
120 assertEquals("Division by zero", thrown.getMessage());
121 }
122
123 @Test
124 @DisplayName("zero dividend with a non-zero divisor is zero, not an error")
125 void zeroDividend() {
126 assertEquals(0.0, calculator.divide(0, 42), 0.0001);
127 }
128 }
129
130 @ParameterizedTest(name = "boundary: {0} {1} {2} = {3}")
131 @DisplayName("boundary values: Integer.MIN_VALUE / Integer.MAX_VALUE")
132 @CsvSource({
133 "2147483647, add, 0, 2147483647",
134 "-2147483648, add, 0, -2147483648",
135 "0, subtract, 2147483647, -2147483647",
136 })
137 void boundaryValues(int a, String operation, int b, int expected) {
138 int result;
139
140 switch (operation) {
141 case "add":
142 result = calculator.add(a, b);
143 break;
144
145 case "subtract":
146 result = calculator.subtract(a, b);
147 break;
148
149 default:
150 throw new IllegalArgumentException("Unsupported operation in test data: " + operation);
151 }
152
153 assertEquals(expected, result);
154 }
155 }