ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 인수 테스트 코드 리팩터링-1
    Today I Learned 2023. 1. 3. 21:13

    인수 테스트 코드를 수정하는 과정에서 사용한 것들을 정리했다.

     

    CodeceptJS에서 className으로 요소 확인하기

    인수 테스트에서 class 속성을 갖는 요소의 화면 존재 여부를 다음과 같이 확인할 수 있다.

     

    // src/components/post/PostGameMembers.jsx
    
    <button className="member">
      // elements
    </button>

     

    // tests/registration_acceptance_test.js
    
    I.seeElement('.member');

     

     

    CodeceptJS에서 입력 필드 값을 비우면서, 입력 필드 상태에도 반영시키기

    게시글 작성 기능 중, 필수 입력 폼에 내용을 입력하지 않고 게시글 작성을 시도하는 경우 예외를 발생시키는 동작의 인수 테스트를 다음과 같이 수행하려고 했다.

     

    1. 모든 입력 폼에 내용을 입력한다.
    2. 특정 입력 폼의 내용을 비운다.
    3. '작성하기' 버튼을 누른다.
    4. 내용을 입력해달라는 예외 메시지를 확인한다.

     

    특정 입력 폼에만 내용이 입력되지 않은 경우를 만드는 방법에는 두 가지가 있었는데, 특정 입력 폼을 제외한 모든 입력 폼에 내용을 입력하게 하는 방법과, 모든 입력 폼에 내용을 입력한 뒤 특정 입력 폼의 내용만 비우는 방법이 있었다. 후자의 방법은 setps_file.js에 모든 입력 폼에 내용을 입력하는 로직을 메서드화할 수 있었기 때문에 재사용을 통해 코드 중복을 줄일 수 있어 후자의 방법을 선택했다.

     

    처음에는 입력 폼의 내용을 지우는 것을 다음과 같이 시도했다.

    // tests/writing_post_test.js
    // 다음의 두 가지 방법으로 시도했다.
    
    I.clearField('Label')
    I.fillField('Label', '')

     

    이렇게 테스트를 수행했을 때는 화면 상에서는 입력 필드가 지워지는 것이 확인되었지만, 인수 테스트에서 예외를 처리하지 않고 기존에 입력했었던 내용으로 게시글을 등록하는 문제가 발생했다. 디버깅을 수행한 결과, 입력 필드의 상태를 관리하고 있는 PostFormStore에 지워진 입력 필드의 상태가 반영되지 않는 것을 확인했다.

     

    문제 해결을 위해 웹에서 정보를 찾던 중 화면을 클릭하거나 키보드의 버튼을 누르는 것과 같은 동작을 CodeceptJS에서 수행할 수 있음을 확인하고 소스코드에 적용했다. 입력 필드를 마우스 클릭으로 직접 선택한 뒤, 입력 값들을 전체 선택하고 직접 지우는 방식의 테스트 코드를 작성해 문제를 해결했다.

     

    // steps_file.js
    
    const backdoorBaseUrl = 'http://localhost:8000/backdoor';
    
    module.exports = function () {
      return actor({
        setupWritingPostCase() {
          this.amOnPage(`${backdoorBaseUrl}/setup-writing-post-case`);
        },
        login({ username, password }) {
          // ...
        },
        fillPostForm() {
          // ...
        }
        clearInputField(locator) {
          this.doubleClick(locator);
          this.pressKey(['Shift', 'Home']);
          this.pressKey('Backspace');
        },
      });
    };
    // tests/writing_post_test.js
    
    Feature('4. 운동 모집 게시글 작성: '
      + '운동 팀원을 모집하는 사람이 '
      + '자신의 게시글을 운동을 찾는 사람들에게 노출시키기 위해 '
      + '운동 팀원을 모집하는 게시글을 작성할 수 있다.');
    
    Before(({ I }) => {
      I.clearDatabases();
    });
    
    Scenario('종목을 입력하지 않은 경우', ({ I }) => {
      // Given
      I.setupWritingPostCase();
      I.login({
        username: 'user1234',
        password: 'Password!1',
      });
    
      // When
      I.amOnPage('/write');
      I.fillPostForm();
      I.clearInputField('#input-game-exercise');
      I.click('작성하기');
    
      // Then
      I.seeElement('[placeholder="종목을 입력하지 않았습니다."]');
    });

     

    References

    - https://github.com/codeceptjs/CodeceptJS/issues/499

     

     

    CodeceptJS에서 체크박스 관련 동작 수행하기

    CodeceptJS에서 체크박스를 클릭하고, 체크박스가 클릭되거나 클릭되지 않았는지 다음과 같이 검증할 수 있다.

     

    // src/components/notices/NoticeTitle.jsx
    
    <Checkbox
      type="checkbox"
      id="1"
    />
    // tests/notice_test.js
    
    I.checkOption('[id="1"]');
    I.seeCheckboxIsChecked('[id="1"]');

     

    References

    - https://github.com/codeceptjs/CodeceptJS/issues/877

     

     

    JavaScript에서 Date 객체를 toISOString()으로 변환할 때 한국 시간을 기준으로 변환하기

    게시글을 등록한 뒤 등록된 게시글의 내용을 인수 테스트로 검증하는 과정에서 입력했던 운동 예정 날짜와 다른 날짜가 운동 예정 날짜로 등록되어 있는 문제를 확인했다. 디버깅을 진행한 결과, 게시글 작성 과정에서 입력한 시간과 서버에서 DTO로 전달받은 시간 값이 다르다는 점이 확인되었다.

     

    사용자가 입력한 날짜 값은 서버에 전달되기 전에 Date 객체를 문자열 형태로 변환하기 위해 .toISOString() 메서드를 이용해 문자열로 변환하고 있었다. 문제는 이처럼 Date 객체를 단순화한 확장 ISO 형식인 ISO 8601으로 변환할 경우, UTC Timezone 기준으로 변환되기 때문에 UTC+9인 한국 시간보다 9시간 이전의 시간 값으로 변환되는 문제가 있었다.

     

    문제 해결을 위해 Date 객체에 9시간만큼의 Offset을 더해준 뒤에 ISO 8601 형태로 변환하도록 하는 식으로 문제를 해결했다.

    const dateOffset = 1000 * 60 * 60 * 9;
    
    const date = (new Date(this.gameDate.getTime() + dateOffset)).toISOString();

     

    References

    - https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString

    - https://archijude.tistory.com/560?category=826157

     

     

    JPA로 List를 가져올 때 정렬된 상태로 가져오기

    // services/GetNoticesService.java
    
    public NoticesDto findAllNoticesOfUser(Long currentUserId) {
        List<Notice> notices
            = noticeRepository.findAllByUserId(
                currentUserId, Sort.by(Sort.Direction.DESC, "createdAt"));
    
        List<NoticeDto> noticeDtos = notices.stream()
            .filter(Notice::active)
            .map(Notice::toDto)
            .toList();
    
        return new NoticesDto(noticeDtos);
    }
    // repositories/NoticeRepository.java
    
    public interface NoticeRepository extends JpaRepository<Notice, Long> {
        List<Notice> findAllByUserId(Long userId, Sort sort);
    }

     

    References

    - https://lovemewithoutall.github.io/it/spring-data-sort/

     

     

     

     

    댓글

Designed by Tistory.