<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>vaping_ape.log</title>
        <link>https://velog.io/</link>
        <description>-</description>
        <lastBuildDate>Mon, 26 Dec 2022 14:39:23 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>vaping_ape.log</title>
            <url>https://velog.velcdn.com/images/vaping_ape/profile/f14c589d-9abd-4e57-a493-f09f78ba9947/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. vaping_ape.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/vaping_ape" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[SW test란 무엇일까]]></title>
            <link>https://velog.io/@vaping_ape/SW-test%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</link>
            <guid>https://velog.io/@vaping_ape/SW-test%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</guid>
            <pubDate>Mon, 26 Dec 2022 14:39:23 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p><strong>테스트</strong>란 용어는 누구에게나 익숙한 용어일 것입니다. 소프트웨어 개발자, 하드웨어 개발자, 기획자 등 어떠한 일을 하던 직책을 가지던 테스트를 이야기 하고 테스트의 중요성을 강조합니다. 이렇듯 우리는 누구나 테스트의 중요성에 대해 알고 있습니다. 그렇다면 이 중요한 테스트라는 친구에 대해 우리는 얼마나 알고 있을까요? 이번 포스트는 테스트 그중에서도 소프트웨어 테스트란 무엇인가에 관해 이야기 해볼까 합니다.</p>
