Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: 신청폼 희망분야 페이지 e2e 테스트 작성 #166

Merged
merged 11 commits into from
Aug 27, 2024

Conversation

geongyu09
Copy link
Collaborator

@geongyu09 geongyu09 commented Aug 24, 2024

주요 변경사항

신청폼 희망 분야 페이지(첫 페이지) e2e테스트 작성하였습니다.

기존에 작성해둔 유저 시나리오를 바탕으로 테스트케이스를 작성하였습니다.
테스트케이스의 경우 아래의 아티클을 많이 참고하여 중복되는 부분을 describe로 빼는 방식으로 작성하였습니다.
https://yozm.wishket.com/magazine/detail/2435/

  • 중복적으로 사용할 수 있을만한 로직(alert창 검사, 로컬스토리지값 검사)을 commands로 빼두었습니다.
  • 모든 테스트마다 쿠키, 로컬스토리지, 세션스토리지를 제거하도록 하여 테스트 고립을 실천하였습니다.
테스트 케이스 보기
  • 초기 상태에서
    • 질문 제목 네비게이션 클릭시 “필수 질문을 작성해주세요.” 알람창이 뜬다.
    • 질문 제목 네이게이션 클릭 시 “필수 질문을 작성해주세요.” alert창이 뜬다.
    • 다음 버튼 클릭시 “필수 질문을 작성해주세요.” alert창이 뜬다.
  • 개발자(디자이너/기획)를 누르면
    • 질문 제목 네이게이션에서 개발자(디자이너/기획자)에 대한 질문들을 볼 수 있다.
    • 질문 제목 네이게이션 클릭 시 “필수 질문을 작성해주세요.” alert창이 뜬다.
    • 다음 버튼 클릭시 “필수 질문을 작성해주세요.” alert창이 뜬다.
    • 1순위 분야를 선택하면
      • 2순위로는 동일한 분야를 선택할 수 없다.
      • 2순위를 선택한 후
        • 2순위와 동일한 분야를 1순위에서 선택할 수 없다.
        • 선택하지 않는 분야를 1순위에서 선택시 2순위 선택이 풀린다
        • 다음 버튼을 누르면 다음 질문(기본 인적사항)으로 넘어간다.
테스트 결과 화면 보기

image

추가 의문점

  • 테스트를 작성하는데 어느정도까지 작성을 해야 할지에 대해서 조금 고민이 들었습니다. 예를 들어서 "개발자(디자이너/기획)를 누르면 왼쪽 네비게이션바에 질문이 생긴다" 와 같은 유저 시나리오가 있다면, 개발자, 디자이너, 기획자 전부 테스트를 해야 할지에 대해서 고민이 들었으며, "2순위로 동일한 분야를 선택할 수 없다" 를 테스트 한다면, 나올 수 있는 모든 케이스를 해보는게 맞을지 고민이 들었습니다.

  • 클래스명, 혹은 useId() 의 id 등으로 가져오는 방식은 가독성도 떨어지고 유지보수적으로도 좋은 방법이 아닐 수 있겠다는 생각이 들었습니다. 다만, 구별되는 점을 찾기 어려운 경우(1순위, 2순위가 동일하게 분야 버튼이 생기는 경우. 아래 사진 참고) 어쩔 수 없이 cypress에서 찾아주는 선택자로 사용할 수 밖에 없어지는 것 같습니다. 해당 부분은 우선적으로 선언문을 통해서 get()한 부분이 어떤 부분인지를 알려주도록 하였습니다만 더 좋은 방법이 있을지 고민이 됩니다. ( testid 를 넣는 방식이 좋아 보이긴 합니다..!)

image

해당 부분에서 1순위의 APP을 가져오고자 한다면 cy.get('label').contains('APP') 을 할 수 있지만, 2순위의 APP을 가져올 때도 동일한 로직을 사용해야 합니다. eq(1)로도 안잡히네요.. 혹시나 방법을 아신다면 커맨트 달아주세요!

리뷰어에게...

  • 의문점에서도 적었듯 테스트를 조금 더 촘촘하게 작성해야 한다고 느껴지는 부분이 있는지 확인 부탁드립니다!
  • 2순위 선택 부분이 어쩔 수 없이 선택자를 사용하게 되었는데, 해당 부분이 가독성이 떨어지는지, 떨어진다면 어떻게 개선할 수 있을지 조언 부탁드립니다!
  • 추가적으로 더 테스트 해볼만한 플로우가 있을지 확인 부탁 드립니다.

