시운전에 관한 두 번째 장을 공유합니다. 여기에 있는 테스트는 주로 웹 백엔드 테스트를 위한 것입니다. 테스트 케이스를 작성해야 하는 이유(즉, 테스트 케이스 개선이 시간 낭비인지 여부), 테스트 케이스를 개선하는 방법, 코드 디자인으로 테스트 케이스 작성을 단순화할 수 있는 방법 , 그리고 몇 가지 이후의 아이디어.
1. 테스트 케이스를 작성해야 하는 이유
이러한 습관은 일반적으로 개발 진행을 지연시키는 동작으로 간주됩니다. 테스트 사례를 점진적으로 개선하려면 코드 개발과 거의 동일한 시간을 투자해야 합니다. 그러나 개발 과정에서 코드 조각을 개발한 후 책임을 지고 문제를 테스터에게 맡기지 않으면 일반적으로 이때 수동 테스트를 수행하게 됩니다. 예:
코드의 특정 메소드를 실행하여 출력 값이 예상한 것과 같은지 확인하세요.
데이터베이스/캐시를 수정한 다음 특정 메서드를 실행하여 데이터베이스 변경 사항이 예상한 대로인지 확인합니다.
도구를 사용하여 특정 인터페이스 요청을 시뮬레이션하여 데이터베이스의 인터페이스/변경된 값의 반환 값이 기대치를 충족하는지 확인합니다.
프런트엔드 페이지가 있는 경우 프런트엔드 및 백엔드 조인트 디버깅도 포함됩니다. 즉, 프런트엔드 페이지의 프런트엔드 상호 작용을 통해 프런트엔드 피드백이 기대치를 충족하는지 확인하여 간접적으로 확인합니다. 백엔드 코드의 정확성.
최신 테스트 도구는 이러한 수동 테스트 동작을 코드 블록으로 추상화하기 위해 최선을 다하고 있습니다. 의식적으로 수동 테스트를 수행하면 실제로 테스트 사례의 동작을 시도하기 시작한 것입니다. 테스트는 수동으로 수행할 수 있는데 왜 테스트를 구현하기 위해 코드를 사용해야 합니까?
코드를 재사용하거나 간단한 리팩토링을 통해 더 많은 기능을 얻을 수 있지만, 수동으로 수행하기로 선택한 경우 매번 다시 시작해야 합니다.
성숙한 워크플로에는 코드 검토 프로세스가 포함되어야 합니다. 코드를 문장별로 읽거나 테스트 코드의 완전성과 정확성을 확인한 다음 테스트 케이스를 실행하세요. 후자가 더 간단합니다.
버그 수정과 같이 코드가 변경되면 변경 사항이 코드에 의존하는 다른 부분에 영향을 미칠지 여부를 보장하기 어렵습니다. 수동 테스트 시대에는 버그를 수정한 후 시스템을 다시 테스트하는 회귀 테스트라는 것이 있습니다. 그러나 이미 완전한 테스트 사례가 있는 경우 명령을 직접 실행하면 됩니다.
코드를 리팩터링할 때도 마찬가지입니다.
2. 테스트 케이스를 개선하는 방법
정제 단계에 들어가기 전에 먼저 테스트 케이스를 어떻게 구현할 것인지에 대해 이야기하세요.
describe Meme do before do @meme = Meme.new end describe "when asked about cheeseburgers" do it "must respond positively" do @meme.i_can_has_cheezburger?.must_equal "OHAI!" end end describe "when asked about blending possibilities" do it "won't say no" do @meme.will_it_blend?.wont_match /^no/i end end end
위 코드는 Ruby의 minitest에서 가져온 것입니다. 이전에 포함된 코드 블록은 다음 테스트 케이스를 실행하기 전에 수행해야 하는 작업이며 일반적으로 테스트 케이스가 실행된 후에 실행될 해당 메서드도 지원합니다. 각 사용 사례마다 몇 가지 작은 판단이 내려집니다.
첫 번째 단락에서는 수동 테스트에 자주 포함되는 몇 가지 테스트 내용을 언급했습니다. 그 중 2가지와 3가지에 대해 설명하겠습니다. 데이터베이스 관련 테스트를 수행할 때 before에 테스트 데이터를 삽입하고 after에 테스트 데이터를 삭제해야 합니다. 중간 테스트 케이스에서는 해당 메소드를 실행하여 코드의 정확성을 확인합니다. 실행 완료 후: 데이터 변경 사항 확인/예상된 예외가 있는지 확인/예상한 결과가 반환되는지 확인합니다. 인터페이스인 경우 코드를 통해 해당 요청을 시작한 다음 반환된 콘텐츠가 예상된 결과를 반환하는지 확인하는 것입니다. 필요한 경우 데이터베이스의 데이터가 예상된 변경 사항을 충족하는지 확인합니다.
이제 테스트 사례가 생겼지만 아직 고려해야 할 특별한 사례가 있습니다. 이제 함수에 대한 비교적 완전한 테스트 사례를 작성했습니다. 실행한 후 테스트를 통과했지만 온라인 로그에 해당 함수에 대한 오류가 여전히 있음을 발견했습니다. 확인 결과, 이전 테스트에서 함수의 특정 분기가 테스트되지 않았음을 발견했는데, 이 분기에 온라인에서 문제가 발생하여 매우 모호한 구문 오류가 보고되었습니다. 모든 코드가 정확합니까? 여기서 소개해야 할 것은 테스트 케이스 커버리지라는 개념입니다. 기본적으로 모든 언어에는 해당 구현이 있습니다. 테스트 케이스 커버리지를 통해 테스트 케이스가 특정 파일의 모든 코드를 실행했는지 여부를 정량적으로 알 수 있습니다.
어떤 의미에서 테스트 케이스와 테스트 커버리지는 개발자가 자신의 코드에 대한 자신감을 높이는 데 사용되는 도구입니다. 그러나 그들은 전능하지 않습니다. 테스트 케이스에는 일부 매개변수가 누락될 가능성이 항상 있습니다. 물론 귀하의 코드는 이러한 가능성에 대해 작성되지 않았습니다. 결국 테스트 케이스 적용 범위는 귀하가 작성한 코드만 알려줄 수 있습니다. .. 테스트해봤지만, 고려하지 않은 가능성에 대해서는 할 수 있는 일이 없습니다. 따라서 가능한 한 엄격한 코드를 작성하십시오. 예를 들어 JavaScript에서는 == 대신 ===를 최대한 사용하고, 강력한 유형의 프로그래밍 표준 등을 사용하여 너무 큰 매개변수를 허용함으로써 발생할 수 있는 위험을 줄이십시오. 범위.
3. 코드 디자인으로 테스트 케이스 작성을 단순화하는 방법
웹에만 국한되지 않고 전체 웹에는 일반적으로 순수 데이터 처리 및 계산, 데이터베이스 관련, 특정 네트워크 프로토콜 관련이라는 세 가지 수준의 코드가 포함됩니다. 그중 순수 데이터 작업은 주로 일반적인 작업을 처리하는 함수 또는 기타 코드입니다. 데이터베이스의 경우 전통적인 의미에서 MVC의 M에 해당합니다. 이 세 가지 테스트는 첫 번째 섹션의 정규 테스트 내용 중 처음 세 항목에 해당합니다.
C 레벨에서는 일반적으로 페이지 렌더링 및 해당 프로토콜의 시뮬레이션이 포함되므로 일반적으로 기능 및 데이터베이스 관련 코드에 대한 테스트에 중점을 두면 컨트롤러 코드가 필요한 테스트 케이스 코드의 복잡성을 줄일 수 있습니다. . 보다 복잡한 애플리케이션에 대한 현재 권장 사항:
데이터의 기본 검증을 M 레이어에 둡니다. Ruby를 사용하여 개발하면 ActiveRecord와 Mongoid 모두 매우 편리한 검증 기능을 제공합니다.
모델 간 통신을 달성하기 위해 ORM에 제공된 일부 후크와 함께 코드에서 Pub/Sub 모드를 사용해 보세요. 예를 들어, A가 메시지가 생성될 때 이를 게시하면 B는 메시지를 들은 후 자신의 속성 값 중 하나를 수정합니다.
이메일 전송과 같은 일부 비업무 기능을 시스템에서 추출하려면 명령 모드를 사용하십시오.
위 제안에 대한 참고 자료: Laravel wisper resque
4. 아이디어
위 내용은 프런트엔드와 백엔드의 공동 디버깅이 필요한 테스트 사례를 피합니다. 다음 내용은 주로 이 영역에 중점을 둡니다. Ruby에는 이미 이 방향에 대한 몇 가지 우아한 구현이 있습니다. 관심이 있다면 먼저 Capybara로 직접 이동할 수 있습니다.
Selenium Phantomjs 및 Watir를 기반으로 하는 일련의 브라우저 드라이버가 인기를 끌면서 코드를 사용하여 브라우저를 제어하는 것은 더 이상 복잡한 문제가 아닙니다. 이 기능을 기반으로 프런트 엔드 기반 테스트를 네 단계로 나눌 수 있습니다.
等待某标志性元素出现(例如等待页面载入玩,或者某个内容异步加载出现)
模拟用户操作,这里的操作包括且不局限于用户点击、用户输入
等待反馈中标志性元素出现(例如某某输入框出现)
判断内容,是否符合预期
基于这个流程,可以解决绝大多数的前端测试。但是单纯依靠这个流程任然不够,因为页面中可能出现例如验证码这样的阻碍元素,在不修改代码的前提下,可以尝试通过数据库/缓存来取到这些内容。同样,和测试接口相同,这里也涉及到在测试前数据库中插入测试数据,测试用例执行后严重数据库里面数据变化,以及全部测试完毕后删除测试数据的内容。最终导致这块测试用例代码的实现需要同时对前端后端有一定的了解。目前还在考虑在借鉴 Capybara 的基础上,设计出更加通用的方案。
最后贴一段 Capybara 的代码结束这段内容:
feature "Signing in" do background do User.make(:email => 'user@example.com', :password => 'caplin') end scenario "Signing in with correct credentials" do visit '/sessions/new' within("#session") do fill_in 'Email', :with => 'user@example.com' fill_in 'Password', :with => 'caplin' end click_button 'Sign in' expect(page).to have_content 'Success' end given(:other_user) { User.make(:email => 'other@example.com', :password => 'rous') } scenario "Signing in as another user" do visit '/sessions/new' within("#session") do fill_in 'Email', :with => other_user.email fill_in 'Password', :with => other_user.password end click_button 'Sign in' expect(page).to have_content 'Invalid email or password' end end