selenium-java-maven-testng-dataprovider
안녕하세요, 오늘은 Selenium에서 DataProvider를 활용한 데이터 주도 테스트와 테스트 실무 관점에서 어떻게 사용하면 좋을지 알아보겠습니다.
우선 데이터 주도 테스트를 살펴보기 전에 필요한 개념부터 살펴보겠습니다.
블랙박스 테스트 데이터는 크게 Vaild(유효) Invalid(비유효) 2가지로 정의할 수 있는데요. Vaild 데이터를 활용한 테스트 시나리오를 Positive Testing 또는 Happy Path Testing으로 표현합니다. 조금 더 실무자의 관점에서 살펴보면 포괄적인 관점에서 흔히 얘기하는 정상적인 테스트 케이스를 의미합니다. Invalid 데이터를 활용한 테스트 시나리오가 흔히 얘기하는 Negative Testing 또는 예외 케이스라 불리는 테스트 시나리오입니다.
실무에서는 2가지 상황이 운영 환경에서 발생할 경우를 가정하고 출시 전 다양한 테스트 활동이 진행되어야 하는데요. 특히 로그인과 회원가입 기능에서 사용자의 정상 비정상 행동에 대한 예외처리가 이뤄지기 때문에 유효한 데이터뿐 아니라 비유효 데이터 검증이 필요합니다.
그런데 이러한 비유효 데이터 검증은 꽤나 번거로울 수 있습니다. 테스트 난이도가 높다기 보단 경우의 수를 리스트업 하여 테스트 시나리오 수행 간에 테스트 데이터로 활용해야 하기 때문입니다. 블랙박스 테스트 설계 기법인 페어 와이즈 기법을 활용하더라도 일정량의 리소스 투입은 불가피합니다. 중요한 건 이러한 유형의 테스트는 사람이 테스트하는 것보다 자동화를 시켜놓으면 적은 비용으로 높은 테스트 효율을 만들어낼 수 있습니다. 사람의 판단이 필요치 않기 때문이죠.
앞서 Vaild와 Invalid가 무엇인지 살펴보았습니다. 한 가지 고민해야 할 점은 경우의 수가 많다는 점인데요. 올바른 데이터와 그렇지 않은 데이터의 경우의 수를 떠올린다면 테스트 스크립트에 활용할 데이터가 꽤 많아진다는 걸 알 수 있습니다. 그런데 이러한 데이터가 하드 코딩으로 구현되었다면 어떻게 될까요?
몇 가지 예시를 들어보겠습니다. 만약 유효 데이터 1개와 비유효 데이터 3개를 검증해야 한다면 Java 기준으로 4개의 메서드가 구현되어야 합니다. 1개의 메서드에서 비유효 우선 검증 후 마지막에 유효 데이터를 검증하여 구현할 수도 있지만, 테스트 자동화 시나리오는 각 시나리오에 의존성이 생기지 않는 선에서 구현되어야 유지보수 측면에서 효율적이고 생산성이 높습니다. 마지막 유효 데이터 검증 이전에 런타임에서 예외가 발생한다면 이후 테스트 케이스를 검증하지 못합니다. 위와 같은 시나리오에서 발생하는 의존성 문제는 데이터 주도 테스트 접근법으로 쉽고 빠르게 해결 가능하므로 4개의 메서드를 모두 구현한다거나 1개의 메서드로 모든 로직을 처리하는 방법은 효율적이지 못합니다.
더불어 마지막 처리에 해당되는 Assert가 1개인 경우에는 위처럼 1개의 메서드로 구현이 가능하겠지만 만약 유효 데이터 2개와 비유효 데이터 2개를 검증해야 한다면 어떻게 될까요? 로그인 유효 시나리오는 로그인 처리가 진행된 상태이기 때문에 1개의 메서드로 구현할 경우 로그인된 계정을 로그아웃하여 테스트 초기 상태로 만드는 불필요한 로직이 추가되어야만 유효 2개 비유효 2개를 1개의 메서드로 구현할 수 있습니다.
이러한 문제를 쉽고 빠르게 해결하기 위해 데이터 주도 테스트 접근법을 실무에서 활용하며 TestNG 스위트 파일에 파라미터를 추가하여 해결하는 방법도 있지만, 실무에서는 다수의 파라미터를 다뤄야 하기 때문에 @DataProvider 어노테이션을 활용하여 해결하는 방법을 살펴보겠습니다. 경우에 따라 엑셀 파일을 불러올 수도 있지만 DataProvider 만으로도 최소한의 경우의 수를 검증할 수 있습니다.
유효 데이터 검증과 5개의 비유효 데이터 검증을 처리하는 테스트 시나리오를 구현하시오.
https://gist.github.com/Jiveloper/e9cb44715a602a92ff6a3bb44660ab1b
DataProvider는 2차원 배열로 구현할 수 있습니다. 2차원 배열은 배열의 요소로 1차원 배열을 가지는 배열입니다. DataProvider에서 열의 길이는 1쌍의 유효 또는 비유효 데이터를 의미합니다. 행의 길이는 1쌍의 데이터에서 파라미터로 넘길 데이터의 총개수입니다. 1개의 유효 데이터의 쌍, 5개의 비유효 데이터의 쌍, 그리고 2개의 파라미터를 구현하였습니다. 이렇게 구현된 getData() 메서드는 테스트 시나리오가 실행되는 클래스에 2차원 배열을 반환하여 활용됩니다.
https://gist.github.com/Jiveloper/43293bf1c1f3a031dce1da2723f2c1f5
LoginTest 클래스는 driver는 크롬, 사파리, 엣지, 파이어폭스 driver를 반환하는 메서드가 구현된 Base 클래스를 상속합니다. initializeDriver 클래스는 Webdriver를 반환하는 메서드입니다. 반환하기 전 implicitlyWait가 구현되어 있기 때문에 테스트 로직에서 Thread.sleep(2000); 과 같은 wait를 추가하지 않아도 위 코드는 정상 동작합니다. 따라서 위 코드에서 Thread.sleep()과 같은 비효율적인 wait를 사용하지 않았습니다. Thread.sleep()은 파이썬에서는 time.sleep(), 자바스크립트의 WebdriverIO에서는 browser.pause()와 동일한 역할을 수행하는 코드입니다.
로케이터는 page package 하위 파일에 존재하는 LoginPage 클래스에 WebElement를 반환하는 public 메서드로 구현되어 있습니다. 반환하는 시점에 driver를 사용해야 하므로 위와 비슷한 구조로 페이지 개체를 디자인할 경우 각 페이지에 생성자를 구현하고 테스트 로직 파일에서 객체를 생성할 때 driver를 넘겨줘야 합니다. 그렇지 않을 경우 컴파일 시점에서는 에러가 발생하지 않고 런타임 시점에서 java.lang.NullPointerException 에러가 발생하니 테스트 로직에서 저와 비슷한 구조로 페이지 개체를 활용하고 싶을 경우 이 점 참고해주시면 좋습니다.
https://gist.github.com/Jiveloper/43293bf1c1f3a031dce1da2723f2c1f5
페이지 개체 디자인에서 살펴본 코드를 다시 살펴보겠습니다. emailLoginTest 메서드 위에 @Test(dataProvider = "getData")가 작성되어 있습니다. 앞서 구현된 DataProvider 메서드를 emailLoginTest에 이차원 배열을 반환하도록 하려면 위와 같이 구현되어야 합니다.
여기서 중요한 점은 앞서 구현한 이차원 배열에서 반환할 데이터의 총 개수와 emailLoginTest 메서드의 인자 개수가 동일해야 합니다. 따라서 DataProvider 메서드에서 구현된 데이터의 타입은 모두 String 이므로 이메일과 패스워드에 해당하는 String 인자 2개가 구현된 모습입니다.
마지막 조건 처리는 password에 반환된 문자열 데이터가 정상 데이터라면 로그인 정상 처리를 검증하기 위해서 특정 페이지 로케이터의 텍스트를 가져와서 실제 값과 기댓값을 비교하는 assertEquals이 작성되었고 그렇지 않을 경우 에러 메시지 확인을 위한 분기 처리가 진행되었습니다.
https://gist.github.com/Jiveloper/0a2f1899fbfa9ead10fe8d152fc31d7c
꼭 testng.xml에서 실행하지 않아도 괜찮습니다. 보다 깔끔한 예시를 위해 testng.xml을 생성하고 위와 같이 구성하였습니다. testng 구성 옵션은 아래 문서를 참고하면 좋습니다.
첫 번째 유효 검증을 시작으로 총 6번의 크롬 브라우저가 실행되면서 Data Provider를 활용하여 1개의 메서드로 6개의 서로 다른 데이터가 검증된 모습입니다.
유효 비유효 데이터 및 커버리지 그리고 데이터 주도 테스트 접근을 실무에서 어떻게 진행하면 되는지 Selenium-java-maven-testng 환경에서 DataProvider를 통해 살펴보았습니다. 데이터 주도 테스트는 BDD 프레임워크인 Cucumber에서도 비슷한 동작 가능한 기능이 존재하므로 어떠한 상황일 때 데이터 주도 테스트를 활용하면 좋은지에 대해 알고 있다면 보다 수월한 시나리오 최적화가 가능할 것이라 생각합니다.
감사합니다.