<h2 id="테스트란-무엇일까">테스트란 무엇일까</h2>
<blockquote>
<p>프로그램 혹은 시스템이 사용자가 요구하는 기능이나 성능, 신뢰성 등을 만족하는지 확인하고 결함을 발견하는 활동</p>
</blockquote>
<p>보통 테스트가 무엇인가에 대한 답변을 이야기하면 위의 문장에서 크게 벗어나지 않는 답이 나올것 입니다. 위와 같은 테스트의 정의는 소프트웨어 개발 이후에 요구 기능확인과 결함 발견에만 중점을 둔 소극적인 활동으로 테스트를 이야기하고 있습니다. 하지만 저의 생각은 조금 다릅니다. QA 엔지니어로써 회사 업무를 진행하면서 여러 서적과 글을 읽었고 스스로도 테스트라는 업무에 대해서 생각한 결과 제 스스로 테스트는 <strong>개발 기획 단계부터 능동적으로 참여</strong>하여 개발과 함께 테스트를 기획, 실행, <strong>기능확인과 결함 발견뿐만 아니라 결함을 예방하고 이미 일어난 결함을 관리하며 이때 쌓이는 데이터를 이용해 제품 자체의 리스크 정보를 식별해 내는 과정이 테스트</strong>라는 정의를 내리게 되었습니다.</p>
<h2 id="v-model">v-model</h2>
<p>개발을 하다보면 v-model에 관한 이야기를 들어보셨을 것입니다. v-model은 소프트웨어 개발 프로세스로 개발의 생명주기와 이에 대응하는 테스트 들을 보여주는 model 입니다.</p>
<p><img src="https://velog.velcdn.com/images/vaping_ape/post/d247c00e-fadb-4147-8ae9-c05a44366c17/image.png" alt="">
위의 그림이 v-model인데 테스트가 무엇인지에 대해 이야기를 나누려 하는 저희는 v모양의 오른쪽 즉 validation phaese를 봐야합니다. v-model은 왼쪽 -&gt; 오른쪽으로 진행되는 모델이기 때문에 이 모델에서 테스트의 진행과정은 <strong>Unit test &gt; Integration test &gt; System test &gt; Acceptance test</strong>  순으로 진행됩니다. </p>
<ul>
<li><p>Unit test
Unit test 즉 단위 테스트는 첫번째 테스트 단계인 만큼 가장 작은 범위부터 시작합니다. 일반적으로 개발자 분들이 진행을 하게 되며 메서드 혹은 클래스 단위에서의 기능을 테스트하는 단계입니다. 단위 테스트는 소프트웨어 내부 코드에 관련한 지식을 반드시 알고 있어야 하는 화이트박스 테스트로 진행되어야 합니다. 화이트 박스 테스트에 관한 내용은 나중에 다뤄보도록 하겠습니다. 단위 테스트는 TDD(Test-Driven-Development)라는 방법으로 개발을 할때에 더욱 큰 효과를 발휘하게 된됩니다. TDD에 관한 내용은 나중에 다루도록 하겠습니다.</p>
</li>
<li><p>Integration test
Integration test 즉 통합 테스트는 단위 테스트보다 더 큰 동작을 테스트하기 위해 여러 모듈들을 모아 이들이 의도대로 협력하는지 확인하는 테스트입니다. 이때에 클래스, 메서드 뿐 아니라 사용되는 외부 라이브러리와 DB등을 포함한 동작까지 테스트 하게 됩니다. 보통 개발환경에서 테스트 하게 되고 단위 테스트에서는 발견하지 못한 이슈들이 많이 발견되게 됩니다 </p>
</li>
<li><p>System test
이 단계 부터는 개발자가 아닌 테스트를 전문적으로 맡아 하시는 분들의 영역입니다. System test 즉 시스템 테스트는 단위 테스트와 통합 테스트에서 테스트하지 않은 비기능적 테스트에 조금더 중점을 둔 테스트 단계입니다. 비기능적 테스트는 소프트웨어의 성능, 사용성, 효율성, 신뢰성, 효율성, 유지보수성, 이식성 등을 포함하며 소프트웨어가 동작할 실 환경과 최대한 유사한 환경에서 진행되어야 합니다.</p>
</li>
<li><p>Acceptance test
Acceptance test 즉 인수 테스트는 테스트의 마지막 단계로써 앞의 테스트 단계를 거쳐 검증되어 있는 소프트웨어를 사용자의 입장에서 테스트 하는 과정입니다. 일반적으로 테스트 라고 할때 떠오르는 활동이 이 인수 테스트 단계에서의 활동들입니다. 실제 사용자의 환경과 입장에서 테스트를 진행하게 되며 해당 소프트웨어를 인수받을 사용자가 직접 테스트 하게 됩니다.</p>
</li>
</ul>
<h2 id="테스트-유형">테스트 유형</h2>
<p>앞에서 v-model을 통해 테스트의 단계를 살펴보았습니다. 이번에는 테스트를 유형 별로 구분해 살펴보도록 하겠습니다.</p>
<ul>
<li><p>기능 테스팅
소프트웨어의 기능들을 테스트 하는 과정을 이야기 합니다. 소프트웨어의 기능들이 정상적으로 작동하는지에 대해 테스트하게 됩니다</p>
</li>
<li><p>비기능 테스팅
소프트웨어의 비기능적 요소들을 테스트 하는 과정을 이야기 합니다. 앞서 설명한 System test와 유사한 테스팅 과정입니다.</p>
</li>
<li><p>구조적 테스팅
소프트웨어의 구조나 아키텍쳐에 관련된 테스팅 과정입니다.</p>
</li>
<li><p>확인/리그레션 테스팅
소프트웨어의 기능 추가 혹은 이슈 해결 등의 이유로 소프트웨어가 변경되었을때에 해당 변경사항이 정상적으로 적용되었는지 테스트 하는 과정입니다. 이때에 변경 과정에서 의도치 않게 유입된 결합이 있는지 확인하는 테스트를 같이 진행하게 됩니다. 반복적 성향이 강한 테스트이기 때문에 테스트 자동화를 고려해볼수 있는 부분입니다.</p>
</li>
</ul>
<h2 id="마무리">마무리</h2>
<p>이번 포스팅에서는 테스트가 무엇인가 부터 시작해서 테스트를 단계별로, 유형별로 나누어 보았습니다. 위와 같이 테스트의 유형과 종류는 매우 많기 때문에 완성도가 높은 테스트를 진행하기 위해서는 테스트 유형과 종류를 정확하게 알고 상황과 목적에 맞는 테스트를 적용시켜 완성된 계획을 세우는 것이 중요합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[playwright를 이용한 e2e test]]></title>
            <link>https://velog.io/@vaping_ape/playwright%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-e2e-test</link>
            <guid>https://velog.io/@vaping_ape/playwright%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-e2e-test</guid>
            <pubDate>Tue, 20 Dec 2022 08:20:58 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/vaping_ape/post/ad01f304-951a-408e-8951-e96595e9a0c7/image.png" alt=""></p>
