Шаблон объекта страницы для тестов пользовательского интерфейса Xcode 7

Кто-нибудь успешно реализовал шаблон Page Object в своих тестах пользовательского интерфейса? Я попробовал и столкнулся с проблемой. Когда я использую предикат для ожидания существования элемента, я получаю предупреждение в журнале. Вот фрагмент кода из класса Page Object:

XCUIElement *myCoolButton = self.app.buttons[@"Super Cool Button"];
[self expectationForPredicate:self.existsPredicate evaluatedWithObject:myCoolButton handler:nil];
[self waitForExpectationsWithTimeout:5.0f handler:nil];

Когда этот код выполняется, я вижу в журнале следующее:

Questionable API usage: creating XCTestExpectation for test case -[MyPageObject (null)] which is not the currently running test case -[MyTestCase test]

и когда время ожидания превышено, я вижу ошибку в журнале, но сам тест на самом деле не терпит неудачу. Я предполагаю, что это связано с тем, что предикат настроен в классе Page Object, а не в самом тесте. Кто-нибудь смог обойти это?


person ryasui    schedule 02.05.2016    source источник


Ответы (2)


Из вопроса я предполагаю, что waitForExpection написан в классе Page Objects.

Как правило, объекты страницы не наследуются от XCTestCase, поскольку они используются только для хранения ссылок на все элементы пользовательского интерфейса, возможно, на странице. Следовательно, они не могут иметь действия подтверждения (waitForExpectation в этом случае).

Если вы пытаетесь написать повторно используемую функцию, которая может иметь такие утверждения, как waitForExpectation в классе PageObject, вам необходимо передать тестовый пример в качестве параметра функции (ваша функция должна принимать тестовый пример)

В этом случае я бы предложил вам написать категорию (расширение в swift) в XCTestCase с XCUIElement в качестве входного параметра, который ждет ForTheElementToExist вместо записи в классе pageobject. Вы можете избежать дублирования одного и того же кода во всех объектах страницы.

Пример кода здесь

class LoginPageObect {
 let userNameTextField  = XCUIApplication().textFields["UserName"]
 let passwordTextField = XCUIApplication().textFields["Password"]

  func loginUser(testcase: XCTestCase) {
    testcase.waitForElementToAppear(userNameTextField)
    testcase.waitForElementToAppear(passwordTextField)
     //Do rest of things here
   }
 }
extension XCTestCase {
  func waitForElementToAppear(element: XCUIElement) {
    let existsPredicate = NSPredicate(format: "exists == true")
    expectationForPredicate(existsPredicate,
                            evaluatedWithObject: element, handler: nil)
    waitForExpectationsWithTimeout(5, handler: nil)
  }
}
person Shahid    schedule 02.05.2016
comment
Спасибо Шахид. Вы правы, я унаследовал свой класс Page Object от XCTestCase. Попробую расширение. Однако не столкнемся ли мы с той же проблемой тайм-аута, не провалившего тест, поскольку я буду использовать расширение XCUIElement в классе объектов страницы (а не в самом тесте)? - person ryasui; 03.05.2016
comment
@ryasui: Извините, ryasui, имелось в виду написать расширение для XCTestCase, которое принимает XCUIElement... соответствующим образом отредактировал ответ - person Shahid; 03.05.2016
comment
@ryasui: объекты страницы не должны делать никаких утверждений. Таким образом, вам не нужно было бы писать waitForExpection в классе объектов страницы. В идеале он должен иметь ссылки только на объекты представления, которые используются в классе testcase для написания соответствующих тестов и утверждений. - person Shahid; 03.05.2016
comment
Привет Шахид. Извините, я не уверен, что понимаю. Я согласен с тем, что объекты страницы не должны выполнять утверждения. Однако, когда я использовал этот шаблон в прошлом, я инкапсулировал все взаимодействия с пользовательским интерфейсом в объекте страницы, включая ожидание существования элементов пользовательского интерфейса, так что сами тесты не должны были беспокоиться об этом. вещи. Конечно, это связано с написанием тестов для веб-интерфейса с помощью WebDriver/Java, поэтому я не уверен, как это применимо к тестам iOS. - person ryasui; 03.05.2016
comment
В любом случае, вернемся к вашему ответу. Часть, которой я не следую, — это расширение XCTestCase. Как это поможет, поскольку ожидания находятся в объекте страницы? Или среда тестирования не настроена для обработки этого шаблона, поэтому все предикаты ожидания должны находиться в самом тестовом классе? - person ryasui; 03.05.2016
comment
waitForExpectationsWithTimeout определен в XCTestCase, поэтому его нельзя использовать с объектами страницы, если вы не подклассируете его. Если вы не планируете создавать подклассы (чего, я считаю, не следует), то я прикрепил к ответу образец кода. Дай мне знать, если это работает. - person Shahid; 03.05.2016
comment
Я сделал первую часть, где вы передаете тестовый пример объекту страницы, и это решило мою проблему. Я добавлю расширение, чтобы у меня не было везде waitForExpectationsWithTimeout. Спасибо! - person ryasui; 05.05.2016
comment
Рад, что это помогло. Спасибо :) - person Shahid; 06.05.2016

Я использовал ту же идею передачи testCase по параметру, но позже она стала довольно уродливой. Один из подходов (тот, который я предпочитаю) для PageObjects говорит, что у вас не должно быть утверждений на страницах.

Итак, я придумал эту реализацию:

class Page {
    required init(_ c: ((Bool) -> Void)? = nil) {
        c?(waitForExistence())
    }
    func waitForExistence() -> Bool {
        fatalError("Subclass of Page should override \(#function)")
    }
}

Затем вместо использования waitForExpectation, для которого потребовался бы XCTestCase, вы можете использовать новый API XCode9 для XCUIElement: waitForExistence(timeout:) (или XCTWaiter, если вам нужно более сложное ожидание)

class LoginPage: Page {
    override func waitForExistence() -> Bool {
        return XCUIApplication().otherElements["view_login"].waitForExistence(timeout: 10)
    }

И использование будет:

LoginPage { 
   XCTAssertTrue($0, "Login page not shown") 
}
.type(email: "[email protected]")
person Daniel Carlos    schedule 15.11.2017