Using a Base Test Case with a logger

Một phần của tài liệu Manning JUnit recipes practical methods for program (Trang 221 - 225)

Problem

You want to perform logging from within your test cases.

Background

JUnit automates evaluating assertions so that developers don’t waste time rou- tinely verifying the output of test methods. Logging from test cases should not be used as a way to verify that tests have passed. Let the JUnit framework handle that.

But there are several situations where logging messages or even test results to a file, console, or other device is useful in JUnit testing. For example:

■ Temporary debugging, which can be turned on or off by configuration

■ Auditing and storage of test results

■ Auditing of the test environment or data used during a test run

■ Tracking the progress of tests that take a long time to run

■ Trapping customized test results for import into a test management system You can always configure and instantiate a logger from within any test case as you would from any other Java class. But if you are going to write many tests (especially if you are in a team environment, sharing common test infrastructure such as base test classes), it is practical to write a base test case class that provides the logging configuration and instantiation, and then subclass the log-providing class as desired.

If you have a common base class that everyone uses for a particular subsystem or project, you can include the logging configuration as part of that class.

Recipe

Create a base test case class that extends junit.framework.TestCase and provides a configured logger to its subclasses.

You have several options for finding and using a logger:

■ Write your own logging mechanism

■ Use a logger written by someone on your staff

■ Use a logging library from a third party, such as Log4J, Avalon’s LogKit, Jakarta Commons Logging

■ Use the java.util.logging package in JDK 1.4

TE AM FL Y

Team-Fly®

191 Using a Base Test Case with a logger

The pattern for setting up a logger in a base test class is the same, regardless of which logger you choose:

■ Extend TestCase with a class named similarly to BaseTestCase, which might include other commonly used testing utilities (perhaps a JNDIlookup() util- ity, or some other custom logic about where to find, that finds test data).

■ Set up a default configuration for the logger and initialize it in the BaseTest- Case, and make the preconfigured logger accessible to subclasses through a protected variable, an accessor to retrieve the logger instance, or through inherited log methods.

■ Make your test cases extend BaseTestCase so they can use the logger as needed.

Listing 7.1 shows a BaseTestCase class that configures two logger instances, one for static contexts, such as static initializer blocks and suite() methods, and one for non-static contexts, such as setUp(), tearDown(), and test methods. Two sep- arate loggers for static and non-static might be overkill, but this allows the example to show two different approaches for setting up the logger. The example uses Apache Avalon’s LogKit (any version of 1.x can compile and run with this example). In terms of features and ease of use, LogKit is a full-featured, flexible logging kit somewhere in between JDK 1.4’s java.util.logging package and Jakarta’s premier logger, Log4J. You can read more about LogKit and download the library at avalon.apache.org/logkit/.

NOTE Avalon is a Java platform for component-oriented programming includ- ing a core framework, utilities, tools, components, and containers hosted at Apache.

package junit.cookbook.tests.reporting;

import junit.framework.TestCase;

import org.apache.log.Hierarchy;

import org.apache.log.LogTarget;

import org.apache.log.Logger;

import org.apache.log.Priority;

import org.apache.log.format.ExtendedPatternFormatter;

import org.apache.log.output.io.StreamTarget;

public class BaseTestCase extends TestCase { /**

* Sets the default log level for both embedded loggers.

Listing 7.1 BaseTestCase configured with Avalon LogKit

192 CHAPTER 7

Reporting JUnit results

* The default log level setting can be overridden via * -Dlog.level=... on the command line or with a * <sysproperty key="log.level" value="${value}"/>

* in an Ant <java/> task running this test.

* Valid values, in ascending order or severity, are * DEBUG, INFO, WARN, ERROR, FATAL_ERROR.

*/

protected static String logLevel = "INFO";

/**

* Embedded <b>staticLogger</b>. This reference is static * and should be used for messages logged from static * code, such as static initializers and TestCase.suite() * methods.

*/

protected static Logger staticLogger =

Hierarchy.getDefaultHierarchy().getLoggerFor("static.");

/**

* <b>logger</b> is not static and should be used * everywhere except in places where a statically * configured logger is necessary.

*/

protected Logger logger =

Hierarchy.getDefaultHierarchy().getLoggerFor("test.");

/** Logkit Logger output string format for * non-static <b>logger</b> */

protected String pattern =

"%{priority}: %{message} in %{method}\n %{throwable}";

/** Logkit Logger output string format for <b>staticLogger</b> */

protected static String staticPattern =

"%{priority}: %{message} in %{method}\n %{throwable}";

/**

* Logkit extended formatter class, provides method * and thread info. This one is for the non-static * <b>logger</b>

*/

protected ExtendedPatternFormatter formatter = new ExtendedPatternFormatter(pattern);

/**

* Logkit extended formatter class, provides method

* and thread info. This one is for the <b>staticLogger</b>

*/

protected static ExtendedPatternFormatter staticFormatter = new ExtendedPatternFormatter(staticPattern);

193 Using a Base Test Case with a logger

static {

setLogLevelFromSystemProperty();

// log everything to System.out target for now StreamTarget target = new StreamTarget(System.out, staticFormatter);

staticLogger.setLogTargets(new LogTarget[] { target });

Priority priority = Priority.getPriorityForName(logLevel);

staticLogger.setPriority(priority);

} public BaseTestCase() { setLogLevelFromSystemProperty();

// log everything to System.out target for now StreamTarget target = new StreamTarget(System.out, formatter);

logger.setLogTargets(new LogTarget[] { target });

Priority priority = Priority.getPriorityForName(logLevel);

logger.setPriority(priority);

} private static final void setLogLevelFromSystemProperty() { String log_level = System.getProperty("log.level");

if (null != log_level) { logLevel = log_level;

} } }

The most important thing about the code example in listing 7.1 is the general technique of embedding a shared logger instance in a base test case class, not the specifics of using any particular logging implementation.

Discussion

Loggers such as LogKit, Log4J, and the Java 1.4 logging API allow you to config- ure logging on a per-class or per-package basis, by log level or by named catego- ries. Such configurability is useful for enabling logging for a particular subsystem or class hierarchy and helping isolate log messages from a particular set of tests or type of log message.

The advantage to extending a BaseTestCase (for logging and other utilities it might offer) is that subclasses can access the logger with no extra work. The draw- back to any subclassing strategy is that it ties the subclasses to the parent class through inheritance. An alternative to subclassing is to write a logging utility class that configures and instantiates a shared logger, and then use that utility class from within your tests. This tack decouples your test case classes from a common

static initializer for configuring staticLogger

Constructor, where default log level is set and base logger is configured

194 CHAPTER 7

Reporting JUnit results

base class added just for logging. But it is so common in practice to evolve a use- ful, in-house Base Test Case of some kind, that it is a good recipe to have in your personal cookbook.

Related

Một phần của tài liệu Manning JUnit recipes practical methods for program (Trang 221 - 225)

Tải bản đầy đủ (PDF)

(753 trang)