pytest를 썼을 때의 장점을 http://halfcooked.com/presentations/pyconau2013/why_I_use_pytest.html 의 링크에서 번역해 간단하게나마 정리를 해보았다.


Unit Testing이란?

  • Unit Testing이란 어플리케이션이나 모듈의 각각 유닛들이 작성한대로 작성하는지 확인하는 반복적인 작업이다.

  • Unit Testing의 핵심은 각각 테스트들이 서로 분리되어 있으며, 실행은 자동화되고, 테스트들은 당신이 만든 어플리케이션의 일부와 같은 부분을 실행한다는 것이다.


Unit Test를 왜 해야 할까?

다른 이유보다 앞서서 하는 이유는 “내 코드가 잘 작동하는지 확인하기 위해서”라는 것은 모두다 알고 계실겁니다.

Unit Test는 우리가 “어떻게” 코드를 작성할지 생각하는데 도움을 주게되며 나아가 테스트를 추가함을 통해서 당신의 코드를 줄일 수 있게되고 이러한 점들은 당신이 이해하고 테스트하는데 다시 또 도움을 주게 됩니다.

많은 작업을 하는 코드를 테스트 하는것은 어려우며 또한 디버깅하기도 어렵다. 이 문제의 해결은 코드가 많은 작업을 하지 않도록 하게 하는 것이며 그렇기에 함수 하나는 하나의 기능만 하도록 하고 이를 테스트 하게 하는것이다.

“그리고”라는 말을 표현해서 코드를 설명해야 한다면 그 코드는 줄일 수 있어야 한다.(하나의 함수는 하나의 기능만을 구현해야 하기 때문에)

Unit Test의 다른 장점로는 문제를 찾기 쉽다는 점, 변화를 가능하게 한다는 점, 디자인을 개선시키고 통합을 단순화 할 수 있다는 점들이 있다.


Unit Test 라이브러리들

테스트 라이브러이에는 대표적으로 unittest(파이썬 기본 내장 모듈)와 pytest가 있는데 저 본문에는 pytest를 선호한다고 나와있다.

그 이유는 코드를 비교적 더 간단하게 만들 수 있고 필요할 떄 더 많은 기능들을 강력하게 제공해주기 때문이라고 한다.

아래와 같은 코드를

from parse_conn import parse_connection
import unittest

class InvalidInputs(unittest.TestCase):
    def testNoAt(self):
        """parse_connection should raise an exception for an invalid connection string"""
        self.assertRaises(ValueError, parse_connection, 'invalid uri')

if __name__ == "__main__":
    unittest.main()

다음과 같이 바꿀 수 있다고 한다.

from parse_conn import parse_connection
import py.test

def test_not_at():
    py.test.raises(ValueError, parse_connection, 'invalid uri')

한눈에 봐도 비교적 코드가 많이 간결해졌음을 알 수 있다.


그러면 Unit Testing을 어떻게 시작해야할까?

테스팅을 할 수 있는 가장 쉬운 방법은 버그를 수정하는 방법이다.

첫번째로, 버그를 발견하고 그다음에 버그를 고칠 수 있는 최소한의 코드를 작성하고 마지막으로 테스트를 다 통과 할 때까지 코드를 수정하는 것이다.

앞서 말한것처럼 해서 테스팅을 시작 할 수 있고 이제 새로 작동해야하는 기능을 추가 할 때 최소한의 테스트를 만들고 그 테스트를 통과하는 코드들을 완성하는 방향으로 시작하면 된다.


참고자료

업데이트(2018.12.26): 2018년 3월에 작성한 자료로 현재의 Android Studio 버전과 다를 수도 있습니다.

Android Studio에 JUnit을 붙이고 간단한 테스트를 진행해 보기로 한다.


환경 및 선수조건

  • Android Studio


app/build.gradle에 코드 추가

  • JUnit과 Espresso 그리고 그 둘을 위한 annotations과 test:runner를 gradle에 아래처럼 추가합니다.

build.gradle

...
dependencies {
    ...
    testCompile 'junit:junit:4.12'
    androidTestCompile 'com.android.support:support-annotations:25.1.0'
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
    androidTestCompile 'com.android.support.test:runner:0.5'
    ...
}
...


