- 버전 관리
- 회귀 테스트
- 전체 자동화 이 세가지는 모든 프로젝트를 지탱하는 기둥이다. 어떻게 활용되는지 살펴보자
프로젝트를 빌드하는데 필요한 모든 것을 버전 관리 하에 두어야 한다.
그렇게 하면 빌드 장비를 일시적으로 쓰고 없앨 수 있다. 배포 설정도 버전관리 시스템 안에 있으므로 실제 서비스에 릴리스 하는것도 자동으로 처리된다.
이 부분이 중요하다. 프로젝트 차원에서는 버전 관리 시스템이 빌드와 릴리스 프로세스를 운용한다.
빌드는 클라우드의 컨테이너 위에서 돌아가고, 테스트를 위해 스테이징 서버에 배포할지 실제 서비스에 릴리스 할지는 버전관리 시스템의 태그를 사용하여 지정한다.
릴리스 절차가 더 간단해져서 일상의 일부가 되고, 빌드 장비나 개발자의 장비에 의존하지 않는 진정한 지속적인 배포가 가능해진다.
많은 개발자들이 무의식적으로 코드가 어디에서 꺠지는지 파악하고서는 약한 지점을 피해 다니면서 살살 테스트하려 하지만, 실용주의 프로그래머들은 다르다.
우리는 지금 당장 버그를 찾아 나서도록 몰아세우지마느 덕분에 나중에 다른 사람이 자기 버그를 발견하게 되는 상황을 피할 수 있다.
코드를 작성하자마자 테스트해야 한다. 작은 버그일지라도 빠르게 확산되어 나중에는 손쓸수없는 버그가 될 가능성이 있고, 그렇게 되면 해결하기 어려워진다.
그래서 우리는 단위테스트를 아주 많이 작성해야 한다.
훌륭한 프로젝트는 제품코드보다 테스트 코드가 더 많을수도 있다. 이렇게 노력을 쏟는 시간에는 그 노력만큼의 가치가 있다.
테스트를 통과했다는 것은 그 코드가 '완성'되었다는 말에 높은 수준의 확신을 준다.
'진짜 상황 테스트'를 목표로 하는 것이 중요한데, 테스트 환경은 실제 환경과 최대한 비슷해야 하고, 이 차이에서 버그가 번식하게 된다.
단위 테스트는 하나의 모듈을 테스트하는 코드이고, 이 항목에서 논의할 다른 모든 형태 테스트의 근간이 된다.
부분으로 떼어놓았을때 제대로 작동하지 않는다면, 합쳤을때도 역시 제대로 동작하지 않을 것이다.
다음 단계로 넘어가기 전 우리의 모든 모듈의 단위 테스트가 반드시 통과해야 한다.
통합 테스트는 프로젝트를 구성하는 주요 서브시스템이 다른 부분과 제대로 작동하는지 보여준다.
게약이 제대로 되어 있고 테스트가 잘 되어있다면, 어떤 문제든 쉽게 발견할 수 있다.
통합테스트는 앞서 설명한 단위 테스트의 확장에 지나지 않는다. 단지 전체 서브시스템들이 모두 계약을 제대로 지키는지 테스트하는 것 뿐이다.
실행가능한 사용자 인터페이스나 프로토타입이 갖춰지자마자 가장 중요한 질문에 대답해야 하는데,
이게 정말 사용자들이 필요로하는가? 시스템의 기능적 요구 사항을 충족하는가? 이것 역시 테스트해 봐야 한다.
버그가 없는 시스템일지언정 잘못된 문제를 푼다면 별 쓸모가 없어지게 된다.
성능 테스트 혹은 스트레스 테스트 역시 중요한 측면이다.
소프트웨어가 실제 조건에서 성능 요구 사항들을 준수하는지 자문해 보라. 사용자 수나 접속 수, 초당 트랜잭션 숫자를 염두하여 감당이 가능한가?
우리는 완벽한 소프트웨어를 작성할 수 없기에, 완벽한 테스트 소프트웨어 역시 작성할 수 없다. 그렇다면 테스트를 테스트할 필요가 있다.
어떤 버그를 감지해 내는 테스트를 작성한 후에, 그 버그가 의도적으로 생기도록 한 다음 테스트가 경보를 울리는지 확인하라.
이렇게 하면 실제로 버그가 생겼을 때 테스트가 그걸 잡아낼 것이라고 확신할 수 있다.
테스트를 작성할 땐 경보가 필요할 때 정말 울리는지 확인하라.
테스트가 올바르다는 확신이 들고 우리가 고의로 만들어낸 버그도 찾아냈더라도, 코드 전체를 철저하게 테스트했다는 것은 어떻게 알수 있을까?
한마디로 답하자면 '알수 없다' 그리고 앞으로도 알 수 없을 것이다.
'커버리지 분석'도구를 적용해 볼 수도 있다. 커버리지 분석도구는 테스트 중에 코드를 지켜보면서 코드의 어느 줄이 실행되지 않았는지 알려준다.
이런 도구들 덕에 코드의 어느 줄이 실행되지 않았는지 알수 있고, 우리의 테스트가 얼마나 포괄적인지 대략적인 느낌을 얻을 수 있지만 100% 커버리지를 기대하지는 마라.
우리의 코드가 예상치 못한 상태를 어떻게 다루는지 확인하기 위해 컴퓨터가 그런 상태들을 생성하도록 하면 좋을 것이다.
테스트하려는 코드의 계약과 불변식에 따라 테스트 데이터를 생성하는 '속성 기반' 테스트 기법을 사용하자.
테스트에서 가장 중요한 개념이 있는데, 뻔하고 거의 모든 교과서에서 이렇게 하라고 말하고 있다.
하지만 무슨 이유에서인지 대다수 프로젝트에서 지켜지지 않는다.
버그가 기존 테스트의 그물을 빠져나갔다면 다음번에는 그걸 잡아낼 수 있도록 새 테스트를 추가해야 한다.
한번 테스터가 버그를 찾았다면, 더는 테스터가 그 버그를 만나서는 안된다. 해당 버그를 확인할 수 있게 자동화 테스트를 수정해야 한다.
그런 일은 앞으로 다시 일어날 것이고, 자동화 테스트가 우리를 대신해 찾아 줄 수 있는 버그까지 찾아다닐 시간이 없다.
우리는 새 코드(+ 새로운 버그)를 작성하는데 시간을 쏟아야 한다.
현대 소프트웨어 개발은 스크립트화된 자동 절차에 의존하고 있다.
rsync나 ssh를 넣은 셸 스크립트처럼 단순할 수도 있고, 앤서블이나, 퍼핏, 셰프 등등 처럼(사실 잘 모르겠음...) 강력한 기능을 갖춘 패키지를 사용할 수도 있다.
어쨌든 수작업이 필요해서는 안된다. 당연하게도 모든 개발자의 컴퓨터는 조금씩 다르게 설정되있다.
동일한 코드를 실행하는데도 애플리케이션의 동작은 개발자마다 미묘한 차이를 보이게 된다.
사람들은 컴퓨터처럼 같은 일을 반복할 수 없을 뿐더러 그런 것을 기대해서도 안된다.
버전 관리, 가차없는 테스트, 전체 자동화라는 세 기둥이 있다면 우리의 프로젝트에 필수적인 견고한 기반이 생긴 것이고,
우리는 사용자를 기쁘게 한다는 어려운 부분에 집중 할 수 있게 될수 있다.