관련 이슈

closes #160

@geongyu09 geongyu09 added the test ✅ 테스트 작성 label Aug 24, 2024
@geongyu09 geongyu09 requested review from 2yunseong and smb0123 August 24, 2024 10:53
@geongyu09 geongyu09 self-assigned this Aug 24, 2024
Copy link
Collaborator

@loopy-lim loopy-lim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

보고 많이 배웁니다... 저도 test를 거의 쓰지 않는 입장이라 보면서 배우고 있습니다. 아래의 내용에 대한 글을 잠시 끄적입니다.

테스트를 작성하는데 어느정도까지 작성을 해야 할지에 대해서 조금 고민이 들었습니다. 예를 들어서 "개발자(디자이너/기획)를 누르면 왼쪽 네비게이션바에 질문이 생긴다" 와 같은 유저 시나리오가 있다면, 개발자, 디자이너, 기획자 전부 테스트를 해야 할지에 대해서 고민이 들었으며, "2순위로 동일한 분야를 선택할 수 없다" 를 테스트 한다면, 나올 수 있는 모든 케이스를 해보는게 맞을지 고민이 들었습니다.

제 생각에는 test도 또한 모든 선택 사항입니다. 만약 모든 케이스를 고려하여 짜게 된다면, 그 또한 좋은 test코드, 더욱 견고한 test코드가 될 수 있겠죠. 하지만 만약 요구사항이 변경이 된다면 어떻게 되는지 한번 생각하면 좋을 것 같습니다.
추가적으로 저는 개발자만 짜더라도 한번에 같은 로직으로서 점검하는 코드를 짠다면 MC/DC를 만족하는코드를 작성할 수 있을 것이라고 생각합니다.

클래스명, 혹은 useId() 의 id 등으로 가져오는 방식은 가독성도 떨어지고 유지보수적으로도 좋은 방법이 아닐 수 있겠다는 생각이 들었습니다. 다만, 구별되는 점을 찾기 어려운 경우(1순위, 2순위가 동일하게 분야 버튼이 생기는 경우. 아래 사진 참고) 어쩔 수 없이 cypress에서 찾아주는 선택자로 사용할 수 밖에 없어지는 것 같습니다. 해당 부분은 우선적으로 선언문을 통해서 get()한 부분이 어떤 부분인지를 알려주도록 하였습니다만 더 좋은 방법이 있을지 고민이 됩니다. ( testid 를 넣는 방식이 좋아 보이긴 합니다..!)

클래스, id의 경우에는 유지보수에 좋지 않는 방식이 않다는 것에 저도 동의합니다. 다만, 저희가 사용하는 연산자는 get() 뿐 아니라 훨신 더 많은 연산자를 사용할 수 있음을 알면 좋을 것 같습니다. 그 element가 유니크한 것이라면 대부분의 use case에 대해 찾을 수 있도록 하였고, 만약 아니라면 왜 그 use case가 못찾는지, 코드가 잘못 짜여 있는지 확인하는 것도 좋아보입니다.


하나 생각난 것이 있다면... 아마 APP또한 하나의 매직 넘버와 같은 위치라고 생각합니다. 이들에 대해서 고민하자면, constants안에 있는 배열 중 0번째 배열의 것을 테스트 해보는 것으로 더욱 견고한 테스트를 작성할 수 있을 것이라고 생각합니다.

Comment on lines +10 to +23
"checkLocalStorage",
(key: string, expectedValue: string) => {
cy.window().then((win) => {
const actualValue = win.localStorage.getItem(key);
expect(actualValue).to.equal(expectedValue);
});
}
);

