It is making me crazy to see how many people are talking about Page Objects in what seems to be the least robust way possible. I will try to keep it civil, but here are several examples of what I consider a terrible way to model your UI for testing:
http://www.seleniumhq.org/docs/06_test_design_considerations.jsp#page-object-design-pattern
http://fluentbytes.com/maintainable-test-automation-for-winforms-using-codedui/ (this one makes me particularly sad as it's Marcel and he's kinda the name for testing in this space ~_~)
https://www.youtube.com/watch?list=PL6tu16kXT9PoUbSYNcLrMG8ox6UBbbsCv&v=UUxSUsUVg-U
In all of these cases, the page objects have methods like .SetUserName("MyUserName"), .ClickLoginLink(), .LoginUser(string username, string password).
How terribly inconsistent and un-maintainable. What if the user name input is not shown by default and there is a button that shows the user name box. I'll have to add methods for .IsUserNameBoxVisible(), .ClickButtonToShowUserNameBox(). Wowzer. As a tester who did not write the page object, I have no consistent expectation of what the page object can do or what the controls on the page can do or what I can assert about them.
Instead, the page object, which I'll now refer to as a Page Model, should expose the UserName input as a property which has methods for .IsVisible(), .Click(), .IsEnabled(), .SetText(string text). Now, instead of .SetUserName("MyUserName"), it would be .UserName.SetText("MyUserName"). At a glance you may say, so what? However, having the .UserName property gives me (for free) all of the common actions I may take. For instance,
interface ILoginPage : IPageModel
{
IReadWriteValuePageModel<string, ILoginPage> UserName {get;}
IValuablePageModel<string, ILoginPage> Password {get;}
IClickablePageModel<ILoginPage> ShowLogin {get;}
IClickablePageModel<IAccountPage> Login{get;}
}
// page starts with username hidden
Assert.IsTrue(UserName.IsHidden());
// click show login button
ShowLogin.Click();
// assert that the user name is now visible
Assert.IsTrue(UserName.IsVisible());
// assert that the login button is disabled (no username / password entered)
Assert.IsTrue(Login.IsNotActionable());
// enter user name
UserName.SetText("MyUserName");
// assert login button is still disabled (no password)
Assert.IsTrue(Login.IsNotActionable());
// enter a password
Password.SetText("MyPassword");
// the login button should be enabled
Assert.IsTrue(Login.IsActionable());
Login.Click();
compared to
interface ILoginPage
{
ILoginPage SetUserName(string userName);
bool IsUserNameVisible();
ILoginPage SetPassword(string password);
ILoginPage ClickShowUsernameButton();
bool IsLoginButtonEnabled();
IAccountPage ClickLoginButton();
}
If I want to check if the password is visible, then I have to add it as a method. If I want to check that the show username button disappears after clicking, I have to add a method. This violates the Open / Close principal and in general is annoying.
It is the responsibility of the tests themselves to create these higher level concepts like
public void LoginUser(string username, string password)
{
if(ShowLogin.IsVisible())
{
ShowLogin.Click();
}
UserName.SetText(username).Password.SetText(password).Login.Click();
}
Then, this can be reused in a test:
[TestMethod]
public void GivenLogin_WhenSaveFirstNameInProfile_ThenNameIsLoadedBack()
{
var accountPage = LoginUser("someUser", "somePass");
Assert.IsTrue(accountPage.IsVisible());
string myName = "Myname";
account.FirstName.SetText(myName).Save.Click();
Logoff();
accountPage = LoginUser("someUser", "somePass");
Assert.IsTrue(myName.Equals(accountPage.FirstName.Value));
}
No comments:
Post a Comment