swsnu / swppfall2020

28 stars 17 forks source link

mysql로 교체 시 테스트 에러 #227

Open KimJonghoSNU opened 3 years ago

KimJonghoSNU commented 3 years ago

안녕하세요, backend database를 mysql로 교체한 후, 웹사이트는 정상적으로 실행되지만 테스트에서 에러가 발생합니다. sqlite db를 사용할 시에는 테스트 코드가 문제 없이 실행되어 mysql 문제인 것 같은데 구글링해도 해결책을 찾지 못해 문의드립니다 대개 matching query를 찾지 못한다거나 id가 매우 큰 수를 반환합니다 ㅠ 혹시 원인을 알 수 있을까요?

또한 미봉책으로 test만 sqlite로 실행하고 서버 실행 시에는 mysql로 사용해도 되는 것인지 함께 질문드립니다.

image image

아래 issue에 조금 더 자세히 적어두었습니다. https://github.com/swsnu/swpp2020-team12/issues/41

ddanddan18 commented 3 years ago

제대로된 해결책은 아니지만 backend settings를 develop 따로 deploy버전 따로 만들어놓고 deploy시에만 mysql을 사용하는 방식이 잘 동작하는 것을 보았습니다!

Algy commented 3 years ago

test도 mysql로 하는 것이 동일성 측면에서 좋습니다만, sqlite로 하셔도 크게 상관은 없습니다. 테스트 후에 Last ID State가 남아서 이후 test에서 id가 1이 아닌 22로 나오는 것처럼 보이는데요, mysql을 사용하실것이라면 해당 테스트가 끝난 뒤 database에 찌꺼기 state가 없는지 (e.g. 테이블들이 잘 지워졌는지 , 데이터베이스가 없어졌는지) 확인해주세요.

Namnamseo commented 3 years ago

비교할 id를 하드코딩하고 계신 지점(https://github.com/swsnu/swpp2020-team12/blob/master/backend/caffeine/group/tests.py#L41 그리고 45, 50, 56번 줄 등)이 문제로 보입니다.

django의 TestCase 기능 덕분에, 테스트 도중 생성한 데이터(row)는 종료 시 자동으로 삭제됩니다. 이때 DB를 완전히 초기화해서 처리하지는 않고, transaction을 기록했다가 모두 revert시켜 처리합니다. 즉 테스트 시작부터 끝까지 쓰는 DB 인스턴스(schema)는 단 한 개입니다.

그리고 이 DB는 auto increment라는 편리한 기능이 있습니다. 모델에 숫자로 된 id라는 필드가 자동으로 붙고 이 번호가 1씩 증가하는데요, 이건 django가 DB의 auto increment 기능을 사용하기 때문입니다. 이 카운터는 TestCase가 종료하면서 데이터를 청소하더라도 초기화되지 않습니다.

5명의 유저를 생성하는 테스트 케이스 A와 B가 있고, ./manage.py test 명령어를 실행한 상황을 가정합니다. A → B 순서대로 실행된다면 A는 id: [1...5], B는 id: [6...10]의 유저를 받고, 반대로 B → A 순서대로 실행된다면 A는 id: [6...10], B는 id: [1...5]의 유저를 받게 됩니다.

이렇듯 각 test가 받는 id는 고정되어 있지 않습니다. 특히 테스트를 추가/삭제하면서 순서가 바뀔 때마다 값이 바뀔 가능성이 있습니다. 또, Windows와 Linux에서는 파일 목록의 순서가 다르기 때문에 플랫폼에 따라서도 테스트 실행 순서가 달라질 수 있습니다. (엄밀히 말하면 ntfs에서는 이름순이고, ext에서는 조금 더 random합니다) 마지막으로 질문 내용처럼 사용하는 DB 백엔드에 따라 이 auto increment 작동이 조금씩 다를 수 있습니다.

최선은 ID를 직접 지정하지 않는 방법입니다. 예를 들면 group/tests.py의 13-27번째 줄에서 만든 object의 ID를 저장해 두고,

self.group_ids = [group1.id, group2.id]

뒤쪽 테스트에서 이 값을 가져다 사용하는 방법이 있습니다.

self.assertEqual(response.json(), [{(...), 'id': self.group_ids[0], ...