<h2 id="들어가며">들어가며</h2>
<p>많은 전/현직 개발자 분들이라면 운영중인 서비스에 예상하지 못한 이슈/버그가 발견되는 일을 빈번하게 겪어봤을 것이라고 생각합니다. 아무리 개발단에서 철저하게 단위 테스트, 유닛 테스트를 구성하고 실행하여 개발을 한다고 하여도 분명히 요상한 장소, 기능에서 이슈가 터져나옵니다. 이 포스트에서는 이러한 이슈들로 인한 개발에 불편한 경험을 겪으시는 분들을 위해 <strong>playwright</strong> 라는 프레임워크를 통해 E2E 테스트 자동화를 구성하는 방법에 대해 알아보고자 합니다</p>
<h2 id="e2e-테스트">E2E 테스트</h2>
<p>그렇다면 먼저 E2E 테스트가 무엇인지 부터 생각해볼 필요가 있습니다. E2E test란 뭘까요? </p>
<blockquote>
<ul>
<li>E2E testing is a methodology used for ensuring that applications behave as expected and that the flow of data is maintained for all kinds of user tasks and processes</li>
<li>E2E testing is a methodology that assesses the working order of a complex product in a start-to-finish process</li>
<li>In theory, end-to-end testing (E2E testing) is the process of testing a piece of software from start to finish as it will be used by the actual users</li>
</ul>
</blockquote>
<p>위의 인용문들이 모두 E2E 테스트의 정의입니다. 각각 표현이 약간씩 다르지만 중복되는 말을 모아보면</p>
<blockquote>
<p>실제 사용자의 입장에서 소프트웨어가 정상적으로 작동되는지 
각각의 기능들을 처음부터 끝까지(end to end) 테스트 하는 것</p>
</blockquote>
<p>정도로 E2E 테스트를 간단하게 정리 할 수 있을것 같습니다. 조금 풀어서 설명을 더하자면 개발자의 입장에서 테스트를 하는 것이 아닌 실제 소프트웨어를 사용하는 유저의 입장이 되어서 유저가 소프트웨어를 실행하는 시나리오를 따라 소프트웨어를 테스트 하면서 하나의 기능을 전체적으로 테스트 하는것이 E2E 테스트라고 할수 있을것 같습니다.</p>
<h2 id="playwright">playwright</h2>
<p>앞에서 E2E 테스트가 무엇인지에 대해 알아보았으니 이제 이 테스트를 자동화 시키기 위한 툴을 알아볼까 합니다. 이번 포스팅에서는 <strong>playwright</strong>라는 프레임워크를 사용해 테스트 자동화를 구현해 보겠습니다. 먼저 playwright의 <a href="https://playwright.dev/">공식문서</a>를 보게되면 playwright에 관한 설명과 사용할수 있는 API들이 정리되어있습니다. playwright 프레임워크는 파이썬, 자바스크립트, 타입스크립트등 여러가지 언어로 사용할 수 있는데 저는 자바스크립트를 이용해서 코드를 작성하도록 하겠습니다.</p>
<blockquote>
<p>const { test, expect } = require(&#39;@playwright/test&#39;);</p>
</blockquote>
<p>playwright로 테스트를 작성하실 때에는 위의 코드를 항상 추가해서 코드를 작성하여 주세요!</p>
<h2 id="로그인-테스트-작성">로그인 테스트 작성</h2>
<p>웹 서비스에서 이슈 혹은 버그가 발생하였을때 굉장히 큰 비지니스적 임팩트를 가져올 수 있는 로그인 쪽 테스트를 작성해 보도록 하겠습니다. </p>
<pre><code class="language-javascript"> //logintest.spec.js
 test(`id/pw login`, async ({page}) =&gt; { 
      await page.goto(&quot;테스트 사이트 로그인 페이지 url&quot;); //로그인 페이지로 이동
      await page.locator(&#39;div.join-btn.cursor&#39;).click(); //로그인 버튼 클릭
      await page.getByPlaceholder(&quot;아이디&quot;).fill(&quot;user_id&quot;);
      await page.getByPlaceholder(&quot;비밀번호&quot;).fill(&quot;user_pw&quot;); //placeholder를 이용해 입력칸에 정보 삽입
      await page.locator(&#39;button.login-btn&#39;).click(); //로그인 버튼 클릭(로그인 api 호출)
      await page.waitForResponse(&quot;api_response_url&quot;); //로그인 api 응답 대기

      const context = page.context();
      const state = await context.storageState();
      const token_value = state.origins[0].localStorage.some((elem) =&gt; elem.name === &#39;token&#39;); 
      await expect(token_value).toBe(true);
      console.log(&quot;login success&quot;);
      //console.log(token_value);
      page.close();
    })</code></pre>
<p>로그인 테스트 코드를 작성하였습니다. 코드를 크게 두덩이로 나누어 위쪽 덩이는 실질적인 유저의 동작 즉 로그인을 하는 동작을 page라는 클래스의 여러 매소드들을 이용해 구현하였고 아래쪽 덩이에서는 사용자가 로그인을 시도하였을때 발급되는 토큰이 정상적으로 발급되는지와 발급된 토큰을 이용해 로그인 테스트의 성공 여부를 판단하는 코드를 작성하였습니다. </p>
<p>위와 같이 코드를 작성한 뒤에는 터미널에 아래와 같은 명령어를 입력하면 테스트가 실행되게 됩니다.</p>
<blockquote>
<p>npx playwright test logintest.spec.js</p>
</blockquote>
<h2 id="그외의-테스트-코드">그외의 테스트 코드</h2>
<p>아래에 첨부하는 테스트 코드들은 실제 한 커뮤니티 회사에서 제가 작성하였던 테스트 코드들입니다. 실무에서 작성된 코드이기 때문에 계정정보, url 정보는 모두 제외하였습니다.</p>
<h4 id="hot게시글-리스트-체크">hot게시글 리스트 체크</h4>
<pre><code class="language-javascript">//list_check.spec.js
test.describe(&quot;리스트 체크&quot;, () =&gt; {
  test.beforeEach(async ({ page }) =&gt; {
    await page.goto(&quot;url&quot;);
    await page.locator(&quot;div.join-btn.cursor&quot;).click();
    await page.getByPlaceholder(&quot;아이디&quot;).fill(&quot;user_id&quot;);
    await page.getByPlaceholder(&quot;비밀번호&quot;).fill(&quot;user_pw&quot;);
    await page.locator(&quot;button.login-btn&quot;).click();
    await page.waitForResponse(&quot;api_response_url&quot;);
  });
  test(&quot;메인, 게시판 페이지 HOT글 리스트 일치 테스트&quot;, async ({ page }) =&gt; {
    await page.waitForSelector(&quot;a &gt; div.txt&quot;);
    const main_element = await page.$$(&quot;a &gt; div.txt&quot;);
    let mainHotArr = [];
    for (const arrNum in main_element) {
      const elem = await main_element[arrNum].innerHTML();
      mainHotArr.push(elem);
    }
    await page.goto(&quot;page_url&quot;);
    const selector = &quot;array_selector&quot;;
    await page.waitForSelector(selector);
    const postElement = await page.$$(selector);
    let postHotArr = [];
    for (const arrNum in postElement) {
      const elem = await postElement[arrNum].innerHTML();
      postHotArr.push(elem);
    }
    await expect(mainHotArr.toString() === postHotArr.toString()).toBe(true);
  });</code></pre>
<p>한 커뮤니티 사이트의 hot게시글 리스트가 각각의 페이지에서 동일하게 보이는지에 대한 테스트 코드입니다. 위에서 보여드린 로그인 테스트 코드에서는 로그인 테스트 하나만을 테스트하기 때문에 test 함수만 이용했지만 이번 hot게시글 리스트 체크 코드에서는 추후 다른 리스트들의 테스트 코드를 추가하기 위해 test.describe 메소드로 리스트 체크라는 큰 테스트 묶음을 만들었고 각 테스트 진행 전에 이루어져야할 로그인 작업을 test.beforeEach 메소드를 이용해 작성하였습니다. </p>
<p>이 코드를 작성하면서 동기와 비동기 방식의 개념을 다시 한번 정리하고 학습하는 계기가 되었습니다. async와 await가 다음 코드를 실행하는 시점에 대해 고민한 결과 로딩이 길어지는 부분에 waitfor<del>~</del> 메소드를 사용하였고 배열을 받아오는 코드를 작성하는 과정에서 foreach 문은 비동기함수(async)를 기다리지 않는다 라는 것을 깨닫고 for in 문으로 배열위치를 받아와 postHotArr에 해당 배열 위치에 있는 값을 push 해주는 코드를 작성하였습니다.</p>
<h4 id="공유-스크랩-버튼">공유, 스크랩 버튼</h4>
<pre><code class="language-javascript">//share_scrap_test.spec.js
test.describe(&quot;share, scrap test&quot;, () =&gt; {
    test.beforeEach(async ({ page }) =&gt; { 
        await page.goto(&quot;loginpage_url&quot;);
        await page.locator(&quot;div.join-btn.cursor&quot;).click();
        await page.getByPlaceholder(&quot;아이디&quot;).fill(&quot;user_id&quot;);
        await page.getByPlaceholder(&quot;비밀번호&quot;).fill(&quot;user_pw&quot;);
        await page.locator(&quot;button.login-btn&quot;).click();
        await page.waitForResponse(&quot;api_response_url&quot;);
    })
    test(&quot;공유&quot;, async ({ page }) =&gt; { 
        await page.goto(&quot;page_url&quot;);
        await page.locator(&quot;button selector&quot;).click();
        await page.locator(&#39;double check button selector&#39;).click();
        await page.keyboard.press(&quot;Enter&quot;);

        const context = page.context();
        await context.grantPermissions([&#39;clipboard-read&#39;]); //권한 요청
        let clip = await page.evaluate(&quot;navigator.clipboard.readText()&quot;);
        await expect(page.url() === clip).toBe(true);
        await page.close();
    })
    test(&quot;스크랩&quot;, async ({ page }) =&gt; {
        await page.goto(&quot;page_url&quot;);
        await page.waitForSelector(&quot;title selector&quot;);
        const title = await page.locator(&quot;title seletor&quot;).innerHTML();
        await page.locator(&quot;button selector&quot;).click();
        await page.locator(&quot;double check button selector&quot;).click();
        await page.goto(&quot;scrap board page url&quot;);
        await page.waitForSelector(&quot;title selector&quot;);
        const myTitle = await page.locator(&quot;title selector&quot;).innerHTML();

        await expect(myTitle === title).toBe(true);

        await page.locator(&quot;button selector&quot;).click();
        await page.locator(&quot;double check button selector&quot;&quot;).click();
        await page.close();
    })
})</code></pre>
<p>같은 커뮤니티 사이트에서 게시물을 공유, 스크랩할때의 테스트 코드입니다. 이번 역시 test.describe를 통해 테스트를 묶고 test.beforeEach 메소드를 통해 각 테스트 전 로그인을 했습니다. </p>
<p>이번 코드에서 중요하게 볼 점은 권한 관련된 내용입니다. 게시물 공유 버튼을 클릭하면 클립보드에 해당 게시물의 url가 복사되게 되는데 이 복사된 url과 게시물의 url을 비교하기 위해 브라우저에 권한을 요청하는 코드를 작성하였습니다. 이때 문제가 발생하는데 위의 코드로는 playwright에서 기본적으로 사용하는 테스트 브라우저 3개중 chromium에서만 권한을 받을수 있습니다. webkit과 firefox에서는 권한을 받아오지 못합니다. 그렇기 때문에 playwright로 브라우저의 권한을 받아와 특정 테스트를 실행하실 때에는 아래와 같이 꼭 프로젝트를 chromium으로 설정하여 실행하시기 바랍니다.</p>
<blockquote>
<p>npx playwright test --project=chromium share_scrap_test.spec.js</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>