programing

각도 테스트셀레늄이 있는 JS

muds 2023. 3. 2. 22:41
반응형

각도 테스트셀레늄이 있는 JS

스택 ASP MVC + Angular에 SPA 어플리케이션이 있다.JS와 저는 UI를 테스트하고 싶습니다.지금은 셀레늄과 팬텀을 시도하고 있어JS 및 WebKit 드라이버.

단일 요소가 포함된 샘플 테스트 페이지 보기입니다. 항목 " " "<li>앵귤러

<div id="items">
    <li>text</li>
    <li>text2</li>
</div>

테스트를 통과하려고 하는데 다음 행에 오류가 있습니다.

_driver.FindElements(By.TagName('li'))

이 시점에서는 로드된 요소와 _driver는 없습니다.PageSource에 요소가 없습니다.

어떻게 하면 아이템이 로딩될 때까지 기다릴 수 있나요?★★★★★★★★★★★★★★★★는 추천하지 말아 주세요.Thread.Sleep()

페이지 로드 / jquery.ajax(존재하는 경우) 및 $http 호출 및 그에 따른 다이제스트/렌더 사이클을 대기하고 유틸리티 함수로 던져놓고 대기합니다.

/* C# Example
 var pageLoadWait = new WebDriverWait(WebDriver, TimeSpan.FromSeconds(timeout));
            pageLoadWait.Until<bool>(
                (driver) =>
                {
                    return (bool)JS.ExecuteScript(
@"*/
try {
  if (document.readyState !== 'complete') {
    return false; // Page not loaded yet
  }
  if (window.jQuery) {
    if (window.jQuery.active) {
      return false;
    } else if (window.jQuery.ajax && window.jQuery.ajax.active) {
      return false;
    }
  }
  if (window.angular) {
    if (!window.qa) {
      // Used to track the render cycle finish after loading is complete
      window.qa = {
        doneRendering: false
      };
    }
    // Get the angular injector for this app (change element if necessary)
    var injector = window.angular.element('body').injector();
    // Store providers to use for these checks
    var $rootScope = injector.get('$rootScope');
    var $http = injector.get('$http');
    var $timeout = injector.get('$timeout');
    // Check if digest
    if ($rootScope.$$phase === '$apply' || $rootScope.$$phase === '$digest' || $http.pendingRequests.length !== 0) {
      window.qa.doneRendering = false;
      return false; // Angular digesting or loading data
    }
    if (!window.qa.doneRendering) {
      // Set timeout to mark angular rendering as finished
      $timeout(function() {
        window.qa.doneRendering = true;
      }, 0);
      return false;
    }
  }
  return true;
} catch (ex) {
  return false;
}
/*");
});*/

웹 사이트가 Angular를 사용하는지 여부를 확인할 수 있는 새 클래스를 만듭니다.JS는 다음과 같이 AJAX 콜의 송신을 종료했습니다.

import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;

public class AdditionalConditions {
    public static ExpectedCondition<Boolean> angularHasFinishedProcessing() {
        return new ExpectedCondition<Boolean>() {
            @Override
            public Boolean apply(WebDriver driver) {
                return Boolean.valueOf(((JavascriptExecutor) driver).executeScript("return (window.angular !== undefined) && (angular.element(document).injector() !== undefined) && (angular.element(document).injector().get('$http').pendingRequests.length === 0)").toString());
            }
        };
    }
}

다음 코드를 사용하여 코드의 모든 위치에서 사용할 수 있습니다.

WebDriverWait wait = new WebDriverWait(getDriver(), 15, 100);
wait.until(AdditionalConditions.angularHasFinishedProcessing()));

사내 프레임워크가 여러 사이트를 테스트하기 위해 사용되고 있고, 일부는 JQuery를 사용하고 있으며, 일부는 AngularJs를 사용하고 있습니다(또한 1은 혼합되어 있습니다!).프레임워크는 C#로 작성되어 있기 때문에 실행 중인 JScript는 (디버깅을 위해) 최소한의 청크로 실행하는 것이 중요합니다.실제로 위의 답변 중 많은 것을 취해서 그것들을 함께 뭉쳤다(따라서 신용이 있어야 할 곳은 @npjohns).다음은 우리가 수행한 작업에 대한 설명입니다.

다음은 HTML DOM이 로드된 경우 true/false를 반환합니다.

        public bool DomHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)
    {

        var hasThePageLoaded = jsExecutor.ExecuteScript("return document.readyState");
        while (hasThePageLoaded == null || ((string)hasThePageLoaded != "complete" && timeout > 0))
        {
            Thread.Sleep(100);
            timeout--;
            hasThePageLoaded = jsExecutor.ExecuteScript("return document.readyState");
            if (timeout != 0) continue;
            Console.WriteLine("The page has not loaded successfully in the time provided.");
            return false;
        }
        return true;
    }

