[jstest] selenium + nodeJS #4

개요

이전 #3에서 이벤트 처리의 기본을 알아봤으니 좀 더 다양한 폼별 테스트를 진행하면서 여기에 맞게 worker.js도 개조해가겠습니다.

  • 이미 사용되는 코드는 날코드가 아니라 worker.js와 report.js에 의존하고 있습니다. 최소한 #3이라도 보셔야 그 안에 여지껏 전개된 코드가 있습니다.

일반적인 form요소의 테스트

가장 무식하게는 해당 엘리먼트를 클릭하고 전개되면 다시 다른 항목을 클릭하는 식으로 테스트할 수 있습니다.
예를 들어 라디오버튼을 테스트한다고 생각해보죠. 다음과 같이 html을 수정합니다.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>test1</title>
<style>
a{font-size:12px}
a:hover{font-size:20px}
</style>
</head>

<body>
<div id="test1">abc</div>
<a id="test2">link</a>
<form>
	<label><input type="radio" name="test3" value="radio1"/>radio1</label>
	<label><input type="radio" name="test3" value="radio2"/>radio2</label>
	<label><input type="radio" name="test3" value="radio3"/>radio3</label>
	<label><input type="radio" name="test3" value="radio4"/>radio4</label>
</form>
</body>
</html>

이제 name=”test3″를 갖는 radio 네 개를 문서에 추가했습니다. radio1을 클릭한 뒤 제대로 클릭된 것을 확인하려면 해당 엘리먼트의 checked속성값을 조사하여 ‘true’에 일치하면 될 것입니다. 이를 바탕으로 test를 작성합니다.

const Reporter = require('./reporter.js');
const reporter = new Reporter('https://www.bsidesoft.com/hika/wp/2196/report.html');
const Worker = require('./worker.js');
const worker = new Worker('https://www.bsidesoft.com/hika/wp/2196/test1.html');

{
	let radio;
	worker.elements('[name=test3]').
	then(v=>worker.action.click(radio = v[0]).perform()).
	then(_=>radio.getAttribute('checked')).
	then(v=>reporter.assert('radioClick Test', v, 'true'));
}

실제 코드를 테스트하는 부분을 살펴보죠.

  1. 우선 [name=test3]를 이용하여 라디오버튼 전체를 얻어옵니다.
  2. 얻어온 리스트 중 0번째를 radio변수에 넣고 이를 클릭하게 합니다.
  3. 해당 라디오버튼의 checked속성을 조사하게 되고,
  4. 이를 단정문으로 확인합니다.

실제 적용하면 실제 결과는 다음과 같이 보일 것입니다.
Screenshot_1

여기까지의 코드를 정리하여 레포지토리에 올려두었습니다.

https://github.com/hikaMaeng/seleniumNode/tree/master/3/1

테스트코드를 고도화하기

파이썬과 자바용 API에는 본디 Select라는 클래스가 제공되는데 selectByValue 같은 메소드를 통해 보다 쉽게 하위요소를 선택할 수 있게 도움을 줍니다. 하지만 도우미 클래스가 없어도 테스트는 코드레벨에서 더욱 고도화할 수 있습니다.

  1. 셀렉터를 이용해서도 보다 상세한 표현을 할 수 있는데 예를 들어 위에 v[0]에 해당되는 셀렉터는 ‘[name=test3][value=radio1]’로 표현할 수 있습니다.
  2. 하지만 then을 포함형태가 아니라 병렬 형태로 사용하려면, 어쩔 수 없이 외부 변수(let radio)를 사용할 수 밖에 없습니다. 타협점은 한 단계만 포함관계를 기술하여 스코프로 처리하는 방안입니다.

이 두 가지를 반영하여 코드를 수정해보죠.

worker.element('[name=test3][value=radio1]').
then(//radio공유를 위해 포함관계를 만든다.
  radio=>radio.click(). //귀찮으니 click() 사용 ^^;
  then(_=>radio.getAttribute('checked'))
).
then(v=>reporter.assert('radioClick Test', v, 'true'));

이 코드에서는 두 번째 then에서 click다음의 then을 가져가기 때문에 스코프를 통해 radio을 공유하여 외부 변수를 제거했습니다.
하지만 이렇게 해도 select는 경우는 2단계로 선택되기 때문에 반드시 단계를 밟아야합니다.

select 테스트

우선 select를 테스트하기 위해 html을 수정합니다.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>test1</title>
<style>
a{font-size:12px}
a:hover{font-size:20px}
</style>
</head>