java 테스트 폴더 생성하기

  • JUnit으로 테스트를 하려면 src폴더 안에 test 폴더를 만들고 그 안에 또 java라는 폴더를 생성합니다.
  • 즉, src/test/java라는 폴더 생성
  • SampleTest파일은 아래에 설명하겠지만 추후에 폴더를 생성하고 추가해주시면 됩니다.



SampleTest 작성하기

아래와 같이 테스트를 하기 위한 간단한 테스트샘플 클래스를 생성해보도록 합시다.

Annotation(@RunWith와 @Test) 주의해서 작성해주세요!

SampleTest.java

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import static org.junit.Assert.assertTrue;

@RunWith(JUnit4.class)
public class SampleTest {

   @Test
   public void TestSample() {
       assertTrue(true);
   }

}
  • Annotation에서 @RunWith(JUnit4.class)를 명시해주셔야만 합니다!
  • 아주 간단한 테스트로 assertTrue()함수에서 내부의 결과가 true인지를 확인하는 코드입니다. JUnit을 어떻게 사용하는지에 대한 간략한 설명이기에 테스트 코드는 간략하게 작성하였습니다:)


Android Studio에서 JUnit 테스트 돌려보기

이제 작성한 코드를 돌려보도록 합시다. src/java폴더에서 우클릭을 하시면 다음과 같이 Run 'Tests in 'java''가 뜨는데 눌러주도록 합시다,


테스트 결과

위에 Test를 돌리면 아래처럼 테스트가 돌아가는것을 Android Studio에서 확인이 가능합니다.


Circle CI에서 테스트 하도록 circle.yml 수정하기

Circle CI에서도 테스트를 진행하도록 하기 위해서 다음과 같이 circle.yml에 추가를 하도록 합니다. 자동적으로 ./gradlew test를 진행하긴 하지만… 다음에 포스팅 할 Espresso와 함께 테스팅 하려면 수동으로 설정을 해줘야 하니 미리 해주었습니다:)

circle.yml

...
dependencies:
  override:
     - chmod +x gradlew

test:
  override:
    - ./gradlew test
...


참고자료

Code Coverage를 측정하는 도구인 codecov를 사용하는 간단한 방법에 대해서 포스팅한다.


사전준비

기본적으로 Github에 계정이 있어야 하며 다음으로 codecov에 들어가서 Sign Up With Github를 클릭해서 로그인을 하고 아래의 사진처럼 Github에서 Repo를 등록합니다.

저 같은 경우에는 Github에 있는 for_run_code의 Repo를 등록하였습니다.

Github Add Repo


설치

패키지 설치

  • python 패키지 모듈이기 떄문에 pip를 통해서 설치가 가능합니다.
pip install codecov pytest-cov

requirements.txt 생성

  • 이제 requirements.txt에 추가를 해줍니다.(Circle CI에서 빌드를 위해서는 필요한거 아시죠? :)
  • virtualenv를 사용하여서 프로젝트별로 pip모듈을 따로 관리하는게 맞지만 codecov를 사용하는 방법만 볼 예정이기 떄문에 우선 모든 pip모듈을 requirements.txt에 기록합니다.
pip freeze > requirements.txt


circle.yml수정

이제 circle.yml을 수정해봅시다. 다음 아래와 같은 설정 값들이 들어가야 합니다.

  • coverage를 측정할 폴더를 --cov값으로 경로를 넣어주시고 그 다음에 테스트를 하기위한 python 파일을 입력해주시면 됩니다.
  • 개인 Repo의 경우에는 token이 필요합니다 토큰은 아래 스크린샷에서 보이는바와 같이 가져오실 수 있습니다.

circle.yml

test:
    override:
        # pytest --cov=./측정할_폴더 테스트파일
        - pytest --cov=./ test_sample.py

    post:
        # 개인 Repo라면 token이 필요합니다!
        # 개인이 아니라면 codecov만 있으면 됩니다.
        - codecov --token=<token>

dependencies:
    pre:
       -  pip install -r requirements.txt

codecov repo setting


repo token


Github에 Push하고 Report를 확인

