소프트웨어 테스팅

강민성·2022년 1월 12일
0

소프트웨어 테스팅


코드 작성 --> 기대 결과가 나오는지 테스트 --> 성공/실패
실패하면 --> 다시 코드 작성

소프트웨어 테스팅이란

1) 제대로 된 소프트웨어를 개발했는지
2) 소프트웨어를 제대로 개발했는지(제대로 된 절차에 맞춰서)
확인하는 것

소프트웨어 테스팅을 하는 이유

결함 확인

사전 방지

시간 절약

테스트를 하지 않고 나중에 버그를 발견하는 것보다 시간을 절약할 수 있음

구조 개선

보다 효율적인 코드로 개선 가능

품질 개선

확장성

Manual Testing

단점

인력 소모가 크고 실수할 가능성이 높음
비용이 큼
테스트 속도가 느림
--> 이 모두를 개선할 수 있는 것이 시스템 테스트

시스템 테스트 전략 3가지

(복잡도 순)

End-to-End Tests

전체적인 플로우 확인
(ex. 브라우저 클릭 후 생기는 변화에 대한 검증, cypress)

Integration Tests(통합 테스트)

모듈 간의 호환성 검증, 만든 기능이 맞는 값을 리턴하는지 확인
(ex. 포스트맨 등을 통한 API 호출 시 올바르게 동작하는지 확인)

Unit Tests(단위 테스트)

독립적으로 진행되는 가장 작은 단위의 테스트
테스트할 수 있는 가장 작은 단위를 테스트하는 코드를 작성해서 테스트하는 것

장점

빠르게 문제를 파악할 수 있으며, 시간과 비용을 절감하고, 리팩토링 시 안정성을 확보하고, 코드에 대한 문서로 활용할 수 있음
예시)

파이썬 유닛 테스트

TestCase unittest 프레임 워크의 테스트 조직의 기본 단위

Fixture 테스트를 진행할때 필요한 테스트용 데이터 혹은 설정 등(테스트용 데이터베이스에 넣는 테스트용 데이터). 주로 테스트 가 실행되기 전이나 후에 생김. 테스트를 할 때는 로컬 데이터베이스를 사용하는 게 아니라 테스트 데이터베이스를 사용.

Assertion unittest에서 테스트 하는 부분이 제대로 됐는지를 확인하는 부분. 내가 기대한 결과가 나왔는지 확인하는 부분. Assertion이 실패하면 테스트도 실패.

