9

Sto costruendo un modello di oggetto Page in Selenium WebDriver per C#, utilizzando PageFactory.Come inizializzare SelectElements usando PageFactory/FindsBy in Selenium C#?

Sfortunatamente, ho scoperto che lo FindsByAttribute non inizializzerà un SelectElement (HTML <select> menu/menu a discesa). Ho capitato su o venire con alcune idee per lavorare intorno ad esso fino ad ora, ma nessuno di loro è l'ideale:

  1. PageFactory e FindsByAttribute sono sealed, quindi non posso costringerlo a semplicemente ereditare quelle .
  2. L'istanzializzazione manuale di SelectElement da un IWebElement in ciascun metodo è piuttosto disordinata e duplicata. Ignora anche l'apparenza incorporata apparente in PageFactory e genera NoSuchElementException s a meno che non aggiungo un'attesa ogni volta che lo faccio - il che richiederebbe la ripetizione del localizzatore ovunque, sconfiggendo (in parte) lo scopo del POM.
  3. Il wrapping di ciascuna proprietà IWebElement con una proprietà SelectElement è meno disordinato, ma presenta ancora lo stesso problema di attesa di cui sopra.

L'opzione migliore finora è # 3 e la scrittura di un wrapper per SelectElement che aggiunge solo un'attesa a ogni metodo. Anche se questa soluzione sarà lavoro, sarà irrobustirsi il codice di ogni pagina un sacco, come invece di questo (ipotetico) Codice abbastanza:

[FindsBy(How = How.Id, Using = "MonthDropdown")] 
public SelectElement MonthDropdown; 

mi sono bloccato con un wrapper involucro (qualcosa che mi volentieri a meno), e:

[FindsBy(How = How.Id, Using = "MonthDropdown")] 
private IWebElement _monthDropdown; 
public Selector MonthDropdown 
{ 
    get { return new Selector(MonthDropdown, Wait); } 
} 

Con Selector essere il SelectElement involucro, che deve anche prendere in IWait<IWebDriver> in modo che possa aspettare, e istanziare un nuovo Selector ogni volta che vi accedono.

C'è un modo migliore per farlo?

MODIFICA: Modificatori di accesso errati inseriti in modo assente. Fisso. Grazie, @JimEvans.

+0

Grazie mille per questa domanda e risposta. +1 –

risposta

13

In primo luogo, non c'è "attesa integrata" nell'implementazione .NET PageFactory. Puoi facilmente specificarne uno nella chiamata a InitElements (di più su questo in un po '). Al momento, l'opzione migliore per te sarebbe la tua opzione 3, anche se non esporrò il membro IWebElement; Lo renderei private, dal momento che lo PageFactory può enumerare su membri privati ​​con la stessa facilità di quelli pubblici. Così il vostro oggetto pagina sarebbe simile a questa:

[FindsBy(How = How.Id, Using = "MonthDropdown")] 
private IWebElement dropDown; 
public SelectElement MonthDropdownElement 
{ 
    get { return new SelectElement(dropdown); } 
} 

Come si ottiene l'attuale IWebElement quando ne avete bisogno? Poiché gli strumenti SelectElementIWrappedElement, è possibile chiamare semplicemente la proprietà WrappedElement se è necessario accedere ai metodi e alle proprietà dell'elemento fornito dall'interfaccia IWebElement.

Le versioni recenti dei binding .NET hanno ristrutturato il PageFactory per essere più estensibile.Per aggiungere il "wait built-in" che si desidera, è possibile effettuare le seguenti operazioni:

// Assumes you have a page object of type MyPage. 
// Note the default timeout for RetryingElementLocator is 
// 5 seconds, if unspecified. 
// The generic version of this code looks like this: 
// MyPage page = PageFactory.InitElements<MyPage>(new RetryingElementLocator(driver), TimeSpan.FromSeconds(10)); 
MyPage page = new MyPage(); 
PageFactory.InitElements(page, new RetryingElementLocator(driver, TimeSpan.FromSeconds(10))); 

Inoltre, se si realmente necessario personalizzare come funzionano le cose, siete sempre i benvenuti a implementare IPageObjectMemberDecorator, che permette personalizzare completamente il modo in cui gli attributi vengono enumerati e i valori impostati sulle proprietà o sui campi decorati con tali attributi. Uno degli overload (non generici) di PageFactory.InitElements prende un'istanza di un oggetto che implementa IPageObjectMemberDecorator.

Lascerò da parte che le implementazioni corrette del modello di oggetto Page come rigorosamente definite non dovrebbero esporre oggetti WebDriver al di fuori di ogni oggetto di pagina. Altrimenti, tutto quello che stai implementando è un "page wrapper", che è un approccio perfettamente valido, ma non quello che chiameremmo "oggetto di pagina".

+0

Sempre come la tua risposta. Qualcosa di nuovo da imparare. Tuttavia, qual è la logica dietro 'RetryingElementLocator'? In termini di pagine fortemente Ajax o Angolari, sarebbe utile individuare l'elemento? – Saifur

+0

La cosa strana è che, sebbene non avessi ispezionato il codice PageFactory per cercare un'attesa, non mi aspettavo che ce ne fosse uno, eppure, aspetta ancora che gli elementi vengano caricati. So che il rigoroso modello oggetto pagina non espone elementi. Sono esposti solo ora perché sono abbastanza presto nel processo di creazione del POM e lo trovo temporaneamente conveniente. – MartinRosenberg

+2

Grazie! Il 'RetryingElementLocator' mi ha fatto risparmiare dal dover racchiudere' SelectElement'. Speravo che ci fosse un modo per ottenere qualcosa di più pulito (come 'FindsBy' direttamente su un' SelectElement'), ma non mi sorprende che non ci sia. Non che ne abbia necessariamente bisogno in questo momento, ma avresti ulteriori informazioni sull'uso di 'IPageObjectMemberDecorator'? Anche Google ha solo 10 risultati per questo. – MartinRosenberg

Problemi correlati