이제 위의 모든 과정이 완료되었다면 한번 Circle CI에서 어떻게 나오는지 그리고 codecov에서는 어떻게 보고서가 나오는지 스크린샷으로 보도록 하겠습니다.

아래는 우선 Circle CI에서 coverage가 어느정도인지를 확인하고 codecov에 보고서를 보내는 사진입니다.

coverage report

codecov send report


codecov 홈페이지에 가면 아래와 같은 보고서를 볼 수 있습니다.

codecov report


마지막으로 이제 Github에 가면 아래처럼 Integration의 결과들도 commit과 함께 보이는 것을 확인 할 수 있습니다 :)

github result


참고자료

IDE같은 환경에서 코딩을 하면 코드 자동 정렬이 있지만 없는 경우도 있으며 이 때 사용 할 수 있는 python package module이 있다.


설치

  • python 패키지 모듈이기 때문에 pip를 통해서 설치가 가능하다
$ pip install autopep8


사용법

컨벤션에 맞는지 확인

  • 아주 간단하다. 다음 아래의 명령어를 쳐주면 된다.
$ autopep8 [filename.py]


다음과 같은 아주 간단한 코드가 있다고 했을 때 위의 명령어를 실행하면 사진처럼 나온다.

코드가 변화하는 과정을 보기 위해 일부러 엉망으로 작성하였다.

autopep_practice.py

def add(a,b):
    c =    b


    d =    a


    return(c + d)

위의 명령어를 실행하면 아래와 같이 수정해야하는 방향의 코드가 나옵니다.

Example


컨벤션에 맞게 수정

  • 여기서 더 나아가서 코드를 아예 컨벤션에 맞게 수정을 하고 싶다면 아래처럼 -i 옵션을 주면된다.
$ autopep8 -i [filename.py]
  • 예시

After

git을 사용 할 때 지금보다 더 아무것도 모를때는 .gitignore의 중요성도 모르고 막 커밋하고 그랬는데 IDE나 툴들을 이용하다 보면 큰 프로젝트들은 개인 환경에 따라서 설정 파일들이 바뀌기도하고 push하고 pull하는데 conflict가 나서 .gitignore을 수정했는데 계속 트래킹하는 파일이 존재해서 어떻게 하면 수정 할 수 있는지를 포스팅한다.


문제 상황

바로 이전 포스트에서도 포스팅을 하였지만 안드로이드로 프로젝트를 하나 진행하고 있었고 툴은 Android Studio를 통해서 개발을 하고 있었다. 커밋을하고 push를 할때마다 자꾸 로컬의 환경설정 파일들과 gradle 캐시 등등 여러 파일도 같이 push가 되어서 이제야 .gitignore에 파일을 추가했는데 여전히 트래킹 되는 파일이 존재해서 해결책이 있기에 포스팅을 해본다.

현재 내 로컬에서 문제는 해결된 상황이라서 스크린샷은 없고… 설명을 하자면 .gitignore에 .idea/가 추가되어 있는데도 계속 해서 .idea/workspace.xml과 같은 파일이 git에서 변화를 감지하고 commit 해야 할 목록에 추가를 한다….

이게 구글링을 해보니까 나중에 .gitignore에 추가를 해도 git이 이미 파일을 트래킹하고 있어서 이러한 트래킹 기록을 한번 싹 물갈이 해줘야한다고 한다. 더 자세히 들어가면 cach를 싹 밀어줘야 한다.

  • .gitignore에 파일이 추가 되었는데도 git이 계속 파일의 변화를 감지할 때


주의 할 점!

NOTE: 작업하던 내용들이 있으면 반드시 commit을 진행하고 아래 작업을 해야합니다!


문제 해결

  • 아래 명령어들을 shell에서 진행해주면 됩니다.
  • 캐시를 지우고 새로 add하는 과정으로 볼 수 있습니다.
...
git rm -r --cached .
git add .
git commit -m "Fix untracked files"
...


그래도 트래킹이 된다면…

  • 가끔 그래도 트래킹이 될 때가 있는데…(특정 브랜치에서 하고 다른곳으로 넘어가면 적용이 안될 때도 본거 같습니다.) .gitignore를 추가하고 다시 remote repository를 clone받으면 문제가 사라진다.


참고자료