방법

  1. 먼저 unittest 모듈을 import 한다.
  2. unittest.TestCase 클래스를 상속하는 테스트 클래스를 만든다.
  3. 테스트 클래스 안에 `test_₩ 로 시작하는 테스트 메서드를 생성한다.
  4. 테스드 메서드에서는 일반적으로 테스트하고자 하는 함수나 메서드를 호출하고 그 결과값을 self.assert*() 메서드를 사용하여 확인한다. 로직을 새로 짜지 말고 인풋과 아웃풋만 비교한다(assertEqual, assertTrue, assertFalse, assertRaises, assertRegex 등 다양한 assert 메서드들을 사용할 수 있다).
  5. 테스트 클래스가 완성되었으면, unittest.main()을 호출하여 테스트를 실행시킨다.

Test Fixture

테스트 시나리오에 따라 어떤 경우는 테스트 전에 테스트를 위한 사전 준비 작업을 할 필요가 있음
또한 테스트가 끝난 후 테스트를 하기 위해 만든 리소스들(ex. 데이터)을 정리(clean up)를 해야하는 경우도 있을 수 있음
unittest는 이렇한 사전 준비 작업을 위해 setUp() 이라는 메서드를, clean up 처리를 위해 tearDown() 이라는 메서드를 제공
이러한 기능들을 Test Fixture라 하며, Fixture는 각각의 테스트 메서드가 실행되기 전과 후에 매번 실행됨

장고 유닛 테스트

앱 생성 시에 항상 함께 생성되는 tests.py 을 통해 test를 진행
Django는 기본적으로 unittest를 사용하기 때문에 TestCase클래스와 테스트를 위한 Client() 객체를 제공함

테스트용 데이터를 만들 떄는 아이디(PK)를 지정하는 것이 좋음
예시)

class 뷰명Test(TestCase):
	def setUp(self):
        클래스명.objects.create(
        )

	def tearDown(self):
    	 클래스명.objects.all().delete()
 
def test_뷰명_post_success(self):
    client = Client()
    post할딕셔너리명 = {
    }
    
    response = client.post('엔드포인트', json.dumps(author), content_type='application/json')

    self.assertEqual(response.status_code, 200)
    self.assertEqual(response.json(), 
            {
                'message' : 'SUCCESS'
            }
        )
 def test_뷰명_post_에러명(self):
        client = Client()
        author = {
            'name'  : 'Brendan Eich',
            'email' : 'GuidovanRossum@gmail.com'
        }
        response = client.post('엔드포인트', json.dumps(author), content_type='application/json')

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json(),
            {
                'message' : 'DUPLICATE_NAME'
            }
        )

def test_뷰명_post_에러명2(self):
        client = Client()
        author = {
            'name'  : 'Brendan Eich',
            'email' : 'GuidovanRossum@gmail.com'
        }
        response = client.post('엔드포인트', json.dumps(author), content_type='application/json')

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json(),
            {
                'message' : 'DUPLICATE_NAME'
            }
        )
  

class AuthorBookTest(TestCase):
    def setUp(self):
        Book.objects.create(
            id    = 1,
            title = 'python'
        )

        Book.objects.create(
            id    = 2,
            title = 'javascript'
        )

        Author.objects.create(
            id    = 1,
            name  = 'Guido van Rossum',
            email = 'GuidovanRossum@gmail.com'
        )

        Author.objects.create(
            id    = 2,
            name  = 'Brendan Eich',
            email = 'BrendanEich@gmail.com'
        )

        AuthorBook.objects.create(
            book   = Book.objects.get(id=1),
            author = Author.objects.get(id=1)
        )

        AuthorBook.objects.create(
            book   = Book.objects.get(id=1),
            author = Author.objects.get(id=2)
        )

        AuthorBook.objects.create(
            book   = Book.objects.get(id=2),
            author = Author.objects.get(id=1)
        )

        AuthorBook.objects.create(
            book   = Book.objects.get(id=2),
            author = Author.objects.get(id=2)
        )

    def tearDown(self):
        Book.objects.all().delete()
        Author.objects.all().delete()
        AuthorBook.objects.all().delete()

    def test_authorbook_get_success(self):
        client   = Client()
        response = client.get('/book/author-book/1')
        self.assertEqual(response.json(),
            {
                "authors" : [
                    {'author': 1}, 
                    {'author': 2}
                ],
                         
            }
        )
        self.assertEqual(response.status_code, 200)

Mocking

유닛테스트를 하다 보면 테스트를 하기 위해 실제로 실행할 수 없는 코드들이 있음
(외부 API를 실제로 호출하게 되면 결제가 되고, 문자가 보내지는 등 테스트를 위한 추가적인 시간과 비용이 발생할 수 있는 코드)
(ex: SMS 문자, 결제 API, 플랫폼 소셜 로그인 등)
이러한 코드들을 테스트 하기 위해서 Mock(거짓된 값)을 이용한 테스트를 시행

API의 내부 로직을 테스트하는 것이 아니라, API에 요청을 보내고 가상의 response를 받았다고 가정하고 내 코드를 테스트

예시) 카카오 소셜로그인 뷰 테스트 코드

# 카카오로그인 test
# views.py
import requests

class ...
	def get(self, request):
		...
		response = requests.get('/kakao/user/me', token = {...}, )
		..
		...
		kakao_user_id = response.json()["id"].
	  ...
    ......

# test.py
from django.unittest import mock, patch

@patch("users.views.requests")
def test_google_signin_new_user_success(self, mocked_requests):
    client = Client()

    class MockedResponse:
        def json(self):
            return {
							"id":123456789,
					    "kakao_account": { 
					        "profile_needs_agreement": false,
					        "profile": {
					            "nickname": "홍길동",
					            "thumbnail_image_url": "http://yyy.kakao.com/.../img_110x110.jpg",
					            "profile_image_url": "http://yyy.kakao.com/dn/.../img_640x640.jpg",
					            "is_default_image":false
					        },
					        "email_needs_agreement":false, 
					        "is_email_valid": true,   
					        "is_email_verified": true,   
					        "email": "sample@sample.com",
					        "age_range_needs_agreement":false,
					        "age_range":"20~29",
					        "birthday_needs_agreement":false,
					        "birthday":"1130"
            }

		mocked_requests.get = MagicMock(return_value = MockedResponse())
    headers             = {"HTTP_Authorization": "가짜 access_token"}
    response            = client.get("/users/signin/kakao", **headers)

    self.assertEqual(response.status_code, 201)

@patch(내용) 내용을 만나면 멈추고 아래 코드를 실행하라는 코드

profile
Back-end Junior Developer

0개의 댓글