Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Service layer에서 발생한 셀레니움 관련 예외가 controller까지 던져지지 않도록 수정 #15

Merged
merged 3 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,8 @@ public PlaceInfoResponse getKakaoPlaceInfo(@RequestParam String kakaoPid) {

WebElement mArticle = driver.findElement(By.id("mArticle"));

BusinessHoursDto businessHours;
try {
businessHours = kakaoPlaceScrapingService.getBusinessHours(mArticle);
} catch (NoSuchElementException e) {
businessHours = new BusinessHoursDto(null, null);
}

String homepageUrl;
try {
homepageUrl = kakaoPlaceScrapingService.getHomepageUrl(mArticle);
} catch (NoSuchElementException e) {
homepageUrl = null;
}
BusinessHoursDto businessHours = kakaoPlaceScrapingService.getBusinessHours(mArticle);
String homepageUrl = kakaoPlaceScrapingService.getHomepageUrl(mArticle);

return PlaceInfoResponse.of(businessHours, homepageUrl);
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

import java.util.Arrays;
Expand All @@ -19,35 +21,58 @@ public class KakaoPlaceScrapingService {

private final OpeningHoursConverter converter;

public String getHomepageUrl(WebElement mArticle) throws NoSuchElementException {
return mArticle.findElement(By.cssSelector("div.cont_essential > div.details_placeinfo > div.placeinfo_default.placeinfo_homepage a.link_homepage")).getAttribute("href");
@Nullable
public String getHomepageUrl(WebElement mArticle) {
try {
return mArticle.findElement(By.cssSelector("div.cont_essential > div.details_placeinfo > div.placeinfo_default.placeinfo_homepage a.link_homepage")).getAttribute("href");
} catch (NoSuchElementException ex) {
return null;
}
}

public BusinessHoursDto getBusinessHours(WebElement mArticle) throws NoSuchElementException {
@NonNull
public BusinessHoursDto getBusinessHours(WebElement mArticle) {
try {
WebElement expandBtn = mArticle.findElement(By.cssSelector("div.cont_essential > div.details_placeinfo " +
"div.location_detail.openhour_wrap > div.location_present a.btn_more"));
WebElement expandBtn = mArticle.findElement(By.cssSelector("div.cont_essential > div.details_placeinfo div.location_detail.openhour_wrap > div.location_present a.btn_more"));
expandBtn.click();
} catch (NoSuchElementException e) {
WebElement ohInfo = mArticle.findElement(By.cssSelector("div.location_detail.openhour_wrap div.location_present"));
WebElement ohInfo;
try {
ohInfo = mArticle.findElement(By.cssSelector("div.location_detail.openhour_wrap div.location_present"));
} catch (NoSuchElementException ex) {
return new BusinessHoursDto(null, null);
}

String title;
try {
title = ohInfo.findElement(By.cssSelector("strong.tit_operation")).getText();
} catch (NoSuchElementException ex) {
return new BusinessHoursDto(null, null);
}

String title = ohInfo.findElement(By.cssSelector("strong.tit_operation")).getText();
if (!title.contains("영업")) {
return createBusinessHours(null, null);
return new BusinessHoursDto(null, null);
}

String openingHours = ohInfo.findElement(By.cssSelector("ul.list_operation")).getText();
return createBusinessHours(openingHours, null);
try {
String openingHours = ohInfo.findElement(By.cssSelector("ul.list_operation")).getText();
return createBusinessHours(openingHours, null);
} catch (NoSuchElementException ex) {
return new BusinessHoursDto(null, null);
}
}

// TODO: div.inner_floor, div.displayPeriodList ul이 반드시 존재한다고 가정한 코드. 아닌 경우가 있다면 try-catch로 감싸야한다.
WebElement operationList = mArticle.findElement(By.cssSelector("div.details_placeinfo div.fold_floor > div.inner_floor"));
String openingHours = operationList.findElement(By.cssSelector("div.displayPeriodList > ul:nth-child(2)")).getText();
System.out.println(openingHours);
String closingHours = getClosingHours(operationList);
return createBusinessHours(openingHours, closingHours);
try {
WebElement operationList = mArticle.findElement(By.cssSelector("div.details_placeinfo div.fold_floor > div.inner_floor"));
String openingHours = operationList.findElement(By.cssSelector("div.displayPeriodList > ul:nth-child(2)")).getText();
String closingHours = getClosingHours(operationList);
return createBusinessHours(openingHours, closingHours);
} catch (NoSuchElementException ex) {
return new BusinessHoursDto(null, null);
}
}

@NonNull
private BusinessHoursDto createBusinessHours(String openingHours, String closingHours) {
List<OpeningHourDto> openingHourDtos = converter.parseStrToOHs(openingHours);
if (openingHours != null && closingHours == null) {
Expand All @@ -58,10 +83,12 @@ private BusinessHoursDto createBusinessHours(String openingHours, String closing
return new BusinessHoursDto(openingHourDtos, closingHours);
}

@NonNull
private List<Day> findMissingDays(List<Day> days) {
return Arrays.stream(Day.values()).filter(day -> !days.contains(day)).toList();
}

@Nullable
private String makeClosingHours(List<Day> missingDays) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < missingDays.size(); i++) {
Expand All @@ -73,6 +100,7 @@ private String makeClosingHours(List<Day> missingDays) {
return sb.length() > 0 ? sb.toString() : null;
}

@Nullable
private String getClosingHours(WebElement operationList) {
try {
return operationList.findElement(By.cssSelector("div.displayOffdayList > ul:nth-child(2)")).getText();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

import static com.zelusik.scraping.constant.Day.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.mockito.BDDMockito.*;

@DisplayName("[Unit] Place scraping service test")
Expand Down Expand Up @@ -60,10 +59,10 @@ void givenMArticleWithoutHomepageUrl_whenGetHomepageUrl_thenReturnHomepageUrl()
given(mArticle.findElement(By.cssSelector("div.cont_essential > div.details_placeinfo > div.placeinfo_default.placeinfo_homepage a.link_homepage"))).willThrow(NoSuchElementException.class);

// when
Throwable t = catchThrowable(() -> sut.getHomepageUrl(mArticle));
String result = sut.getHomepageUrl(mArticle);

// then
assertThat(t).isInstanceOf(NoSuchElementException.class);
assertThat(result).isNull();
}

@DisplayName("영업시간 조회 - 버튼을 눌러 정보 열람 후, 영업시간과 휴무일 모두 있는 경우")
Expand Down Expand Up @@ -105,37 +104,10 @@ void existsButtonAndOpeningAndClosingHours_whenGetBusinessHours_thenReturnResult
@Test
void existsButtonAndOpeningAndClosingHoursAndAdditionalInfo_whenGetBusinessHours_thenReturnResult() {
// given
WebElement mArticle = createWebElemMock("mArticle");
WebElement button = createWebElemMock("button");
WebElement operationList = createWebElemMock("operationList");
String openingHours = "수~일 10:30 ~ 15:30";
List<OpeningHourDto> openingHourDtos = createOpeningHourDtoList();
String closingHours = "월요일\n화요일";
BusinessHoursDto expectedResult = new BusinessHoursDto(openingHourDtos, closingHours);

given(mArticle.findElement(By.cssSelector("div.cont_essential > div.details_placeinfo " +
"div.location_detail.openhour_wrap > div.location_present a.btn_more"))).willReturn(button);
willDoNothing().given(button).click();
given(mArticle.findElement(By.cssSelector("div.details_placeinfo div.fold_floor > div.inner_floor"))).willReturn(operationList);
WebElement openingHoursElem = createWebElemMock("openingHours");
given(operationList.findElement(By.cssSelector("ul:nth-child(2)"))).willReturn(openingHoursElem);
given(openingHoursElem.getText()).willReturn(openingHours);
WebElement closingHoursTitleElem1 = createWebElemMock("closingHoursTitle1");
given(operationList.findElement(By.cssSelector("strong:nth-child(3)"))).willReturn(closingHoursTitleElem1);
given(closingHoursTitleElem1.getText()).willReturn("공휴일");
WebElement closingHoursTitleElem2 = createWebElemMock("closingHoursTitle2");
given(operationList.findElement(By.cssSelector("strong:nth-child(5)"))).willReturn(closingHoursTitleElem2);
given(closingHoursTitleElem2.getText()).willReturn("휴무일");
WebElement closingHoursElem = createWebElemMock("closingHours");
given(operationList.findElement(By.cssSelector("ul:nth-child(6)"))).willReturn(closingHoursElem);
given(closingHoursElem.getText()).willReturn(closingHours);
given(converter.parseStrToOHs(openingHours)).willReturn(openingHourDtos);

// when
BusinessHoursDto actualResult = sut.getBusinessHours(mArticle);

// then
verifyGetBusinessHours(openingHours, expectedResult, actualResult);
}

@DisplayName("영업시간 조회 - 버튼을 눌러 정보 열람 후, 휴무일이 없는 경우")
Expand Down Expand Up @@ -167,6 +139,74 @@ void existsButtonAndOpeningHours_whenGetBusinessHours_thenReturnResult() {
verifyGetBusinessHours(openingHours, expectedResult, actualResult);
}

@DisplayName("영업시간 조회 - 버튼은 있으나 영업시간과 휴무일 정보를 담은 tag 자체를 찾을 수 없는 경우")
@Test
void notExistsInnerFloor_whenGetBusinessHours_thenReturnEmptyResult() {
// given
WebElement mArticle = createWebElemMock("mArticle");
WebElement button = createWebElemMock("button");
given(mArticle.findElement(By.cssSelector("div.cont_essential > div.details_placeinfo div.location_detail.openhour_wrap > div.location_present a.btn_more"))).willReturn(button);
willDoNothing().given(button).click();
given(mArticle.findElement(By.cssSelector("div.details_placeinfo div.fold_floor > div.inner_floor"))).willThrow(NoSuchElementException.class);

// when
BusinessHoursDto result = sut.getBusinessHours(mArticle);

// then
then(mArticle).should().findElement(By.cssSelector("div.cont_essential > div.details_placeinfo div.location_detail.openhour_wrap > div.location_present a.btn_more"));
then(button).should().click();
then(mArticle).should().findElement(By.cssSelector("div.details_placeinfo div.fold_floor > div.inner_floor"));
then(mArticle).shouldHaveNoMoreInteractions();
then(button).shouldHaveNoMoreInteractions();
assertThat(result)
.isNotNull()
.hasAllNullFieldsOrProperties();
}

@DisplayName("영업시간 조회 - 버튼이 없고 영업시간 정보를 담고 있는 상위 tag인 ohInfo도 없는 경우")
@Test
void notExistsButtonAndOhInfo_whenGetBusinessHours_thenReturnEmptyResult() {
// given
WebElement mArticle = createWebElemMock("mArticle");
given(mArticle.findElement(By.cssSelector("div.cont_essential > div.details_placeinfo div.location_detail.openhour_wrap > div.location_present a.btn_more"))).willThrow(NoSuchElementException.class);
given(mArticle.findElement(By.cssSelector("div.location_detail.openhour_wrap div.location_present"))).willThrow(NoSuchElementException.class);

// when
BusinessHoursDto result = sut.getBusinessHours(mArticle);

// then
then(mArticle).should().findElement(By.cssSelector("div.cont_essential > div.details_placeinfo div.location_detail.openhour_wrap > div.location_present a.btn_more"));
then(mArticle).should().findElement(By.cssSelector("div.location_detail.openhour_wrap div.location_present"));
then(mArticle).shouldHaveNoMoreInteractions();
assertThat(result)
.isNotNull()
.hasAllNullFieldsOrProperties();
}

@DisplayName("영업시간 조회 - 버튼이 없고, 제목을 찾지 못하는 경우")
@Test
void notExistsButtonAndTitle_whenGetBusinessHours_thenReturnEmptyResult() {
// given
WebElement mArticle = createWebElemMock("mArticle");
WebElement ohInfo = createWebElemMock("ohInfo");
given(mArticle.findElement(By.cssSelector("div.cont_essential > div.details_placeinfo div.location_detail.openhour_wrap > div.location_present a.btn_more"))).willThrow(NoSuchElementException.class);
given(mArticle.findElement(By.cssSelector("div.location_detail.openhour_wrap div.location_present"))).willReturn(ohInfo);
given(ohInfo.findElement(By.cssSelector("strong.tit_operation"))).willThrow(NoSuchElementException.class);

// when
BusinessHoursDto result = sut.getBusinessHours(mArticle);

// then
then(mArticle).should().findElement(By.cssSelector("div.cont_essential > div.details_placeinfo div.location_detail.openhour_wrap > div.location_present a.btn_more"));
then(mArticle).should().findElement(By.cssSelector("div.location_detail.openhour_wrap div.location_present"));
then(ohInfo).should().findElement(By.cssSelector("strong.tit_operation"));
then(mArticle).shouldHaveNoMoreInteractions();
then(ohInfo).shouldHaveNoMoreInteractions();
assertThat(result)
.isNotNull()
.hasAllNullFieldsOrProperties();
}

@DisplayName("영업시간 조회 - 버튼이 없고, 영업시간은 있는 경우")
@Test
void existsOpeningHours_whenGetBusinessHours_thenReturnResult() {
Expand Down Expand Up @@ -202,19 +242,55 @@ void existsNothing_whenGetBusinessHours_thenReturnResult() {
// given
WebElement mArticle = createWebElemMock("mArticle");
WebElement ohInfo = createWebElemMock("ohInfo");
BusinessHoursDto expectedResult = new BusinessHoursDto(null, null);
given(mArticle.findElement(By.cssSelector("div.cont_essential > div.details_placeinfo " +
"div.location_detail.openhour_wrap > div.location_present a.btn_more"))).willThrow(NoSuchElementException.class);
given(mArticle.findElement(By.cssSelector("div.cont_essential > div.details_placeinfo div.location_detail.openhour_wrap > div.location_present a.btn_more"))).willThrow(NoSuchElementException.class);
given(mArticle.findElement(By.cssSelector("div.location_detail.openhour_wrap div.location_present"))).willReturn(ohInfo);
WebElement ohInfoTitleElem = createWebElemMock("ohInfoTitle");
given(ohInfo.findElement(By.cssSelector("strong.tit_operation"))).willReturn(ohInfoTitleElem);
given(ohInfoTitleElem.getText()).willReturn("정보");

// when
BusinessHoursDto actualResult = sut.getBusinessHours(mArticle);
BusinessHoursDto result = sut.getBusinessHours(mArticle);

// then
then(mArticle).should().findElement(By.cssSelector("div.cont_essential > div.details_placeinfo div.location_detail.openhour_wrap > div.location_present a.btn_more"));
then(mArticle).should().findElement(By.cssSelector("div.location_detail.openhour_wrap div.location_present"));
then(ohInfo).should().findElement(By.cssSelector("strong.tit_operation"));
then(ohInfoTitleElem).should().getText();
then(mArticle).shouldHaveNoMoreInteractions();
then(ohInfo).shouldHaveNoMoreInteractions();
then(ohInfoTitleElem).shouldHaveNoMoreInteractions();
assertThat(result)
.isNotNull()
.hasAllNullFieldsOrProperties();
}

@DisplayName("영업시간 조회 - 버튼이 없고, '영업'이 포함된 제목은 있으나 영업시간 정보가 없는 경우")
@Test
void notExistsOpeningHoursInfo_whenGetBusinessHours_thenEmptyResult() {
// given
WebElement mArticle = createWebElemMock("mArticle");
WebElement ohInfo = createWebElemMock("ohInfo");
given(mArticle.findElement(By.cssSelector("div.cont_essential > div.details_placeinfo div.location_detail.openhour_wrap > div.location_present a.btn_more"))).willThrow(NoSuchElementException.class);
given(mArticle.findElement(By.cssSelector("div.location_detail.openhour_wrap div.location_present"))).willReturn(ohInfo);
WebElement ohInfoTitleElem = createWebElemMock("ohInfoTitle");
given(ohInfo.findElement(By.cssSelector("strong.tit_operation"))).willReturn(ohInfoTitleElem);
given(ohInfoTitleElem.getText()).willReturn("영업시간");
given(ohInfo.findElement(By.cssSelector("ul.list_operation"))).willThrow(NoSuchElementException.class);

// when
BusinessHoursDto result = sut.getBusinessHours(mArticle);

// then
verifyGetBusinessHours(null, expectedResult, actualResult);
then(mArticle).should().findElement(By.cssSelector("div.cont_essential > div.details_placeinfo div.location_detail.openhour_wrap > div.location_present a.btn_more"));
then(mArticle).should().findElement(By.cssSelector("div.location_detail.openhour_wrap div.location_present"));
then(ohInfo).should().findElement(By.cssSelector("strong.tit_operation"));
then(ohInfoTitleElem).should().getText();
then(ohInfo).should().findElement(By.cssSelector("ul.list_operation"));
then(mArticle).shouldHaveNoMoreInteractions();
then(ohInfo).shouldHaveNoMoreInteractions();
assertThat(result)
.isNotNull()
.hasAllNullFieldsOrProperties();
}

private static List<OpeningHourDto> createOpeningHourDtoList() {
Expand Down