Archive | Uncategorized RSS feed for this section

I’m So Dizzy, Spinners Are Spinnin’

6 Apr

I write a lot of Selenium automation for a very dynamic and Ajax-y application.

Lucky me!

Pages load with some areas populating more quickly than others as endpoints are accessed in the background and the page must wait for the data to load. While this is happening, the areas in progress display ‘spinners’, swirling animated icons to visually indicate to the end user that we’re working as hard as we can to get the data they’re looking for.  

This is a fine and very helpful thing, but a fair pain in the butt to encounter when doing browser automation.  While writing a method to wait for these things to come and go is pretty straightforward, there can be complications.  In our product, we have large spinners and small, each with different class names, which can lead to some fugly coding to cope with them all.

With the olde and oft-maligned XPATH locators, there is an easier way!

I’m not going to get into the logic of how best to implement a wait strategy for these things.  There are many ways, but most will rely on the basic idea of 

1. Wait for spinner(s) to appear (in case the spinner itself takes a while to load)
2. Wait for the spinner(s) to disappear

What I did was to write a single method to wait for any and all spinners of any size or naming convention on a given page to stop.  The kewl thing is, no matter how many varieties there are, with XPATH, we can handle them all with a single locator as follows:

public final static By by_roundSpinnerIcon = By.xpath(

"//*[@class='fa-circle-o-notch fa-2x fa-spin'] | "
+ "//*[@class='spinner_container'] | "
+ "//*[@class='fa-spin fa-spin-fast'] | "
+ "//*[@class='spinny'] | "
+ "//*[@class='fa fa-circle-o-notch fa-4x fa-spin'] |" // many kinds of spinners

A personal quirk of my automation visible here is a fondness for using By objects to store locators, be they CSS, XPATH, link text or whatever. This makes it much easer to create helper methods to deal with dynamic content, like dependableClick(By), waitForElementExists(By) and so on. There are other and probably better ways to do this, but this is my current approach.

In any case, what’s great about this sort of xpath locator, is the ability to use an OR operator ‘|’ to consolidate different xpath locator strings into 1 single entity. Now, without any programming logic whatsoever, I can wait for and interact with an element that matches any 1 of those xpath strings.

Just today I ran into some tests that were failing suddenly, and I noticed that they were no longer heeding the waitForSpinner() method like they used to, and were trying to interact with elements of a page that was not fully loaded. Once I spotted this, I did a bit of investigation and found a new class name had been added to one of the spinners, which I was quickly able to create an xpath string for: "//*[@class='loading_spinner spin']".

I then added this 1 string to the others on the by_roundSpinnerIcon locator, which resulted in fixing 4 broken tests in one fell swoop! Here’s the new xpath locator:

public final static By by_roundSpinnerIcon = By.xpath(
"//*[@class='fa-circle-o-notch fa-2x fa-spin'] | "
+ "//*[@class='spinner_container'] | "
+ "//*[@class='fa-spin fa-spin-fast'] | "
+ "//*[@class='spinny'] | "
+ "//*[@class='fa fa-circle-o-notch fa-4x fa-spin'] |"
+ "//*[@class='loading_spinner spin']"); // many kinds of spinners

Of note, troubleshooting a spinner icon is not always easy, as they tend to fly by quickly before you can inspect them. Here’s one low tech way to do this without having to get into javascript, break points and so on:

1. Open your application to the area with the spinner and dynamic content in the Chrome browser
2. Open the Development Console and click to ‘Customize and control DevTools’ on the right
3. Go down to the ‘More tools’ menu and choose ‘Network conditions’
4. Throttle the network down to something low enough for your application to load, and then when the spinner itself loads, throttle down even further.
5. With the spinner available, right-click-Inspect it, and hopefully you’ll have enough time to at least grab a screen shot you can look at more closely afterwards!

Like I said, ‘lo tech’! It worked for me though and can be handy in a pinch to catch a fleeting element that you want to inspect.

Some (More) Simple Selenium Helper Methods

28 Mar

Are there parts of Selenium automation that scare you?  No? Well there are parts that scare me, and one of them is Advanced User Actions.  It’s very powerful and useful, but I never feel comfortable implementing User Actions without lots of Googling first to make sure I’m doing it right.

So I cheat, and look it up once and then put that dreadful code into a helper method so I never (?) have to look it up again!  If I later find a better way to do it, I can just update the helper method itself, and then all that newfound brilliance will trickle down to the test cases that use that method. I call it (drum roll please) moveToElementAndClickCss():

public void moveToElementAndClickCss(String cssString) {
	new WebDriverWait(driver, 20).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(cssString)));
	new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(By.cssSelector(cssString)));
	Actions builder = new Actions(driver);
	Action moveAndClick = builder.moveToElement(driver.findElement(By.cssSelector(cssString)))
	.click()
	.build();
	moveAndClick.perform();	
}

   

