[ PROJECT ] 당근마켓 클론코딩 - #06 테스트코드 작성하기

Hailee·2021년 1월 7일
2

[ PROJECT ]

목록 보기
16/16
post-thumbnail

앞서 작성한 테스트코드 작성시 발생하는 에러 해결 글에 이어서,
드디어 테스트코드의 에러를 하나하나 풀어나가는 시간이 왔다.
두둥


그래서, 가장 큰 문제가 무엇인고 하니 👉🏻 '로그인 데코레이터'가 있는 메서드의 테스트!

이렇게 하면 어떻게든 header가 넘어가고 있을 것이라고 생각했다.
하지만 콘솔창을 확인해보니 뜨는 에러는..

내가 분석한 상황은 대충 이런 식이다.

  1. 아무리 '로그인 데코레이터'가 정상적으로 작동하지 않는다고 해도, 만약 header에 담긴 정보가 제대로 넘어가고 있다면
    request.user를 했을 시 가져온 유저 정보가 틀렸거나, id가 잘못되었다거나.. 그런 에러가 떠야 정상

  2. 하지만 나는 'NoneType' object has no attribute 'id' 라는 에러가 발생하는 상황.

  3. 애초에 header의 정보를 넘겨받지 못하거나 data 외의 다른 정보들을 넘겨받지 못해서
    None으로부터 무언가 가져오려고 하니 AttributeError가 발생하는 것일까?

내가 header에 토큰 정보를 담는 방식이 틀렸다고 가정하고, 다시 차근차근 테스트코드를 작성해보기로했다.


👆🏻 우선, views.py의 로직 내에 requestrequest.header,request.body에 담긴 데이터를 print 해 본 결과.
header가 존재하지 않는 것을 볼 수 있다! 도대체 왜! 어떻게 전해줄 수 있는거니..!!!!


우선 차근차근 다시 생각해보기

내가 참고한 글 👉🏻 Unit test - 로그인 데코레이터

  • data, **header를 동시에 보내는 것이 문제일까? 하지만 남들도 다 json.dump(data), **headers 이렇게 보내는걸..!!🤨

😱 😱 😱

차이점을 발견했다.
request - headers라 명명한다는 것, 그리고 테스트 코드 내에서 토큰으로 사용될 대상의 key 값앞에 HTTP_라 명시해 두는 것!


설마 하는 마음으로 header 👉🏻 headers , Authorization 👉🏻 HTTP_Authorization으로 수정했다.

드디어 headers의 정보를 전달하는 것을 확인할 수 있다!


진짜 문제 분석하기

테스트 코드의 작동 순서가 너무 궁금해져서, 어떤 순서로 작동하는건지 setUp, tearDown, test 메서드들에 print 코드를 입력해서 확인해보았다.
그 결과, 매 test가 시작 될 때마다 setUp, test, tearDown 순으로 작동하는 것을 확인할 수 있었다.

무튼, 본격적으로 무엇이 문제인지 분석해보쟈

  1. JSONDecodeError
self.assertEqual(response.json(), {'message' : 'SUCCESS'})
-
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

무언가 decodejson 데이터 대상이 없는 경우에 발생하는 에러인듯 하다.

구글링해보니 보통 토큰 발행하거나.. json 파일을 사용하는 로직에서 발생하는 에러인 듯 한데
나는 지금 테스트 메서드의 가장 마지막 부분의 로직에서 에러가 발생해서 애초에 views.py를 호출하지 못하고 있는 상황이다.

정말 이상하지.. responsedecode할 수 없다는 건데, 해당 메서드와 비슷한 다른 메서드들은 다 문제가 없는데
이 메서드에서만 JSONDecode에러가 발생한다.
👉🏻 혹시 DELETE메서드가 문제가 있는걸까? (하지만 다른사람들은 멀쩡하게 적어두었던데..)

설마하는 마음에 response의 모습이 궁금해서 print 해보니 다음과 같이 나왔다.
👉🏻 <JsonResponse status_code=201, "application/json">

~~우선 해당 부분을 주석 처리하고 넘어가기로 했다.. ~~

  1. DoesNotExist
raise self.model.DoesNotExist(
user.models.Address.DoesNotExist: Address matching query does not exist.

단순하게 테스트를 하고자 Address 테이블에 하나의 데이터만 생성해뒀던 것을 잊고있었다.

'내 동네 추가하기' 기능의 테스트 코드에서 발생한 에러인데, 우선 주소등록이 어느 경우에 발생하는지를 생각해보자.
👉🏻 새로운 회원이 회원가입하면서 주소등록을 할 때
👉🏻 이후에 '내 동네'화면에서 새로운 주소를 추가할 때

회원당 최대 2개의 동네를 등록할 수 있으며, 해당 기능을 테스트하기 위해서는 최소 두개 이상의 Address 데이터가 필요하다.
Address 테이블의 또 다른 데이터를 통해 FullAddress 테이블에 데이터를 추가할 수 있기 때문!

👉🏻 한마디로 테스트 할 데이터가 충분하지 않아서, 내가 찾고자 하는 지역코드를 가진 데이터가 없었던 것!

  1. path parameter로 유닛 테스트하기 | ValueError
for key, value in query:
ValueError: not enough values to unpack (expected 2, got 1)
    def test_getnearaddressview_get_fulladdress(self):
        headers = {'HTTP_Authorization': jwt.encode({'id':User.objects.get(phone_number='01000000000').id}, SECRET_KEY, ALGORITHM)}
        data = {
            'address_code'  : '1111017500'
        }
        code = data['address_code']
        
        response = self.client.get(f'/user/getnearaddress/{code}', json.dumps(data), **headers, content_type='application/json')

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json(), {'message' : 'SUCCESS'})

path parameter를 보내야 하는 경우, 왜인지는 모르겠지만 get메서드의 엔드포인트 경로를 user/getnearaddress/1111017500 이라고 적어두어도 해당 부분에서 에러가 나서 테스트코드가 작동하지 않았다.

다른 동기분께 들었던 해결책으로는, data 딕셔너리 안에서 get 해온 값을 그대로 엔드포인트 경로에 적어둘 경우 인식하지 못한다고 해서, 직접 변수에 값을 담아보기로 했다.

내게 일관되게 발생하는 에러 중 눈에 띄는 문구는 QueryString이라는 에러 👇🏻

가 문제인줄 알았는데 정말 어이없게도 ^^ 아무런 데이터도 매개변수로 받지 않고, path parameter만 받으면 되었는데, 내가 data를 보내고 있었다.

설마 해서 인자로 받지 않는 매개변수를 삭제해주니 정말 잘 돌아가는 콘솔창을 볼 수 있었다^^ 와아




👆🏻 이게 실제로 엔드포인트 호출을 했을 때의 데코레이터 request 모습,

👆🏻 이건 테스트 코드를 실행시켰을 때의 데코레이터 request모습!

결론!

한마디로 유닛 테스트 진행 시에 신경써야 할 점은,

  1. request-headers라 작성하고, 토큰이 될 대상의 key 값HTTP_라 명시해주기
  2. response 보낼 부분에 **headers로 인자 담기
profile
웹 개발 🐷😎👊🏻🔥

0개의 댓글