다음으로 JQuery가 사용되고 있는지 여부를 확인합니다.

public bool IsJqueryBeingUsed(IJavaScriptExecutor jsExecutor)
    {
        var isTheSiteUsingJQuery = jsExecutor.ExecuteScript("return window.jQuery != undefined");
        return (bool)isTheSiteUsingJQuery;
    }

JQuery가 사용되는 경우 로드되었는지 확인합니다.

public bool JqueryHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)
        {
                var hasTheJQueryLoaded = jsExecutor.ExecuteScript("jQuery.active === 0");
                while (hasTheJQueryLoaded == null || (!(bool) hasTheJQueryLoaded && timeout > 0))
                {
                    Thread.Sleep(100);
                timeout--;
                    hasTheJQueryLoaded = jsExecutor.ExecuteScript("jQuery.active === 0");
                    if (timeout != 0) continue;
                    Console.WriteLine(
                        "JQuery is being used by the site but has failed to successfully load.");
                    return false;
                }
                return (bool) hasTheJQueryLoaded;
        }

그런 다음 Angular에 대해 동일한 작업을 수행합니다.JS:

    public bool AngularIsBeingUsed(IJavaScriptExecutor jsExecutor)
    {
        string UsingAngular = @"if (window.angular){
        return true;
        }";            
        var isTheSiteUsingAngular = jsExecutor.ExecuteScript(UsingAngular);
        return (bool) isTheSiteUsingAngular;
    }

사용 중인 경우 로드되었는지 확인합니다.

public bool AngularHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)
        {
    string HasAngularLoaded =
        @"return (window.angular !== undefined) && (angular.element(document.body).injector() !== undefined) && (angular.element(document.body).injector().get('$http').pendingRequests.length === 0)";            
    var hasTheAngularLoaded = jsExecutor.ExecuteScript(HasAngularLoaded);
                while (hasTheAngularLoaded == null || (!(bool)hasTheAngularLoaded && timeout > 0))
                {
                    Thread.Sleep(100);
                    timeout--;
                    hasTheAngularLoaded = jsExecutor.ExecuteScript(HasAngularLoaded);
                    if (timeout != 0) continue;
                    Console.WriteLine(
                        "Angular is being used by the site but has failed to successfully load.");
                    return false;

                }
                return (bool)hasTheAngularLoaded;
        }

DOM 이 정상적으로 로드되고 있는 것을 확인하면, 다음의 bool 값을 사용해 커스텀 대기를 실행할 수 있습니다.

    var jquery = !IsJqueryBeingUsed(javascript) || wait.Until(x => JQueryHasLoaded(javascript));
    var angular = !AngularIsBeingUsed(javascript) || wait.Until(x => AngularHasLoaded(javascript));

Angular를 사용하는 경우그렇다면 JS는 Protractor를 사용하는 것이 좋습니다.

프록터를 사용하는 경우 http 요구가 완료될 때까지 대기하는 wait For Angular() 메서드를 사용할 수 있습니다.요소가 표시될 때까지 기다렸다가 실행하는 것이 좋습니다.언어 및 구현에 따라서는 동기 언어로 표시될 수 있습니다.

WebDriverWait wait = new WebDriverWait(webDriver, timeoutInSeconds);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id<locator>));

또는 JS에서는 true를 반환할 때까지 함수를 실행하는 wait 메서드를 사용할 수 있습니다.

browser.wait(function () {
    return browser.driver.isElementPresent(elementToFind);
});

유용한 코드 조각들을 얻기 위해 굴착기를 채굴해도 좋습니다.이 함수는 Angular가 페이지 렌더링을 완료할 때까지 차단합니다.Shahzaib Salim의 답변의 변형입니다.단, Shahzaib Salim은 폴링하고 있고, 저는 콜백을 설정하고 있습니다.

def wait_for_angular(self, selenium):
    self.selenium.set_script_timeout(10)
    self.selenium.execute_async_script("""
        callback = arguments[arguments.length - 1];
        angular.element('html').injector().get('$browser').notifyWhenNoOutstandingRequests(callback);""")

'html은 ''이라는 ng-app.

https://github.com/angular/protractor/blob/71532f055c720b533fbf9dab2b3100b657966da6/lib/clientsidescripts.js#L51 에서 입수할 수 있습니다.

다음 코드를 실행했는데 비동기 레이스 조건 실패에 도움이 되었습니다.

$window._docReady = function () {
        var phase = $scope.$root.$$phase;
        return $http.pendingRequests.length === 0 && phase !== '$apply' && phase !== '$digest';
    }

Selenium PageObject 모델에서는 다음을 기다릴 수 있습니다.

Object result = ((RemoteWebDriver) driver).executeScript("return _docReady();");
                    return result == null ? false : (Boolean) result;