We pass a css element path as the argument and let the method code take care of moving to that element and clicking it.

I of course have another one called moveToElementAndClickXpath(), which you should be able to infer from the css version.  These methods are very simple, but again help to speed up test case design and make the resulting tests more maintainable and easier to read.

Some Simple Selenium Helper Methods

28 Mar

I just wanted to pass along some Selenium/WebDriver helper methods that I used a great deal in a recent project. This first series are my ‘tryBy’ helpers, which simply return a boolean value depending on whether or not the specified element exists. It’s handy for when you want to do some quick content checks using XPATH or CSS, but don’t want to have everything grind to a halt if the element in question is not found. These methods use a simple try/catch loop to bypass the exception and simply return a FALSE if the element is not there.

Here’s the code for tryByXpath():

	/**
	 * This method lets you test for the existence of an element.
	 * 
	 * @param xpath = xpath selector text
	 * @return TRUE if xpath element isDisplayed, false with no error if not
	 */
	public boolean tryByXpath(String xpath) {
		try {
			new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.xpath(xpath)));
			new WebDriverWait(driver, 10).until(ExpectedConditions.visibilityOfElementLocated(By.xpath(xpath)));
			return true;
		} catch (TimeoutException e) {
			report.report("Xpath element not found: " + xpath);
			return false;
		}
	}

And here’s the same idea for CSS, aptly named tryByCss():

	/**
	 * This method lets you test for the existence of an element.
	 * 
	 * @param css = css selector text
	 * @return TRUE if css element isDisplayed, false with no error if not
	 */
	public boolean tryByCss(String css) {
		try {
			new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(css)));
			new WebDriverWait(driver, 10).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(css)));
			return true;
		} catch (TimeoutException e) {
			report.report("Xpath element not found: " + css);
			return false;
		}
	}

And lastly, tryByLinkText:

	/**
	 * This method lets you test for the existence of an element.
	 * 
	 * @param text = link text selector text
	 * @return TRUE if link text element isDisplayed, false with no error if not
	 */
	public boolean tryByLinkText(String text) {
		try {
			return driver.findElement(By.linkText(text)).isDisplayed();
		} catch (NoSuchElementException e) {
			report.report("Xpath element not found: " + text);
			return false;
		}
	}

The pattern is simple and there’s nothing tricky going on here, but creating helpers like these can really help save you a lot of time when writing your tests, and also helps to make your test code much easier to read.

For a small test suite, you can put these helpers in the test class itself. In a larger suite with multiple test classes, put them in a parent or abstract class which will then make them available to all child classes in which you write your tests.

In use, typical scenarios will look a lot like this:

boolean closeButton = tryByXpath("//*[text()='close']");
if (!closeButton) { report.report("problem with closeButton"); }

In that example we’re just checking to see if the Close Button exists, using XPATH. If it’s not there, we report the problem and move on. If it’s there, we move on and can use the information at the end of the test by stringing all the booleans together:

return closeButton && assetIcon && (etc. etc.)

The method calling this test method could then fail the test case if any of the content is missing, or pass it if all the content is there. You could also use it directly as a part of a JUnit assertion or any other way you like.

Equality NOW!

6 Feb

Equality NOW!

When is one item definitely NOT equal to another, even though they are both exactly the same?

When you’re programming in JAVA of course!

I’ve run into this many times, but for some reason today I stared at and wrestled with this problem for an hour straight before the “Oh YEAH!” clicked in, so I’m writing this post to help drive the point home for myself.

And you too, hopefully!

It seemed so simple:

return (elementText == "An email has been sent!");

If they’re equal, return TRUE, if not, FALSE. No biggie. But this is all I saw on every run:


junit.framework.AssertionFailedError: Fail report was submitted

Eh? Whazzat? The ensuing stack trace didn’t even mention the class the code was actually running from, so I looked in all the dustiest, darkest corners of my code (and the surrounding project) trying to find what strange, esoteric programming conundrum or race condition was causing this error.

It was staring me in the face the whole time, and I literally had to do that…STARE…at it for while before it hit me! This is probably the most common Java programming error of all time, and I’m ashamed to say it slapped me a good one this morning. Here’s what that code should look like:

return (elementText.contentEquals(
            "An email has been sent!"));

Nothing strange, nothing esoteric, nothing I haven’t encountered before. We use “==” for numbers and “.contentEquals()” for text. Easy! Grrrrr.

My only excuse is that with the intensity of this current project and deadlines looming, I immediately assumed that the problem was something dreadful I hadn’t encountered before. I was in the habit of seeing the offending method in the stack trace and this time there was nothing and, well, I PANICKED!

There it is, my confession. Kids don’t try this at home and end up a clown like me. I am now going to the (white?) board to write, “Not all equivalencies are ==” 100 times.

My Simple Java Logger

16 May

Captain's log...

While working on a recent project using parameterized tests against 1000+ lines of test data, it quickly became clear that some sort of logging was in order to get all the errors in one place in one file. I knew there were solutions such as log4j readily available, but I felt that the setup for that might be a bit too complex and would slow down an effort that was otherwise going quite smoothly.

So I searched the web to get an idea of the best approach for simply writing data to a file. I was able to put that together fairly quickly but then ran into the issue of how best to implement it, either as a method within the test class itself or as a separate class which would then be called by the test class (and any other future test classes which might want to use it). The first option was clearly the easiest and the second was clearly the most sensible.

So I went for both, of course!

While implementing the basic logging as method in my test class, I ran into questions as to how best to initialize the output file, how to append new data to the old without trouncing the file each time, adding a nice separator between each test case and so on. I finally got it sorted out to my liking after which I made the move to putting it into a class of it’s own. Fully acknowledging plenty of help from ‘anonymous donors’ on the Web, here are the results:

import java.io.*;

public class Logger {
    public static String logFileName;

    public Logger (String filename) throws IOException {
        this.logFileName = filename;
    }

    public void initLog(String fileName) throws IOException {
        // initialize log file and delete existing
        File logFile = new File(fileName);
        logFile.delete();
    }

    public void logThis(String message) {

        BufferedWriter bw = null;

        try {
            bw = new BufferedWriter(new FileWriter(this.logFileName, true));
            bw.write(message);
            bw.newLine();
            bw.flush();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {                       // always close the file
            if (bw != null) try {
                bw.close();
            } catch (IOException ioe2) {
                // just ignore it
            }
        } // end try/catch/finally

    } // end method()

    public void logSeparator() {
        String separator = "------------------------------\n";
        logThis(separator);
    }

}

To implement the code from another file, you first need to instantiate an object and (if you like) define a variable to hold the log file name:

public static Logger logger;
public static String logFileName = "ExampleLoginsParameterizedTest.log";

Then, in my JUnit @BeforeClass method, I fully instantiate and initialize the logger:

@BeforeClass
public static void startLogger() throws IOException {
    // instantiate logger
    logger = new Logger(logFileName);
    // initialize new log
    logger.initLog(logFileName);
}

The first line hits the Logger constructor with the logFileName text variable, while the second calls the ‘initLog’ method with the same variable name to clobber the old log if it exists. The old log is clobbered in a way that would make Rube Goldberg beam with pride. The Logger class creates a file with the desired name and then deletes it. If a file with the same name exists, then that file ends up deleted. If it doesn’t, then a file is quickly created and deleted, no harm no foul. There’s probably a much better way to do this, but that’s what I came up with on short notice.

The actual act of logging looks like this:

// log test info and separator to log.txt file AND log to console
message = "Testing: (" + this.UserType + ") " + this.Username
        + "/" + this.Password + ", " + this.SchoolID + "\n";
System.out.println(message);
logger.logSeparator();
logger.logThis(message);

That code appears in my JUnit @Before method, which runs before each test. In this case it puts a separator and then writes some basic info about the test in question.

In another area of the test class, I use the logger to record pertinent details if a try/catch loop fails:

try {
        wait.until(ExpectedConditions.presenceOfElementLocated(By.linkText("Log out")));
    }
    catch (TimeoutException timeout) {
        // log error to log.txt file AND log to console
        message = ">>>ERROR:  Login for " + this.Username + "/" + this.Password
                + " appears to be unsuccessful, 'Log out' link never appeared.\n"
                + "Error text: \n" + timeout + "\n";
        System.out.println(message);
        logger.logThis(message);
    }

That’s it. A poor man’s logger that does what I need and very little more!

Parametrized Testing With WebDriver and JUnit

12 May

Parametrized testing, with WebDriver and JUnit (4 in this case) is not all that hard, provided you have a few magical incantations, tremendous computing horsepower and endless coffers of disposable funds.

No, really, it’s not that hard! If I can do it, anyone can. The incantations are all you really need (although ample funding, a sturdy laptop and plenty of tasty snacks is always nice).

The first one is this:

@RunWith(Parameterized.class)

This magical JUnit directive goes above the initial class declaration in your code.

The next one is this:

public LoginsParameterizedTest(String uname, String pword, String schoolID, String userType) {
        this.Username = uname;
        this.Password = pword;
        this.SchoolID = schoolID;
        this.UserType = userType;
    }

That’s a constructor for my particular class file. Yours will be different. Just make sure to replace ‘LoginsParameterizedTest’ with whatever the name of your class is, and pass along whatever parameters will be important for your testing. All this constructor does is assign the passed in variables to the actual class variables (each of which should be declared as private beforehand outside of the constructor method).

Now we get to the fun part, the data we’ll be passing to each iteration of the test.

@Parameterized.Parameters
    public static Collection data() {
        return Arrays.asList(
                new Object[][]{
                        { "username1", "password1", "demo", "Instructor" },
                        { "username2", "password2", "demo", "Student" },
                        { "username3", "password3", "demo", "Student" },
                        { "username4", "password4", "demo", "Student" }
                }
        );
    }

You can copy/paste the framework of the code and then tailor your data to match your own test. Just 4 lines here, but there could be as many as you wish. Each element of a given array should map directly to the parameters passed to the constructor. Easy, no?

Finally, for the actual @Test’s, go on as usual, but replace specific parameter data with the parameter variable names you defined earlier in the class, prefixed with ‘this.’ like so:

assertTrue(driver.findElement(By.cssSelector("BODY")).getText().contains(this.SchoolID));    
assertTrue(driver.findElement(By.cssSelector("BODY")).getText().contains(this.UserType));   
assertTrue(driver.findElement(By.cssSelector("BODY")).getText().contains(this.Username));

Those are not the most robust assertions of course, but that’s pretty much all there is to it for the basic approach. For each line of data passed to the constructor, you’ll get a fresh junit test result as output. This can really revolutionize your tests and save you from writing mountains of fragile code. Try taking a simple project you already have under way and parametrizing it.

Next up will be parametrizing values from a spreadsheet or file, instead of from within the code itself.

Huge thanks to Alan Richardson and his absolutely beyond superb WebDriver course on Udemy. Check him out, sign up and let the learning really begin!

Nailing (finally) the Navigation Page Object

24 Apr

READERS TAKE NOTE: I wrote this a while back and then found a LOT of mistakes and better ways of doing things, but never came back to update the post. Oh the things I learned from this NavBar class! The basic idea here still applies, but take the details with a grain (or four) of salt.

-Kent

I’ll add details later, but I wanted to share a page object class I wrote to allow for easy navigation for a suite of Selenium tests we’re working on.

The class is simply called ‘NavBar’ and looks like this:


package com.Vericode;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Action;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.FindBy;

public class NavBar {
	
	static String contentMenuCSS = "div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2122";
	static String tagMenuCSS = "div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2132";

	public void contentDashboardButtonClick(WebDriver driver) {
		contentMenuHover(driver);
		String css = "html.js body.html div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2121 a";
		driver.findElement(By.cssSelector(css)).click();
	}
	
	public void addContentButtonClick(WebDriver driver) {
		// THIS FAILS I DON'T KNOW WHY
		// ** troubleshoot later **
		//contentMenuHover(driver);
		//String css = "html.js body.html div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2122";
		//driver.findElement(By.cssSelector(css)).click();
		// ** in the meantime, just get the url **
		driver.get(driver.getCurrentUrl().replace("/content-dashboard", "") + "/node/add");
	}
	
	public void addArticleButtonClick(WebDriver driver) {
		contentMenuHover(driver);
		String css = "div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2122 ul li.menu-2124 a";
		driver.findElement(By.cssSelector(css)).click();
	}
	
	public void addBlogButtonClick(WebDriver driver) {
		contentMenuHover(driver);
		String css = "div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2122 ul li.menu-2125 a";
		driver.findElement(By.cssSelector(css)).click();
	}
	
	public void addFilmButtonClick(WebDriver driver) {
		contentMenuHover(driver);
		String css = "div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2122 ul li.menu-2126 a";
		driver.findElement(By.cssSelector(css)).click();
	}
	
	public void addInfographicButtonClick(WebDriver driver) {
		contentMenuHover(driver);
		String css = "div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2122 ul li.menu-2127 a";
		driver.findElement(By.cssSelector(css)).click();
	}
	
	public void addPollButtonClick(WebDriver driver) {
		contentMenuHover(driver);
		String css = "div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2122 ul li.menu-2128 a";
		driver.findElement(By.cssSelector(css)).click();
	}
	
	public void addSpecialFeatureButtonClick(WebDriver driver) {
		contentMenuHover(driver);
		String css = "div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2122 ul li.menu-2129 a";
		driver.findElement(By.cssSelector(css)).click();
	}
	
	public void carouselControlButtonClick(WebDriver driver) {
		String css="li.menu-2130 a";
		driver.findElement(By.cssSelector(css)).click();
	}
	
	public void gridControlButtonClick(WebDriver driver) {
		driver.findElement(By.cssSelector("li.menu-2131 a")).click();
	}
	
	public void tagsAndAuthorsButtonClick(WebDriver driver) {
		String css = "div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2132 a";
		driver.findElement(By.cssSelector(css)).click();
	}
	
	public void addAuthorButtonClick(WebDriver driver) {
		tagMenuHover(driver);
		String css = "div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2133 a";
		driver.findElement(By.cssSelector(css)).click();
	}

	public void addTagButtonClick(WebDriver driver) {
		tagMenuHover(driver);
		String css = "div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2135 a";
		driver.findElement(By.cssSelector(css)).click();
	}
	
	public void viewAuthorsButtonClick(WebDriver driver) {
		tagMenuHover(driver);
		String css = "div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2134 a";
		driver.findElement(By.cssSelector(css)).click();
	}
	
	public void viewTagsButtonClick(WebDriver driver) {
		tagMenuHover(driver);
		String css = "div#admin-toolbar ul#nice-menu-main.nice-menu li.menu-2136 a";
		driver.findElement(By.cssSelector(css)).click();
	}
	
	public void logoutButtonClick(WebDriver driver) {
		driver.findElement(By.cssSelector("li.menu-2123 a")).click();
	}
	
	//
	// Some helper methods for this class
	//
	
	public static void contentMenuHover(WebDriver driver) {
		WebElement menuHoverLink = driver.findElement(By.cssSelector(contentMenuCSS));
		new Actions(driver).moveToElement(menuHoverLink).perform();
		waiting(1);  // pathetic, I know
	}
	
	public static void tagMenuHover(WebDriver driver) {
		WebElement menuHoverLink = driver.findElement(By.cssSelector(tagMenuCSS));
		new Actions(driver).moveToElement(menuHoverLink).perform();
		waiting(1);  // pathetic, I know
	}
	
	public static void waiting (int n){
        long t0, t1;
        t0 =  System.currentTimeMillis();
        do{
            t1 = System.currentTimeMillis();
        }
        while ((t1 - t0) < (n * 1000));
    }
}

You’ll see near the beginning there where there was one case in which a simple click wasn’t doing the job. I pulled some hair on that one to no avail, so have decided to brute force it with a simple ‘get’ and a bit of base URL massaging. We’ll be using this class across multiple sites for which the URL’s will each be a little bit different depending on locality, so I’m hoping that this won’t break in those cases (testing this soon!)

And then here’s the test from the test driver class I wrote to test it out. All’s green that ends green! The print statements were for troubleshooting early on and will be removed for clarity. This test just navigates to a page using the NavBar page object, and then verifies the correct title.

	@Test
	public void testMenuNavigation() {
		loginAsEditor();
		assertThat(driver.getTitle(), is("Editor Dashboard | US English"));
		
		System.out.println("addContentButton");
		navBar.addContentButtonClick(driver);
		assertThat(driver.getTitle(), is("Add content | US English"));
		
		System.out.println("dashboardButton");
		navBar.contentDashboardButtonClick(driver);
		assertThat(driver.getTitle(), is("Editor Dashboard | US English"));
		
		System.out.println("addArticleButton");
		navBar.addArticleButtonClick(driver);
		assertThat(driver.getTitle(), is("Create Article | US English"));
		
		System.out.println("addBlogButton");
		navBar.addBlogButtonClick(driver);
		assertThat(driver.getTitle(), is("Create Blog | US English"));
		
		System.out.println("addFilmButton");
		navBar.addFilmButtonClick(driver);
		assertThat(driver.getTitle(), is("Create Film | US English"));
		
		System.out.println("addGraphicsButton");
		navBar.addInfographicButtonClick(driver);
		assertThat(driver.getTitle(), is("Create Infographic | US English"));
		
		System.out.println("addPollButton");
		navBar.addPollButtonClick(driver);
		assertThat(driver.getTitle(), is("Create Poll | US English"));
		
		System.out.println("addFeatureButton");
		navBar.addSpecialFeatureButtonClick(driver);
		assertThat(driver.getTitle(), is("Create Special Features | US English"));
		
		System.out.println("carouselControlButton");
		navBar.carouselControlButtonClick(driver);
		assertThat(driver.getTitle(), is(not("Carousel Control | US English"))); // fail when fixed?
		
		System.out.println("gridControlButton");
		navBar.gridControlButtonClick(driver);
		assertThat(driver.getTitle(), is("Grid Manager | US English"));
		
		System.out.println("tagsAndAuthorsButton");
		navBar.tagsAndAuthorsButtonClick(driver);
		assertThat(driver.getTitle(), is("Taxonomy | US English"));
		
		System.out.println("addAuthorButton");
		navBar.addAuthorButtonClick(driver);
		assertThat(driver.getTitle(), is("Tags & Authors | US English"));
		
		System.out.println("addTagButton");
		navBar.addTagButtonClick(driver);
		assertThat(driver.getTitle(), is("Tags & Authors | US English"));
		
		System.out.println("addViewAuthorsButton");
		navBar.viewAuthorsButtonClick(driver);
		assertThat(driver.getTitle(), is("View Authors | US English"));
		
		System.out.println("addViewTagsButton");
		navBar.viewTagsButtonClick(driver);
		assertThat(driver.getTitle(), is("View Tags | US English"));
		
		System.out.println("logoutButton");
		navBar.logoutButtonClick(driver);
		assertThat(driver.getTitle(), is("User Account | US English"));	
	}

This should help us a great deal in writing our tests, as some of the navigation items are wrapped in javascript and hidden. Now that complexity is hidden away in the NavBar class, which will be much easier to change and troubleshoot if there are changes to the application code down the road.

All we need to do is instantiate a NavBar object once in our code:

static NavBar navBar = new NavBar();

…and then we can traverse the menu with simple, easy-to-read one-line statements any time we need:

navBar.viewTagsButtonClick(driver);

Also nice is the fact that in the IntelliJ or Eclipse IDE’s, the context help menu’s will show all of these navigation options in a nice list without our having to memorize them.

19 Apr

One of the most satisfying aspects of the Selenium Webdriver course I’ve been taking on Udemy has been communicating with it’s creator, Alan Richardson. Alan’s delivery on his videos is upbeat, professional and well produced, and his enthusiasm for testing with Selenium is infections. Alan is also great about responding to questions via email and always has a generous and thoughtful answer to even the most mundane of questions.

What a happy surprise it was to find out a while back that he had referenced a comment of mine in one of his blog postings! I was having trouble getting the proper focus when exercising a simple test using the Webdriver user interactions and did a bit of research which led to a simple and unexpected workaround.

You can see Alan’s post here. What a thrill to be officially recognized by the Best Dressed Zombie Tester around!

Thanks, Alan!

; )

Rails E-Z Authentication 1-2-3

30 Mar

I’ve been long considering a way to put some restrictions on the Target Cancer site to keep casual onlookers of the Home page from mucking about and stepping on things Backstage.

For those who aren’t already in the know, the Target Cancer site is an experimental site I’m making for a friend who is doing a benefit concert with a bunch of musician friends from the Pharma industry. I wanted to create a site where they could advertise the event, provide directions, performer bios and so on, and then also have a back-stage area where the performers could collaborate via a Forum page and an editable list of Songs with lyrics, chords and links to youtube videos (most if not all of the songs are covers in this case).

I was hesitant to implement a full-blown user authentication system on the site, however, because high security isn’t the issue here, and as exciting as it might be to implement, I don’t want all sorts of user registration rigamarole getting in the way of helping these busy people to get together (virtually speaking) and collaborate on their collective goal of learning a bunch of songs, discussing them and keeping track of who’s playing which song.

So with a bit of searching I found my first clue. Someone pointed the way to a Railscast video which showed (almost) exactly what I was looking for:

http_basic_authenticate_with :name => "frodo", :password => "thering"

Putting the line above in the controller who’s views you wish to hide from casual view works great, and there are way you can hide the username and password from the code as well if you wish, such as putting it in an external config file.

But I had a bunch of controllers and views I wanted to hide, and putting that line at the top of each controller would mean (I think, I didn’t actually try it to be honest) back-stage users having to enter a password several times as they traversed the different controllers for Songs, Posts, Users and so on.

So I searched a bit more and found exactly what I was looking for. Here’s how it’s done.

First, create a new ‘Admin’ controller. Call it what you like, but ‘Admin’ is as good a name as any!

rails g controller Admin

Now edit that controller to look like the following, putting in your own values for the username and password:

class AdminController < ApplicationController
  before_filter :authenticate

  def authenticate
    authenticate_or_request_with_http_basic
        do |username, password|
      username == 'admin' && password == 'password'
    end
  end
end

Great. But what about authenticating all the other controllers? Here’s how:

For each controller you want protected change the top line to inherit from AdminController instead of ApplicationController.

For example:

class PostsController < AdminController
  layout 'backstage'
.
.
.

It’s that simple!

AdminController already inherits from ApplicationController for them, so all they’re getting is a little extra, specifically the before_filter and the authenticate method. By inheriting this code, the authentication applies to them all without the user having to manually type a log in for each one.

You can go backstage at the Target Cancer site if you wish with the following login:

performer / pass4backstage

Just try not to step on anything! (If you do, just leave some beer in the fridge by the pool table and all is forgiven.)

;)

Rails 3 Many to Many

28 Mar

Working on the Target Cancer site has given me a pretty good grip on creating 1-to-many relationships in rails. For each Song there are many comments and likewise for each Post.

I then wanted to add some functionality to allow for Users to be entered and for those users to be able to choose Songs in which to Perform. Each user could perform on many songs and each song would have several users (let’s call them Players) performing them.

That much I was able to visualize clearly, but actually putting that into Rails terms was fairly intimidating, and after a few sketches and some fits and starts, I finally had to start doing some hard research as to how, exactly, this would be implemented.

So I created a simple Rails app in which to model this relationship exclusively. I also found some good tutorials to get the models and migrations sorted out, my favorite of which can be seen here.

In order to give the Songs and Player models access to each other in a many-to-many fashion I chose to go the route (no pun intended) of has_many :through which, if you’ve read a bit on Rails relationships, you’ll know is a more flexible alternative to the method using has_and_belongs_to_many. The ‘has_many’ route involves building a 3rd model to mediate between the two. Instead of just a simple table to track the foreign keys, the ‘has_many’ approach lets you add columns to the table in addition to the foreign keys, which can be very useful for tracking information that relates to the relationship, if that doesn’t sound too strange. I put a ‘role’ column in there, and called the new model ‘Performances’, the purpose of ‘role’ being to store the role a given Player would play in the performance. So Player ‘John Doe‘ might have a Performance role of ‘bass and vocals’, While ‘Billy Zoom‘ may have the role of ‘blazing rhythm and lead guitar’.

So the tutorials got the ball rolling, but then left me still pretty clueless as to how to manipulate these objects usefully in my own application. Testing in the rails console, I found that I could quite easily add a Player object (selected using Player.find(:id)) to a song (selected in the same fashion) simply by doing the following:

song << player

I thought that was pretty neat, but then found it difficult to update the role which the give Player was playing in that song. Using the method above, the role remained ‘nil’.

After quite a bit of fumbling, I finally resolved to contact the Boston Ruby Group for assistance. My hat is off because the response time and detail with these folks is PHENOMENAL! One kind user clued me into the following:

song.performances.create(player: player, role:"guitar")

All in one line, save to the database and everything! This began to shed light on another problem I was having, which was getting Song and their associated Players (and roles) to display nicely on a view. I was trying the following code and getting errors:

<% @songs.each do |song| %>

      <%= song.title %><br/>
        <% for i in 1..song.players.length %>
          <%= song.players[i].name %>, 
          <%= song.performances[i].role %>
        <% end %>
      <% end %>

This was resulting in a “undefined method `name’ for nil:NilClass” error, and I knew in any case that my approach was wrong and there had to be an easier way. Sure enough there was, and after taking some time to digest the response I got from the BRG folks, I came up with the following:

<% @songs.each do |song| %>
      <u><%= song.title %></u><br />
      <% song.performances.each do |performance| %>
          <%= performance.player.name %> -
          <%= performance.role %><br />
      <% end %><br />
<% end %>

This spits out everything I wanted as neatly as can be:

Songs

Miserlou
Kent – guitar
John Doe – bass guitar

Louie Louie
Kent – tambourine
Sally – bazooka and lead vocal
John – drums and percussion

Love Me Do
John Doe – bass guitar and lead vocal

Happy me! Big thanks to the BRB for wisdom and guidance on this (and other soon-to-be-posted) issues. I’m amazed at how easily (once you know how) Rails lets you access all the columns in the join between the two tables, from either side (or the middle for that matter). This is still sinking in, but is a powerful lesson for me.

I don’t have the code for this on GitHub, as it’s just a sketch, but for the curious I’m putting the Model code here:


class Song < ActiveRecord::Base
  attr_accessible :title
  has_many :performances
  has_many :players, :through => :performances
end

class Player < ActiveRecord::Base
  attr_accessible :instrument, :name
  has_many :performances
  has_many :songs, :through => :performances
end

class Performance < ActiveRecord::Base
  # attr_accessible :title, :body
  attr_accessible :player, :song, :role
  belongs_to :player
  belongs_to :song
end

The schema code (Rails puts this in a single file very conveniently) is here:

ActiveRecord::Schema.define(:version => 20130328181909) do

  create_table "dogs", :force => true do |t|
    t.string   "name"
    t.integer  "age"
    t.string   "sex"
    t.string   "breed"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

  create_table "performances", :force => true do |t|
    t.text     "role"
    t.integer  "player_id"
    t.integer  "song_id"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

  create_table "players", :force => true do |t|
    t.string   "name"
    t.string   "instrument"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

  create_table "songs", :force => true do |t|
    t.string   "title"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

end

Never mind that ‘Dogs’ table behind the curtain. That was for troubleshooting another issue which will appear in another post!