오늘 배운 것

동기 미들웨어를 통해서 특정 API 요청이 들어왔을 때만 FCM 알림을 보내보도록 하겠다. 다음과 같은 방식으로 동기 미들웨어를 만든 뒤, 해당 미들웨어를 settings.py의 MIDDLEWARE 리스트 변수에 추가해 주었다. 

class FCMAlarmMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response

    def startswith_fcm_alarm_paths(self, path):
        for p in FCM_ALARM_PATHS:
            if path.startswith(p):
                return True
        return False

    def __call__(self, request):

        if request.method in FCM_ALARM_METHODS and self.starts_with_fcm_alarm_paths(request.path):
            fcm_token = request.auth.token
            other_device = FCMDevice.objects.filter(user=request.user).exclude(registration_id=fcm_token)

            if other_device.exists():
                device_id = other_device.first().registration_id
                
                if request.path.startsWith(FCM_ALARM_PATH_TODO):
                    send_push_notification(device_id, "Todo", "")
                elif request.path.startsWith(FCM_ALARM_PATH_SUBTODO):
                    send_push_notification(device_id, "Subtodo", "")
                elif request.path.startsWith(FCM_ALARM_PATH_CATEGORY):
                    send_push_notification(device_id, "Category", "")

 

이제 확인차 서버가 잘 실행되는지를 보려고 하는데, 서버 자체는 잘 실행되는데 다른 에러가 났다. MIDDLEWARE의 값으로 주어진 다른 allauth 미들웨어에서 나는 오류였다. 현재는 allauth를 안 사용하고 있었기 때문에 해당 미들웨어를 지우고 싶었는데, 그러려고 하니 또 다른 에러가 났다. 

 

그래서 INSTALLED_APPS에서 allauth를 제거하고 다시 시도해봤다. 그랬더니 allauth 관련 에러는 나오지 않았다. 문제는 또 다른 XFrameOptionsMiddleware에서 또 에러가 났다. 해당 미들웨어는 어떤 미들웨어인지 모르기 때문에, 어떤 일을 하는지 알아보고 지워주는 것이 맞겠다. 

 

그리고 중간에 pytest도 실행시켜봤는데 다른 테스트들이 죄다 fail이 나기 시작했다. 원인은 위에서 작성한 FCMAlarmMiddleware에서는 request.auth.token이라는 값을 필요로 하는데, 이 값이 테스트에서 사용되는 WSGIRequest의 속성에는 없기 때문이다. 그런데 생각해보니, 기존에 작성된 테스트들에서는 FCM 알림이 보내지는 것까지를 테스트할 필요가 없었다. 그러므로 해당 미들웨어는 테스트 때는 우회를 해도 된다고 판단했다. 

 

그러면 현재 할 일은 'pytest', 'python manage.py runserver' 커맨드를 입력했을 때 기본 URL이 오류 없이 동작하는 것이다. 이를 위해서는 두 가지를 해결해야 한다.

 

1. XFrameOptionsMiddleware 알아보고 불필요하다면 지우기

2. 테스트 환경에서만 FCMAlarmMiddleware 우회하기

 

2번이 더 간단해서 먼저 해보자면, pytest에서 자동으로 사용되도록 어떤 fixture를 하나 만들어두고 그 fixture에서 사용하고자 하는 미들웨어를 설정값으로 넣어주면 되었다. pytest 공식문서를 참고해보니, conftest.py라는 파일을 디렉토리 안에 만들면 해당 및 하위 디렉토리의 테스트들에서 해당 파일에 있는 fixture 등을 사용할 수 있다고 한다. 

 

알고보니 이전에 만들어 둔 conftest.py 파일이 있어서 해당 파일 안에서 바로 작업하기로 했다. TEST_MIDDLEWARE는 MIDDLEWARE에서 특정 불필요한 미들웨어들만 뺀 변수이다. 

@pytest.fixture(autouse=True)
def skip_fcm_middleware():
    from django.conf import settings

    settings.MIDDLEWARE = settings.TEST_MIDDLEWARE

 

그랬더니 1개의 testcase만 fail하고 나머지는 다 성공하였다. 

 

fail한 경우는 fcm 알람 테스트였다. 이전에 테스트를 했을 때는 성공으로 나오던 알람이 잘 가지 않아서 Fail이 난 경우였다. 코드를 보니 테스트 코드에서는 별도의 테스트 문자열이 fcm 토큰값이라고 가정하고 이를 넣어주고 있었는데, 실제 FCMDevice를 조회해 보니 해당하는 fcm 토큰값을 갖고 있는 객체가 없었어서 에러가 난 것이었다. 

 

그렇다면 별도의 mock 객체를 만들어서 FCMDevice에 값을 넣어준 후, 해당 객체의 fcm 토큰으로 이를 테스트해봐야 되겠다. 

 

궁금한 점

1. 테스트 때 사용되는 WSGIRequest는 구체적으론 무엇이며, 일반 request와는 어떻게 다를까?

2. 함수 안에서 패키지나 모듈을 import 하는 것과 밖에서 전역으로 import 하는 것은 어떤 차이가 있을까?

3. mock 객체의 개념이 잘 이해가 안 된다...

 

+ Recent posts