말씀하신 대로 웹 앱이 실제로 Angular를 사용하여 작성되었다면 엔드 투 엔드 테스트를 수행하는 가장 좋은 방법은 Protractor를 사용하는 것입니다.

프로젝터는 프로젝터, 프로젝터 waitForAngular방법 - 각도 수정이 완료될 때까지 자동으로 대기합니다.

따라서, 일반적인 경우, 당신은 명시적인 것을 쓸 필요가 없습니다.wait테스트 케이스의 경우: 연장기가 대신합니다.

Angular Phonecat 튜토리얼을 참조하여 프로젝터를 설정하는 방법을 배울 수 있습니다.

프로젝터를 진지하게 사용하려면 페이지 오브젝트를 해야 합니다.예를 들어 Angular Phonecat의 my page object 테스트 스위트를 참조하십시오.

Protractor를 사용하면 테스트를 C#이 아닌 Javascript(Protractor는 실제로 노드를 기반으로 함)로 작성할 수 있지만, 그 대가로 Protractor는 모든 것을 기다립니다.

iframes를 포함하고 Englular와 함께 개발된 HTML 페이지에 대한 나의 특별한 문제는JS는 다음 트릭을 통해 많은 시간을 절약할 수 있었습니다.DOM에는 모든 내용을 감싸는 iframe이 있는 것을 분명히 보았습니다.다음 코드가 작동해야 합니다.

driver.switchTo().frame(0);
waitUntilVisibleByXPath("//h2[contains(text(), 'Creative chooser')]");

하지만 그것은 작동하지 않았고 "프레임으로 전환할 수 없습니다.창이 닫혔습니다.그 후 코드를 다음과 같이 수정했습니다.

driver.switchTo().defaultContent();
driver.switchTo().frame(0);
waitUntilVisibleByXPath("//h2[contains(text(), 'Creative chooser')]");

그 후 모든 것이 순조롭게 진행되었다.따라서 Angular는 iframes로 무언가를 망치고 있었고, 그 드라이버는 기본 콘텐츠에 초점을 맞추고 있다고 예상했을 때 Angular 프레임에 의해 이미 삭제된 일부에 의해 초점이 맞춰졌습니다.이게 여러분들에게 도움이 되길 바랍니다.

만약 당신이 모든 것을 Protractor로 바꾸고 싶지 않지만 Angular를 기다리고 싶다면, 는 Paul Hammants ngWebDriver(Java)를 사용하는 것을 추천합니다.그것은 견인기를 기반으로 하지만 당신은 바꿀 필요가 없습니다.

액션을 실행하기 전에 Angular(ngWebDriver의 waitForAngularRequestsToFinish()를 사용하여)를 기다리는 액션 클래스를 작성하여 문제를 해결했습니다.

코드 스니펫에 대해서는 이 질문에 대한 답변을 참조하십시오.

다음은 WebDriverJS를 사용하는 경우 Angular에서 대기하는 방법의 예입니다.원래는 커스텀 조건을 만들어야 한다고 생각했는데wait는 임의의 기능을 받아들입니다.

// Wait for Angular to Finish
function angularReady(): any  {
  return $browser.executeScript("return (window.angular !== undefined) && (angular.element(document).injector() !== undefined) && (angular.element(document).injector().get('$http').pendingRequests.length === 0)")
     .then(function(angularIsReady) {                        
                    return angularIsReady === true;
                  });
}

$browser.wait(angularReady, 5000).then(...);

안타깝게도 이것은 팬텀과 함께 작동하지 않습니다.CSP(content-security-policy)에 의한 JS 및unsafe-eval. Windows에서 헤드리스 Chrome 59를 기다릴없습니다.

D Sayar의 답변을 바탕으로 사용법을 구현했습니다. 그리고 누군가에게 도움이 될 수 있습니다.여기서 언급한 모든 부울 함수를 단일 클래스에 복사하고 PageCallingUtility() 메서드 아래에 추가해야 합니다.이 메서드는 내부 종속성을 호출합니다.

통상의 사용법에서는, PageCallingUtility() 메서드를 직접 호출할 필요가 있습니다.

public void PageCallingUtility()
{
    if (DomHasLoaded() == true)
    {
        if (IsJqueryBeingUsed() == true)
        {
            JqueryHasLoaded();
        }

        if (AngularIsBeingUsed() == true)
        {
            AngularHasLoaded();
        }
    }
}

에디크의 제안 말고.각을 테스트하는 경우JS 앱, 나는 당신이 프로젝터에 대해 생각해 볼 것을 강력히 권합니다.

프로젝터가 대기 문제(동기, 비동기)를 해결하는 데 도움이 됩니다.하지만 몇 가지 주의사항이 있습니다.

1 - javascript에서 테스트를 개발해야 합니다.

2 - 흐름 처리에는 몇 가지 다른 메커니즘이 있습니다.

언급URL : https://stackoverflow.com/questions/25062969/testing-angularjs-with-selenium

반응형