StringTest

1. contains

  • 용도 : 배열이나 컬렉션이 특정 요소들을 포함하고 있는지 검증할때 사용
  • 순서 무관
  • 부분일치 허용 : 배열이나 컬렉션에 더 만은 요소가 있어도 지정한 요소들이 모두 포함되어있다면 테스트 통과

2. containsExactly

  • 용도 : 배열이나 컬렉션의 요소들이 정확히 일치하는지 검증할 때 사용
  • 순서 상관 있음 : 배열이나 컬렉션의 요소가 정확히 같은 순서로 있어야 함
  • 완전 일치 : 지정된 요소들이 배열이나 컬렉션의 요소들과 순서와 내용까지 모두 일치해야 테스트 통과
** 주어진 구분자가 문자열 내에 없을 경우, 전체 문자열을 하나의 요소로 간주하여 반환한다 **

@Test
    @DisplayName("문자열을 ','로 분리했을 때 올바르게 분리되는지 확인")
    void splitTest() {
        //given
        String input1 = "1,2,3";
        String input2 = "1";

        //when
        String[] result1 = input1.split(",");
        String[] result2 = input2.split(",");

        //then
        assertThat(result1).contains("1", "2"); // 통과
//        assertThat(result1).containsExactly("1", "2");   3이 빠졌으므로 -> X
        assertThat(result2).contains("1"); // 통과
        assertThat(result2).containsExactly("1"); // 통과
    }
    
Expected :[1, 2]
Actual   :[1, 2, 3]

 

3. assertThatThrownBy

  • 용도 : 특정 코드 블록이 예외를 발생시키는지 검증할 때 사용
  • 포괄적인 예외 검증 : 특정 예외 타입이 아니라 예외 발생 자체에 중점을 두며, 예외 타입과 메시지를 함께 검증할 수 있다.
  • 메시지 검증 : 예외 메시지를 검증
    • hasMessageContaining(String text) :
      • 예외 메시지에 특정 문자열이 포함되어 있는지 검증
      • 부분 문자열 일치만을 확인하기 때문에, 메시지의 일부분만 검증할때 유용

4. assertThatExceptionOfType

  • 용도 : 특정 예외 타입이 발생하는지 명확히 검증할 때 사용
  • 예외 타입 명시 : 예외 타입을 처음부터 지정하여 해당 타입의 예외가 발생하는지 명확히 드러냄
  • 메시지와 원인 검증 : 메시지와 원인까지 세밀하게 검증
    • withMessage(String message)
      • 예외 메시지가 정확히 일치하는지 검증 %s, %d 같은 서식 문자와 사용 가능 
    • withMessageContaining(String text)
      • 예외 메시지에 특정 문자열이 포함되어 있는지 검증
      • hasMessageContaining과 같은 역할이지만 assertThatExceptionOfType와 사용
    • withMessageMatching(String regex)
      • 예외 메시지가 정규 표현식과 일치하는지 검증
    • withNoCause()
      • 예외에 원인(cause)이 없는지를 검증
      • 외가 다른 예외에 의해 발생하지 않았음을 확인
    • isThrownBy(() -> { // 예외가 발생할 코드})
      • 람다 표현식 : 예외를 발생시키는 코드를 람다 표현식으로 작성한다.
      • 예외 발생 코드 : 직접 예외를 throw 하거나 , 특정 조건에서 예외를 발생시키는 메소드를 호출한다.
1. hasMessageContaining(String text)

assertThatThrownBy(() -> { throw new Exception("Something went wrong: Error code 123"); })
    .isInstanceOf(Exception.class)
    .hasMessageContaining("Error code");  
    // "Error code"가 메시지에 포함되어 있는지 확인


2. withMessage(String message)

assertThatExceptionOfType(IOException.class)
    .isThrownBy(() -> { throw new IOException("Error code: 404"); })
    .withMessage("Error code: %d", 404);  
    // 정확히 일치하는지 서식 문자 %d를 사용해 메시지 검증


3. withMessageContaining(String text)

assertThatExceptionOfType(IllegalArgumentException.class)
    .isThrownBy(() -> { throw new IllegalArgumentException("Invalid argument: value too high"); })
    .withMessageContaining("value too high");  
    // 메시지에 "value too high"가 포함되어 있는지 확인


4. withMessageMatching(String regex)

assertThatExceptionOfType(IndexOutOfBoundsException.class)
    .isThrownBy(() -> { throw new IndexOutOfBoundsException("Index: 5, Size: 3"); })
    .withMessageMatching("Index: \\d+, Size: \\d+");  
    // "Index: 숫자, Size: 숫자" 형식 검증


5. withNoCause()

assertThatExceptionOfType(IOException.class)
    .isThrownBy(() -> { throw new IOException("boom!"); })
    .withNoCause(); 
    // 예외에 원인이 없는지 확인
    
    
6. isThrownBy(() -> { // 예외가 발생할 코드})

assertThatExceptionOfType(IOException.class)
    .isThrownBy(() -> {
        throw new IOException("File not found");
    })
    .withMessage("File not found");
    
assertThatExceptionOfType(StringIndexOutOfBoundsException.class)
    .isThrownBy(() -> {
        String str = "abc";
        char ch = str.charAt(5); // 유효하지 않은 인덱스 접근
    })
    .withMessageMatching("String index out of range: \\d+");

 

  • 자주 발생하는 Exception의 경우 Exception 별 메소드 제공
    • assertThatIllegalArgumentException()
    • assertThatIllegalStateException()
    • assertThatIOException()
    • assertThatNullPointerException()

 

Set Collection Test

1. BeforeEach && AfterEach

 

  • beforeEach : 각 테스트가 실행되기 전에 특정 작업을 수행하고자 할 때 사용. 테스트마다 공통적으로 설정해야 하는 초기화 작업을 설정
  • afterEach : 각 테스트가 끝난 후에 특정 작업을 수행하고자 할 때 사용. 주로 리소스 정리, 데이터 초기화 등과 같은 작업을 수행하는 데 사용
private Set<Integer> numbers;
@BeforeEach
void setUp(){
    numbers = new HashSet<>();
    numbers.add(1);
    numbers.add(2);
    numbers.add(3);
}

@AfterEach
void tearDown(){
    numbers.clear();
    System.out.println("AfterEach: Set이 초기화되었습니다.");
}

 

 

2. ParameterizedTest

 

  • 중복 코드 감소 : 다양한 입력 값으로 테스트할 수 있어, 반복적으로 비슷한 코드를 작성하지 않음
  • 가독성 향상 : 입력 값만 다를 뿐 같은 로직을 테스트하는 경우, 테스트 코드를 간결하게 유지 가능
  • 유연한 데이터 입력 : 다양한 데이터 소스와 함께 사용하여, 원하는 데이터를 손쉽게 전달 가능
    • 데이터소스
      • ValueSource : 단일 파라미터의 배열 형태로 데이터를 전달할 때 사용
      • CsvSource : 여러 인자를 CSV 형식으로 전달할 때 사용
      • CsvFileSource : 외부 CSV 파일을 데이터 소스로 사용할 때 유용
      • EnumSource : 특정 enum 타입의 값으로 테스트를 반복할 때 사용
      •  MethodSource : 메소드에서 직접 데이터를 생성해 테스트에 사용
      •  ArgumentsSource : 사용자 정의 ArgumentsProvider를 사용해 원하는 데이터 형식으로 값을 전달
1.@ValueSource

@ParameterizedTest
@ValueSource(ints = {1, 3, 5})
void testIsOdd(int number) {
    assertTrue(number % 2 != 0);
}


2.@CsvSource

@ParameterizedTest
@CsvSource({"apple, 5", "banana, 6", "cherry, 6"})
void testStringLength(String word, int expectedLength) {
    assertEquals(expectedLength, word.length());
}


3.@CsvFileSource

@ParameterizedTest
@CsvFileSource(resources = "/data.csv", numLinesToSkip = 1) // 첫 줄은 헤더
void testStringLengthFromFile(String word, int expectedLength) {
    assertEquals(expectedLength, word.length());
}


4.@EnumSource

@ParameterizedTest
@EnumSource(Fruit.class) // Fruit는 enum 타입
void testEnum(Fruit fruit) {
    assertNotNull(fruit); // enum 값이 null이 아닌지 확인
}


5.@MethodSource

@ParameterizedTest
@MethodSource("provideNumbers")
void testGreaterThanZero(int number) {
    assertTrue(number > 0);
}

static Stream<Integer> provideNumbers() {
    return Stream.of(1, 2, 3);
}


6.@ArgumentsSource

@ParameterizedTest
@ArgumentsSource(CustomArgumentsProvider.class)
void testStringLength(String word, int expectedLength) {
    assertEquals(expectedLength, word.length());
}

 

이메일, 전화번호를 위한 정규식은 검색해서 써본적이 있는데 "//" 와 "\n" 사이의 문자를 구분해서 사용하는 것은 생각할때는 쉬웠는데 막상 생각해보니 그렇게 쉽지는 않았다.

Pattern과 Matcher는 공식 문서를 봐도 무슨 소린지 잘 모르겠고 블로그들을 찾아봐도 솔찍히 이해가 되지 않아서 간단한 코드로 제출했다.

public String[] customDelimiter(String input) {
        int idx = input.indexOf("\\n");
        String customDelimiter = input.substring(2, idx); // 구분자 추출
        System.out.println("customDelimiter = " + customDelimiter);
        String escapedDelimiter = Pattern.quote(customDelimiter);
        return str.split(escapedDelimiter); // 이스케이프된 구분자로 분리
    }
String escapedDelimiter = Pattern.quote(customDelimiter);
간단하게도 qoute 메서드 하나로 구분자가 잘 작동하게 되었다.
Pattern.qoute()는 특수 문잦가 포함된 문자열을 정규식으로 안전하게 사용할 수 있도록 해주는 메서드이다
자열을 정규식 패턴으로 사용할 때 특수 문자(., *, ?, [, ], (, ), {, }, |, ^, $, \ 등)가 있으면 예기치 않은 동작을 할 수 있는데, 
Pattern.quote()를 사용하면 이러한 특수 문자를 일반 문자열로 처리할 수 있다.

 

그런데 구분자로 " \n " 이 들어갈때라던가
String customDelimiter = input.substring(2, idx);
String str = input.substring(idx + 2);

특히 저 두줄 코드가 너무 마음에 들지 않아 끝나고 1주일이 지났지만 마음에 안드는 부분을 바꿨다.

이해되지 않아도 이렇게 저렇게 하다보니 조금씩 이해가 됬고, 결국 지저분하게라도 바꾸긴 바꾸었다.

    public String[] customDelimiter(String input) {
        String result = "";
        String START = "//";
        String END = Pattern.quote("\\n");

        Pattern pattern = Pattern.compile(START + "(.*?)" + END);
        Matcher matcher = pattern.matcher(input);
        
        if (matcher.find()) {
            String delimiter = matcher.group(1);
            int idx = matcher.end();
            String splitInput = input.substring(idx);
            return splitInput.split(Pattern.quote(delimiter));
        }
    }
}


*******  if (matcher.find()) { } 를 **무조건** 실행하여 true를 반환하고,    *******
*******			Matcher 객체는 해당 매칭 결과를 내부에 저장한다.     *******

			matcher.find() 메소드를 실행하지 않으면
Exception in thread "main" java.lang.IllegalStateException: No match found 가 발생한다.

 

덧셈할 문자열을 입력해 주세요.
//\t\n1\t2\t3
delimiter = \t

delimiter 로 \n을 넣었을때는 끝까지 성공하지 못했다.

 

 

'프리코스 > 1주차' 카테고리의 다른 글

Java 정규식, Pattern, Matcher  (0) 2024.10.30
Git Commit Message Conventions 정리  (0) 2024.10.24
pr 전 체크리스  (0) 2024.10.18

1. 정규식이란?

문자열 데이터 중에서 원하는 조건과 일치하는 문자열 부분을 찾아 정의하는 표현식으로, 텍스트 검색, 추출, 매칭 등에 매우 유용하게 쓰인다.

  • 이메일, 전화번호와 같은 특정 형식의 데이터 검증
  • 특정 패턴에 맞는 문자열 찾기 및 대체
  • 로그 파일에서 원하는 정보만 추출하기

정규표현식 기호 모음

기호 표현 예시
. 임의의 한 문자 (개행 제외) a.b : acb, a1b
^ 시작을 의미한다.[] 괄호 안에 있다면 일치하지 않는 부정의 의미로 쓰인다. ^a : a로 시작하는 단어
[^a] :  a 아닌 철자 외 문자 1
$ $앞의 문자열로 문자가 끝나는지를 의미한다 a$ : a로 끝나는 단어
[abc] a, b, 또는 c 중 하나 [aeiou] : a, e
[^abc] a, b, c가 아닌 문자 [^a-z] : 알파벳 소문자 a부터 z까지를 제외한 모든 문자
[a-zA-Z] 알파벳 대소문자 [a-zA-Z] → 모든 영문자
\d 숫자 (0-9) \d+ : 숫자 연속
\D 숫자가 아닌 문자 \D+ : 숫자 제외 문자
\w 알파벳 대소문자+숫자+_와 동일 \w+ : [a-zA-Z_0-9]
\W \w의 부정 \W+ : [^a-zA-Z_0-9]
+ 1번 이상 반복 a+ : a, aa, aaa
* 0번 이상 반복 a* : aaa, a, ""(빈문자열) 
? 0번 또는 1번 반복 a? : a, ""(빈문자열)
() 그룹, 캡쳐 (ha)+ : haha , hahaha ...
(?:) 캡쳐하지 않는 그룹 찾지만 그룹에 포함 안
{n} 정확히 n개  a{3} : aaa(o) ...
{n,} n번 이상 반복 a{2} : aa(o) ...
{n,m} n번 이상 m번 이하 반복 a{2,5} : a(x) , aa(o) ... aaaaa(o) 

자주 사용되는 정규식 샘플

정규 표현식 설명
^[0-9]*$ 숫자
^[a-zA-Z]*$ 영문자
^[가-힣]*$ 한글
\\w+@\\w+\\.\\w+(\\.\\w+)? 이메일
^\d{2,3}-\d{3,4}-\d{4}$ 전화번호
^01(?:0|1|[6-9])-\d{3,4}-\d{4}$ 휴대전화번
\d{6} - [1-4]\d{6} 주민등록번호
^\d{3}-\d{2}$ 우편번호

2. Pattern 클래스

Pattern 클래스는 정규식 패턴을 정의하고 컴파일 하는 역할을 한다. 정규식을 매번 해석하는 것은 성능상 비효율적이므로, Pattern 객체를 사용해 정규식을 미리 컴파일해 두면 이후 같은 패턴을 반복적으로 사용할때 좋다.

메서드 설명 예시
Pattern compile(String regex) 주어진 정규표현식(regex) 패턴을 컴파일하여 Pattern 객체를 생성합니다. Pattern pattern = Pattern.compile("\\d+");
Boolean matches(String regex, CharSequence input) 정규표현식(regex)과 입력 문자열(input)이 완전히 일치하는지 검사합니다. 일치하면 true, 그렇지 않으면 false를 반환합니다. boolean result = Pattern.matches("\\d+", "12345"); // true
Predicate<String> asPredicate() 문자열을 일치시키는 데 사용할 수 있는 Predicate<String>을 작성합니다. Predicate<String> predicate = pattern.asPredicate();
String pattern() 컴파일된 정규표현식을 String 형태로 반환합니다. String regex = pattern.pattern();
String[] split(CharSequence input) 입력 문자열(input)을 정규표현식 패턴에 따라 분리하여 문자열 배열로 반환합니다. String[] result = pattern.split("one1two2three");

 

Pattern pattern = Pattern.compile("\\d+");
String[] result = pattern.split("Java8, Java11, Java17");
for (String s : result) {
    System.out.println(s);
}
/*
* 결과
* Java
* , Java
* , Java
*/

3. Matcher 클래스

Matcher 클래스는 Pattern 객체와 문자열을 매칭시켜주는 역할을 한다. Matcher 객체는 Pattern 객체의 matcher() 메서드를 통해 얻을 수 있다. 이 후 매칭된 문자열을 확인하거나 특정 작ㅇ겁을 수행할 수 있다.

메서드 설명 예시
boolean find() 패턴이 일치하는 경우 true를 반환, 불일치하는 경우 false를 반환. 여러 개가 매칭될 경우 반복 실행으로 이어서 매칭됨 boolean result = matcher.find();
boolean(int start) 지정된 start 위치 이후부터 매칭 검색 boolean result = matcher.find(5);
int start() 매칭되는 문자열의 시작 위치 반환 int startPos = matcher.start();
int start(int group) 지정된 그룹이 매칭되는 시작 위치 반환 int startPos = matcher.start(1);
int end() 매칭되는 문자열 끝 위치의 다음 문자 위치 반환 int endPos = matcher.end();
int end(int group) 지정된 그룹이 매칭되는 끝 위치의 다음 문자 위치 반환 int endPos = matcher.end(1);
String group() 매칭된 부분 문자열 반환 String match = matcher.group();
String group(int group) 그룹화된 패턴 중 지정된 group 번째 부분 반환 String match = matcher.group(1);
int groupCount() 괄호로 그룹화된 전체 패턴의 개수 반환 int count = matcher.groupCount();
boolean matches() 패턴이 전체 문자열과 일치할 경우 true 반환 (일부 문자열이 아닌 전체와 일치해야 함) boolean result = matcher.matches();

 

Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("Java 8, Java 11, Java 17");

while (matcher.find()) {
	System.out.println("Found: " + matcher.group() + ", Start: " + matcher.start() + ", End: " + matcher.end());
}
        /*
        * 결과
        * Found: 8, Start: 5, End: 6
        * Found: 11, Start: 13, End: 15
        * Found: 17, Start: 22, End: 24
        */

'프리코스 > 1주차' 카테고리의 다른 글

1주차 끝나고  (3) 2024.10.30
Git Commit Message Conventions 정리  (0) 2024.10.24
pr 전 체크리스  (0) 2024.10.18

 

커밋 메시지의 형식

커밋 메시지의 모든 줄은 100자를 넘을수 없다.

<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

예시

docs(readme): 구현할 기능 목록 추가

목록 설명

Closes #123, #245, #992

 

종류(type)

종류는 크게 7가지가 있습니다.

  • feat : 새로운 기능 추가
  • fix : 버그 수정
  • docs : 문서 수정
  • style : 코드 변경 응답, 세미콜론 누락 등
  • refactor : 코드 리팩토링
  • test : 테스트 추가, 수정
  • chore: 빌드, 패키지 설정 업데이트 등 기타 변경사항

<subject>

  • 커밋의 내용을 간결하고 구체적으로 작성
  • 명령형, 현재형을 사용한다 - 변경됨 x ,  변경 o
  • 첫 글자는 소문자로 쓴다
  • 끝에 점(.)이 없다

<body>

  • 명령형, 현재형을 사용한다 - 변경됨 x ,  변경 o
  • 변경의 이유를 포함하고 이전 상황과 대조한다.

<footer>

중요 변경 사항 ( Breaking changes )

모든 주요 변경 사항은 변경 사항 설명, 정당성 및 마이그레이션 참고 사항과 함께 바닥글에 언급되어야 한다.

BREAKING CHANGE: 분리 범위 바인딩 정의가 변경되었습니다
	지시 컨트롤러 주입에 대한 주입 옵션이 제거되었습니다.
    
    Before:
    
	범위: {
		myAtttr: '속성',
		myBind: 'bind',
		나의 표현식: '표현식',
		myEval: '평가',
        myAccessor: '액세스'
      }

    After:

    범위: {
        myAtttr: '@',
        myBind: '@',
        내 표현식: ' &',
        // myEval - 일반적으로 유용하지는 않지만, 표현식을 할당할 수 있는 경우 '='를 사용할 수 있습니다
        myAccessor: '=' // 디렉티브의 템플릿에서 myAccessor()를 myAccessor로 변경합니다
      }
    	제거된 '주사'는 일반적으로 지침에 유용하지 않으므로 이를 사용하는 코드가 없어야 합니다.

문제 참조 ( Referencing issues )

  • 유형: #이슈 번호 형식으로 작성한다.
  • 여러 개의 이슈번호는 쉼표(,)로 구분한다.
Closes #123, #245, #992

 

 

참고 : AngularJS Git Commit Message Conventions , https://meetup.nhncloud.com/posts/106

 

Git Commit Message Conventions

Git Commit Message Conventions. GitHub Gist: instantly share code, notes, and snippets.

gist.github.com

 

'프리코스 > 1주차' 카테고리의 다른 글

1주차 끝나고  (3) 2024.10.30
Java 정규식, Pattern, Matcher  (0) 2024.10.30
pr 전 체크리스  (0) 2024.10.18
  • 자바 코드 컨벤션을 지키면서 프로그래밍했는가?
    https://google.github.io/styleguide/javaguide.html 참고
  • 한 메서드에 오직 한 단계의 들여쓰기(indent)만 허용했는가?
  • else 예약어를 쓰지 않았는가?
  • 모든 원시값과 문자열을 포장했는가?
  • 콜렉션에 대해 일급 콜렉션을 적용했는가?
  • 3개 이상의 인스턴스 변수를 가진 클래스를 구현하지 않았는가?
  • getter/setter 없이 구현했는가?
  • 메소드의 인자 수를 제한했는가?
  • 코드 한 줄에 점(.)을 하나만 허용했는가?
  • 메소드가 한가지 일만 담당하도록 구현했는가?
  • 클래스를 작게 유지하기 위해 노력했는가?

참조 : 클린코드 원칙

'프리코스 > 1주차' 카테고리의 다른 글

1주차 끝나고  (3) 2024.10.30
Java 정규식, Pattern, Matcher  (0) 2024.10.30
Git Commit Message Conventions 정리  (0) 2024.10.24
  1. 요구사항 분석 : 미션의 요구사항이 무엇인지, 왜 필요한지, 그리고 무엇이 필요한지 파악
  2. 지식 격차 확인 : 무엇을 모르는지, 부족한 지식을 파악
  3. 학습 계획 수립 : 학습할 내용을 설정
  4. 진행 : 시도와 과정을 기록
  5. 클린코드 준수 점검 : 클린 코드 원칙을 지키는지 점검
  6. 정리 : 공부한 내용, 느낀점, 개선점, 기록

'프리코스' 카테고리의 다른 글

[우아한테크코스] 프리코스를 시작하면서  (0) 2024.10.18

공부를 그만둔지 1년 반 정도가 다되간다. 

도망쳤지만 이번 기회를 통해 다시 한번 공부해보려고 한다.

다시 한번 도전하는 만큼, 전에 했던 실수들을 반복하지 않고 성장하며 과정을 완주해보려고 한다.

 

'프리코스' 카테고리의 다른 글

주차 별 기록 목표  (2) 2024.10.18

+ Recent posts