Cypress.Commands.add("checkAlert", (expectedValue: string) => {
cy.on("window:alert", (str) => {
expect(str).to.equal(expectedValue);
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오... 먼가 flux 패턴같은 느낌.... redux같은 느낌의 코드이군요 ㅋㅋㅋ

@@ -0,0 +1,121 @@
describe("chapter", () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

먼가... 이 또한 어떤 것임을 알려주는 describe면 좋겠군요...(저는 사실 회의를 참여 하지 않아서 제 3자라고 생각하고 보십쇼)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 해당 부분은 수정이 필요한게 맞습니다.. 저만 올바르게 작성하지 않았네요


describe("초기 상태(처음 진입시, 로컬 데이터가 없는 상태)에서", () => {
it("질문 제목 네비게이션 클릭시 “필수 질문을 작성해주세요.” 알람창이 뜬다. ", () => {
cy.get("button").contains("기본 인적 사항을 입력해주세요.").click();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 없이 개발할 때는 몰랐지만, 여기서 "기본 인적 사항을 입력해주세요"와 같은 string들은 constants라는 폴더에 한번에 다룬다면 나중에 2군데, 3군데 수정 없이 작동할 것 같다라는 생각이 드네요(이래서 따로 빼는거였나...?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞아요 조금 반복되는 부분이 있는 것 같습니다..!
반복되더라도 하드하게 작성한 이유는 예전 아래의 아티클을 읽고 이게 맞다고 생각을 했던 것 같습니다..
방금 다시 읽어보니 조금은 다른 문제를 다루고 있어서 제가 잘못 생각한 부분이 있는 것 같습니다.
해당 부분 수정해보겠습니다!

https://jojoldu.tistory.com/615

Comment on lines 17 to 21
it("다음 버튼 클릭시 “필수 질문을 작성해주세요.” alert창이 뜬다. ", () => {
cy.get("button").contains("다음").should("exist").click();

cy.checkAlert("필수 질문을 작성해주세요.");
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오... false에 대한 내용을 작성하시는 꼼꼼함까지... ㄷㄷㄷㄷ

Comment on lines 95 to 98
beforeEach(() => {
const secondChapterAPPButton = cy.get('[for=":r1:"]');
secondChapterAPPButton.click();
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

흑흑... 이게 무엇을 의미하는건가요...? label을 기점으로 button에 대한 설명이 부족한가요??

추가로 next 것을 사용할 수 있답니다.(물론 input이 무조건 label뒤에 있어야하지만, 일반적으로 그렇게 짜니깐요...?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

하하... next의 존재를 모르고 있어서 이런 코드가 나왔던 것 같습니다.. 🥲
수정해보겠습니다!

조금 더 명확한 설명으로 변경

#160
@geongyu09
Copy link
Collaborator Author

추가적으로 코드를 수정해보았습니다..!

  • alias 를 추가하였습니다.
  • describe 설명을 조금 더 명확한 설명으로 변경하였습니다.

Comment on lines +20 to +23
cy.on("window:alert", (str) => {
expect(str).to.equal(expectedValue);
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 alert 창의 내용을 콘솔창에 출력되도록만 했는데 alert 창의 내용까지 검증하는 코드 굉장히 좋은 것 같아요.

Comment on lines +26 to +27
it("다음 버튼 클릭시 “필수 질문을 작성해주세요.” alert창이 뜬다. ", () => {
cy.get("@nextButton").should("exist").click();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 알기로는 beforeEach에서 다음 버튼을 찾지 못하면 cypress에서 에러를 출력하기 때문에 should로 추가 검증은 안해도 될 것 같네요. 다른 코드들도 변경하면 좋겠네요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 맞습니다!
처음에 제가 should를 작성한 이유는 제가 봤던 레퍼런스 코드 때문이었습니다.
제 기억상 렌더링이 안된 상태에서 클릭을 하려고 하면 문제가 발생할 수 있어 렌더링을 기다리고자 should를 넣어야 한다고 했던 것 같은데,
제 코드에서 안넣은 곳도 있는데도 성공하는 것을 보니 크게 문제되지는 않는 것 같습니다..!

둘 중에서 하나로 통일하는 것이 좋아 보이긴 합니다! 한번 수정해보도록 하겠습니다

Comment on lines +10 to +18
"checkLocalStorage",
(key: string, expectedValue: string) => {
cy.window().then((win) => {
const actualValue = win.localStorage.getItem(key);
expect(actualValue).to.equal(expectedValue);
});
}
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로컬 스토리지를 확인하는 코드 좋네요. 저도 merge후에 사용하겠습니다 !

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋습니다!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋은 추상화네요 👍

Copy link
Collaborator

@2yunseong 2yunseong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다~! 의문점에 대해 몇 가지 생각을 남겨봅니다. 참고만 하시고 다른 생각도 남겨주세요 :)

테스트를 작성하는데 어느정도까지 작성을 해야 할지에 대해서 조금 고민이 들었습니다. 예를 들어서 "개발자(디자이너/기획)를 누르면 왼쪽 네비게이션바에 질문이 생긴다" 와 같은 유저 시나리오가 있다면, 개발자, 디자이너, 기획자 전부 테스트를 해야 할지에 대해서 고민이 들었으며, "2순위로 동일한 분야를 선택할 수 없다" 를 테스트 한다면, 나올 수 있는 모든 케이스를 해보는게 맞을지 고민이 들었습니다.

저도 이 부분이 고민되었는데요, 너무 많은 케이스를 고민하기에는 비용이 많이 들고, 적은 케이스만 커버하기에는 테스트 커버리지가 낮아진다고 생각해요. 그래서 적절하게 비용과 커버리지를 고민해서 기본 사항은 한 두가지와 특이 케이스만 고려한다면 적절하게 비용과 커버리지를 챙길 수 있지 않을까 라는 생각이네요~ 무엇보다 정답은 없는 것 같습니다

클래스명, 혹은 useId() 의 id 등으로 가져오는 방식은 가독성도 떨어지고 유지보수적으로도 좋은 방법이 아닐 수 있겠다는 생각이 들었습니다. 다만, 구별되는 점을 찾기 어려운 경우(1순위, 2순위가 동일하게 분야 버튼이 생기는 경우. 아래 사진 참고) 어쩔 수 없이 cypress에서 찾아주는 선택자로 사용할 수 밖에 없어지는 것 같습니다. 해당 부분은 우선적으로 선언문을 통해서 get()한 부분이 어떤 부분인지를 알려주도록 하였습니다만 더 좋은 방법이 있을지 고민이 됩니다. ( testid 를 넣는 방식이 좋아 보이긴 합니다..!)

저는 e2e에서는 접근할 때, "사용자의 측면에서 테스트" 하는 것이라고 생각해요. 따라서 선택자의 코드도 실제 인간이 시각적으로 접근하듯이 짜면 좋겠다는 생각이 들었습니다. 예를 들어, 1순위의 label들이면,
"1순위" 라는 값이 있는 요소의 형제 요소들 중에 존재하는 라벨 중, APP을 text로 가지고 있는 요소

// 의사 코드
cy.get("h3").contains("1순위").sibling().contains("APP");

처럼 말이죠! 건규님의 생각도 궁금하네요~!

Comment on lines +10 to +18
"checkLocalStorage",
(key: string, expectedValue: string) => {
cy.window().then((win) => {
const actualValue = win.localStorage.getItem(key);
expect(actualValue).to.equal(expectedValue);
});
}
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋은 추상화네요 👍

@geongyu09
Copy link
Collaborator Author

  1. 사실 저 또한 동일하게 생각하고 있습니다. 가장 좋은 방법은 모든 테스트케이스를 만들어 보는 것이지만, 현실적으로 너무나도 큰 비용이 들어가게 되는 것 같습니다. 어쩌면 이에 집중해서 오히려 더 중요한 것들을 놓칠 수도 있겠다는 생각이 들었습니다. 적정한 선 내에서 현실과의 타협점을 잘 찾는 것이 중요할 것 같습니다!

  2. 윤성님께서 제시해주신 .sibling()을 이용한 코드도 너무나 좋아보이네요. 설명해주신 것 처럼 유저의 시선에서 접근하는 방법이 거희 정답으로 보입니다.
    어쩌면 아직 cypress 에 대해서 잘 알지 못했기도 하다보니 접근하는 방법에 대해서 약간은 어려움이 있었던 것 같습니다. 이번 pr을 통해서 앞으로의 방향성이 잡혀서 정말 시원한 느낌이 드네요! 리뷰 감사합니다

@geongyu09 geongyu09 merged commit 66e40d4 into main Aug 27, 2024
1 check passed
@2yunseong 2yunseong deleted the test/160-application-chapter branch September 9, 2024 06:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
test ✅ 테스트 작성
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FE] test: 신청폼 희망분야 페이지 e2e 테스트 작성
4 participants