LoggerFactory 简介
单元测试常用日志打印工具LoggerFactory。
LoggerFactory
是 JUnit 平台中的一个类,用于创建Logger
实例。它被设计用于提供日志记录功能,使得 JUnit 在执行测试时能够记录信息、警告、错误等。
LoggerFactory
的主要目的是为 JUnit 提供一个集中式的日志记录机制,允许在测试过程中记录重要的信息,同时保持与不同日志实现的灵活性。这样做可以增强调试能力和监控测试执行过程中的重要事件。
package org.junit.platform.commons.logging;
/**
* Factory for the {@link Logger} facade for JUL.
*
* @since 1.0
*/
@API(status = INTERNAL, since = "1.0")
public final class LoggerFactory {
private LoggerFactory() {
/* no-op */
}
private static final Set<LogRecordListener> listeners = ConcurrentHashMap.newKeySet();
private static final class DelegatingLogger implements Logger {}
}
创建 Logger 实例(示例)
LoggerFactory
的主要职责是根据传入的参数创建适当的Logger
实例。这个实例可以用于记录不同级别的日志,例如调试信息、错误信息等。
/**
* Get a {@link Logger} for the specified class.
*
* @param clazz the class for which to get the logger; never {@code null}
* @return the logger
*/
public static Logger getLogger(Class<?> clazz) {
// NOTE: we cannot use org.junit.platform.commons.util.Preconditions here
// since that would introduce a package cycle.
if (clazz == null) {
throw new JUnitException("Class must not be null");
}
return new DelegatingLogger(clazz.getName());
}
调用方式:
public class Test {
private Logger logger = LoggerFactory.getLogger(this.getClass());
}
使用原生的Logger有个问题就是,日志打印的时候传入的参数对象不合适,需要自己再封装,愿意如下,看Logger 接口的定义。
Logger 接口
package org.junit.platform.commons.logging;
/**
* The {@code Logger} API serves as a simple logging facade for
* {@code java.util.logging} (JUL).
*
* @since 1.0
*/
@API(status = INTERNAL, since = "1.0")
public interface Logger {
/**
* Log the provided {@code Throwable} and message from the provided
* {@code messageSupplier} at error level.
*
* <p>Maps to {@link java.util.logging.Level#SEVERE} in JUL.
*/
void error(Throwable throwable, Supplier<String> messageSupplier);
/**
* Log the provided {@code Throwable} and message from the provided
* {@code messageSupplier} at info level.
*
* <p>Maps to {@link java.util.logging.Level#INFO} in JUL.
*/
void info(Throwable throwable, Supplier<String> messageSupplier);
/**
* Log the provided {@code Throwable} and message from the provided
* {@code messageSupplier} at trace level.
*
* <p>Maps to {@link java.util.logging.Level#FINER} in JUL.
*/
void trace(Throwable throwable, Supplier<String> messageSupplier);
}
自定义Logger
因为库函数中的日志工具比较抽象,所以自定义Logger是很关键的。
一个保存数据时验证UI行为的功能。
import org.junit.platform.commons.logging.LoggerFactory;
/**
* 保存数据,验证是否弹窗的功能
*/
@RunWith(MockitoJUnitRunner.class)
public class TestSave {
private static final String TAG = "TestSave";
//内部自定义日志打印接口,没使用库的类就要自己定义接口
@Mock
private Logger logger; // Mocking the Logger class used for Log.d statements
@Mock
private SaveActivity activity;
// Instance of the class to test, initialised in setUp
private SaveActivityUnderTest saveActivityUnderTest;
//过程空值变量,类似flag的作用
// Helper mock boolean variables to control the test cases
private boolean mIsShowSaveDialog;
private boolean mIsApnListChanged;
@Before
public void setUp() {
saveActivityUnderTest = new SaveActivityUnderTest();
saveActivityUnderTest .logger = logger;
saveActivityUnderTest .activity = activity;
}
// Moved the class outside any other class scope to avoid issues with instantiation
static class SaveActivityUnderTest {
private Logger logger;
private SaveActivity activity;
public void onMenuSave() {
logger.info(TAG, "MENU_SAVE: mIsShowSaveDialog = " + mIsShowSaveDialog);
if (mIsShowSaveApnDialog) {
if (mIsListChanged) {
activity.showSaveDialog();
} else {
activity.finish();
}
} else {
if (activity.validateAndSaveData()) {
activity.finish();
}
}
}
}
//省略测试代码
//自定义接口,这是还没有实现的。
private interface Logger {
void d(String tag, String message);
void info(String tag, String message);
void error(String message);
void warn(String message);
}
private interface SaveActivity {
boolean validateAndSaveData();
void showSaveDialog();
void finish();
}
}
参考源码库定制适合自己类的log。
LoggerFactory 常量类
final class 不能被继承。
package com.demo.test.util;
import org.junit.platform.commons.JUnitException;
import java.util.logging.Level;
import java.util.logging.LogRecord;
public final class LoggerFactory {
private LoggerFactory() {
/* no-op */
}
public static Logger getLogger(Class<?> clazz) {
// NOTE: we cannot use org.junit.platform.commons.util.Preconditions here
// since that would introduce a package cycle.
if (clazz == null) {
throw new JUnitException("Class must not be null");
}
return new TestLogger(clazz.getName());
}
private static final class TestLogger implements Logger {
private static final String FQCN = TestLogger.class.getName();
private final String name;
private final java.util.logging.Logger julLogger;
TestLogger(String name) {
this.name = name;
this.julLogger = java.util.logging.Logger.getLogger(this.name);
}
@Override
public void error(String tag, String message) {
System.out.println(tag + ": " + message);
}
@Override
public void warn(String tag, String message) {
}
@Override
public void info(String message) {
log(Level.INFO, null, message);
}
@Override
public void info(Throwable throwable, String message) {
log(Level.INFO, throwable, message);
}
@Override
public void info(String tag, String message) {
System.out.println(tag + ": " + message);
}
@Override
public void config(String tag, String message) {
}
@Override
public void debug(String tag, String message) {
}
@Override
public void trace(String tag, String message) {
}
private void log(Level level, Throwable throwable, String msg) {
boolean loggable = this.julLogger.isLoggable(level);
if (loggable) {
LogRecord logRecord = createLogRecord(level, throwable, msg);
}
}
private LogRecord createLogRecord(Level level, Throwable throwable, String message) {
String sourceClassName = null;
String sourceMethodName = null;
boolean found = false;
for (StackTraceElement element : new Throwable().getStackTrace()) {
String className = element.getClassName();
if (FQCN.equals(className)) {
found = true;
}
else if (found) {
sourceClassName = className;
sourceMethodName = element.getMethodName();
break;
}
}
LogRecord logRecord = new LogRecord(level, message);
logRecord.setLoggerName(this.name);
logRecord.setThrown(throwable);
logRecord.setSourceClassName(sourceClassName);
logRecord.setSourceMethodName(sourceMethodName);
logRecord.setResourceBundleName(this.julLogger.getResourceBundleName());
logRecord.setResourceBundle(this.julLogger.getResourceBundle());
return logRecord;
}
}
}
Logger接口
package com.demo.test.util;
public interface Logger {
void error(String tag, String message);
void warn(String tag, String message);
void info(String message);
void info(Throwable throwable, String message);
void info(String tag, String message);
void config(String tag, String message);
void debug(String tag, String message);
void trace(String tag, String message);
}