<body>
<div id="test1">abc</div>
<a id="test2">link</a>
<form>
	<label><input type="radio" name="test3" value="radio1"/>radio1</label>
	<label><input type="radio" name="test3" value="radio2"/>radio2</label>
	<label><input type="radio" name="test3" value="radio3"/>radio3</label>
	<label><input type="radio" name="test3" value="radio4"/>radio4</label>
	<select name="test4">
		<option value="option1">option1</option>
		<option value="option2">option2</option>
		<option value="option3">option3</option>
		<option value="option4">option4</option>
		<option value="option5">option5</option>
	</select>
</form>
</body>
</html>

이제 select의 항목을 단계별로 선택해봅니다. 여기서 유의할 점이 전략을 다음과 같이 사용하려고 한다는 것입니다.

  1. 우선 [name=test4]를 찾아서 클릭한 뒤
  2. 그 자식 중 [value=option3]를 찾아서 클릭한다.

이 전략은 언뜻 맞는거 같지만 생각보다 selectBox는 훨씬 복잡한 객체입니다. 이 전략을 취하면 셀렉트박스의 값은 option3로 바뀐 상태로 여전히 셀렉트리스트가 펼쳐져있는 상황을 보게 됩니다.
자바와 파이썬용 API는 이를 다루는 전용 클래스(Select)를 제공하여 처리합니다만 nodeJS판에는 이 클래스가 없습니다. 따라서 자주 사용하는 꽁수는 sendKey를 활용한 꽁수입니다. 원래 셀렉트박스를 클릭한 뒤 키보드로 option에 해당되는 값을 쳐보면 그 항목이 선택됩니다. 이를 이용하는거죠. 다음과 같이 테스트를 작성합니다.

worker.element('[name=test4]').
then(
	select=>select.sendKeys('option3').
	then(_=>select.getAttribute('value'))
).then(v=>reporter.assert('select Test', v, 'option3'));

이전 예와 동일하게 포함된 then을 이용해 select를 공유하는 기법을 사용하고 있습니다. 여기까지 전개된 radio와 select의 테스트 결과는 다음과 같이 보일 것입니다.

Screenshot_2

여태 작성한 test.js는 아래 링크에 있습니다.

https://github.com/hikaMaeng/seleniumNode/tree/master/3/2

worker.js 수정하여 보다 편리하게 사용하기

이제 어느 정도 폼을 테스트하는 패턴이 보입니다.

  1. radio테스트에서는 click를 이용했고, checked속성을 얻어왔습니다.
  2. select테스트에서는 sendKey를 이용했고, value속성을 얻어왔습니다.

이를 통해 worker에 attribute라는 메소드를 추가하는 방안을 생각해볼 수 있습니다. 인자로는 다음과 같은 것이 필요할 것입니다.

  1. 최초 엘리먼트를 얻는 cssSelector(예를들어 ‘[name=test4]’ 같은..)
  2. 실행하고자하는 액션(click, sendKey) 만약 액션을 지정하지 않으면 즉시 속성값을 추출함
  3. 추출하고자 하는 속성값의 이름

헌데 여기서 sendKey의 경우는 ‘option3’ 라는 인자를 받았죠. 따라서 실질적으로는 액션을 위한 인자도 받아들여야할 것입니다.
최종적으로 도출된 메소드는 다음과 같습니다.

//인자에 객체해체를 쓰면 좀 가독성은 올라갈텐데 귀찮아서..죄송 =.=;
attribute(selector, attr, action = null, ...arg){
	return this.element(selector).then(
		el=>action ? el[action](...arg).then(_=>el.getAttribute(attr)) : el.getAttribute(attr)
	);
}

이를 worker.js에 반영하고 나면 위에 복잡했던 테스트 코드는 다음과 같이 줄어들 것입니다.

//radio
worker.attribute('[name=test3][value=radio1]', 'checked', 'click').
then(v=>reporter.assert('radioClick Test', v, 'true'));

//select
worker.attribute('[name=test4]', 'value', 'sendKeys', 'option3').
then(v=>reporter.assert('select Test', v, 'option3'));

다시 감당할 수 있는 복잡성 레벨로 테스트코드가 내려와서 다행입니다 ^^
여기까지의 코드는 다음의 링크에 있습니다.

https://github.com/hikaMaeng/seleniumNode/tree/master/3/3

결론

결국 셀레늄으로 테스트하는 대부분은 폼인 경우가 많아 폼에 대한 기본처리의 초석을 다져봤습니다. #5에서는 스크린샷을 떠볼 예정입니다.

%d 블로거가 이것을 좋아합니다: