Archive | May, 2013

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!