public class Calculator {
int add(int i, int j ) {
return i+j;
}
int subtract(int i, int j ) {
return i-j;
}
int multiply(int i, int j ) {
return i*j;
}
int divide(int i, int j ) {
return i/j;
}
public static void main() {
Calculator cal = new Calculator();
System.out.println(add(3,4));
System.out.println(subtract(3,4));
System.out.println(multiply(3,4));
System.out.println(divide(3,4));
}
}
기본적으로 테스트할 때 쓰는 코드 실제 서비스를 담당하는 프로덕션 코드와 이 프로덕션 코드가 정상적으로 동작하는지 확인하기 위한 main()으로 나뉜다
문제점: 프로덕션 코드와 테스트 코드(main() 메소드)가 같은 클래스에 위치 : 서비스 시점에 테스트 코드를 같이 배포할 필요가없음
-> 1차적 해결법: 서비스 코드와 테스트 코드를 다른 클래스로 나누자!
class CalculatorTest {
public static void main() {
Calculator cal = new Calculator();
System.out.println(add(3,4));
System.out.println(subtract(3,4));
System.out.println(multiply(3,4));
System.out.println(divide(3,4));
}
}
이 경우에는 서비스 코드와 테스트 코드가 분리는 되어있지만 main() 하나의 메소드에서 프로덕션의 여러 코드의 여러 메소드를 동시에 테스트 하고 있다. 이는 프로덕션 코드의 복잡도가 증가하면 할 수록 main() 메소드의 복잡도도 증가하고, 결과적으로 main() 메소드를 유지하는데 부담이 된다.
-> 2차적 해결법: JUnit을 활용하기
package calcualtor;
import org.junit.Test;
public class CalculatorTest {
@Test
public void add() {
Calculator cal = new Calculator();
System.out.println(cal.add(6,3));
}
@Test
public void subtract() {
Calculator cal = new Calculator();
System.out.println(cal.subtract(6, 3));
}
}
JUnit Test 구현을 통해서 메소드 각각을 실행해볼 수 있다 (독립적 실행이 가능) 다른 메소드에 영향을 받지 않기 때문에 내가 현재 구현하고 있는 프로덕션 코드에 집중할 수 있다.
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CalculatorTest {
@Test
public void add() {
Calculator cal = new Calculator();
assertEquals(9, cal.add(6, 3));
}
@Test
public void subtract() {
Calculator cal = new Calculator();
assertEquals(3, cal.subtract(6, 3));
//assertEquals(2, cal.subtract(6, 3));
}
}
![]() | ![]() |
---|
왼쪽은 subtract()에서 주석 없는 부분 오른쪽은 주석있는 부분 Test 결과
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CalculatorTest {
private Calculator cal; = new Calculator();
@Test
public void add() {
assertEquals(9, cal.add(6, 3));
}
@Test
public void subtract() {
assertEquals(3, cal.subtract(6, 3));
}
}
import static org.junit.Assert.assertEquals;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class CalculatorTest {
private Calculator cal;
@Before
public void setup() {
cal = new Calculator();
System.out.println("Before");
}
@Test
public void add() {
assertEquals(9, cal.add(6, 3));
System.out.println("add");
}
@Test
public void subtract() {
assertEquals(3, cal.subtract(6, 3));
System.out.println("subtract");
}
@After
public void teardown() {
System.out.println("teardown");
}
}
출력 값
Before
subtract
teardown
Before
add
teardown
메소드 하나를 테스트 할 때 마다 Before에 해당하는 메서드를 실행하고 테스트 메서드 실행 후에 After에 해당하는 메서드를 실행한다.
전달하는 문자를 구분자로 분리한 후 숫자의 합을 구해 반환한다
- 쉼표 (,) 또는 콜론 (:)으로 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다
예) " " => 0, "1,2" => 3, "1,2,3" => 6 "1,2:3" => 6
- 쉼표, 콜론 외에도 커스텀 구분자를 지정할 수 있음 문자열 앞 부분의 "//" 와 "\n"사이에 위치하는 문자를 커스텀 문자로 사용
예) "//;n1;1;2;3"과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;) 이며 결과값은 6을 반환해야한다.
- 문자열 계산기에 음수를 전달하는 경우 RuntimeException으로 예외 처리해야한다.
public class StringCalculator {
public int add(String text) {
if(text == null || text.isEmpty()) {
return 0;
}
return 0;
}
}
import static org.junit.jupiter.api.Assertions.*;
import org.junit.Before;
import org.junit.jupiter.api.Test;
class StringCalculatorTest {
private StringCalculator cal;
@Before
public void setup() {
cal = new StringCalculator();
}
@Test
public void add_null() {
//null값 또는 빈 문자열을 넣었을 때
assertEquals(0, cal.add(null));
assertEquals(0, cal.add(""));
}
}
public class StringCalculator {
public int add(String text) {
if(text == null || text.isEmpty()) {
return 0;
}
return Integer.parseInt(text);
}
}
public class StringCalculatorTest {
[...] 생략
@Test
public void add_oneNum() {
//숫자 하나를 문자열로 넣었을 때
assertEquals(0, cal.add("1"));
}
}
public class StringCalculator {
public int add(String text) {
if(text == null || text.isEmpty()) {
return 0;
}
if(text.contains(",")) {
String[] values = text.split(",");
int sum = 0;
for(String value : values) {
sum += Integer.parseInt(value);
}
return sum;
}
return Integer.parseInt(text);
}
}
public class StringCalculatorTest {
[...] 생략
@Test
public void add_shimpyo() {
//숫자 두개를 쉽표로 구분된 문자열을 넣었을 때
assertEquals(3, cal.add("1,2"));
}
}
public class StringCalculator {
public int add(String text) {
if (text == null || text.isEmpty()) {
return 0;
}
String[] values = text.split(",");
return sum(values);
}
private int sum(String[] values) {
int sum = 0;
for(String value: values) {
sum+=Integer.parseInt(value);
}
return sum;
}
}
public class StringCalculator {
public int add(String text) {
if (text == null || text.isEmpty()) {
return 0;
}
String[] values = split(text);
return sum(values);
}
[...]
private String[] split(String text) {
return text.split(",|:");
}
}
public class StringCalculatorTest {
private StringCalculator cal;
@Before
public void setup() {
cal = new StringCalculator();
}
[...]
@Test
public void add_shimOrCol() {
//숫자가 쉼표 또는 콜론으로 구분된 문자열을 넣었을 때
assertEquals(6, cal.add("1,2:3"));
}
}
public class StringCalculator {
public int add(String text) {
if (isBlank(text)) {
return 0;
}
String[] values = split(text);
return sum(values);
}
[...]
private String[] split(String text) {
//패턴과 매칭되는 문자열을 넣는다
Matcher m = Pattern.compile("//(.)\n(.*)").matcher(text);
//매칭되는 구분자가 있다면
if(m.find()) {
String customDelimeter = m.group(1);
return m.group(2).split(customDelimeter);
}
return text.split(",|:");
}
[...]
}
public class StringCalculatorTest {
private StringCalculator cal;
@Before
public void setup() {
cal = new StringCalculator();
}
[...]
@Test
public void add_custom() throws Exception{
//커스텀 문자열이 들어오면
assertEquals(6, cal.add("//;\n1;2;3"));
}
}
public class StringCalculator {
public int add(String text) {
if (isBlank(text)) {
return 0;
}
String[] values = split(text);
return sum(values);
}
private int sum(String[] values) {
int sum = 0;
for(String value: values) {
sum+=toPositive(value);
}
return sum;
}
[...]
private int toPositive(String value) {
int number = Integer.parseInt(value);
if(number < 0 ) {
throw new RuntimeException();
}
return number;
}
public class StringCalculatorTest {
private StringCalculator cal;
@Before
public void setup() {
cal = new StringCalculator();
}
[...]
@Test(expected = RuntimeException.class)
public void add_negative() throws Exception{
cal.add("-1,2,3");
}
}
package stringcalculator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class StringCalculator {
public int add(String text) {
if (isBlank(text)) {
return 0;
}
String[] values = split(text);
return sum(values);
}
private int sum(String[] values) {
int sum = 0;
for(String value: values) {
sum+=toPositive(value);
}
return sum;
}
private String[] split(String text) {
//패턴과 매칭되는 문자열을 넣는다
Matcher m = Pattern.compile("//(.)\n(.*)").matcher(text);
//매칭되는 구분자가 있다면
if(m.find()) {
String customDelimeter = m.group(1);
return m.group(2).split(customDelimeter);
}
return text.split(",|:");
}
private boolean isBlank(String text) {
return text == null || text.isEmpty();
}
private int toPositive(String value) {
int number = Integer.parseInt(value);
if(number < 0 ) {
throw new RuntimeException();
}
return number;
}
}
package stringcalculator;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.Before;
import org.junit.Test;
public class StringCalculatorTest {
private StringCalculator cal;
@Before
public void setup() {
cal = new StringCalculator();
}
@Test
public void add_null() {
//null값 또는 빈 문자열을 넣었을 때
assertEquals(0, cal.add(null));
assertEquals(0, cal.add(""));
}
@Test
public void add_oneNum() throws Exception {
//숫자 하나를 문자열로 넣었을 때
assertEquals(1, cal.add("1"));
}
@Test
public void add_shimpyo() {
//숫자 두개를 쉼표로 구분된 문자열을 넣었을 때
assertEquals(3, cal.add("1,2"));
}
@Test
public void add_shimOrCol() {
//숫자가 쉼표 또는 콜론으로 구분된 문자열을 넣었을 때
assertEquals(6, cal.add("1,2:3"));
}
@Test
public void add_custom() throws Exception{
//커스텀 문자열이 들어오면
assertEquals(6, cal.add("//;\n1;2;3"));
}
@Test(expected = RuntimeException.class)
public void add_negative() throws Exception{
cal.add("-1,2,3");
}
}