在android studio junit测试中记录消息
在Android Studio中运行JUnit(方法)测试时,有没有打印Logcat(Log.i,Log.d)消息的方法 我可以看到System.out.print消息,但没有logcat打印输出 在runconfiguration(androidstudio的GUI窗口)中,有用于Android测试的logcat选项,但不用于JUnit测试在android studio junit测试中记录消息,android,android-studio,junit,logcat,Android,Android Studio,Junit,Logcat,在Android Studio中运行JUnit(方法)测试时,有没有打印Logcat(Log.i,Log.d)消息的方法 我可以看到System.out.print消息,但没有logcat打印输出 在runconfiguration(androidstudio的GUI窗口)中,有用于Android测试的logcat选项,但不用于JUnit测试 这有可能吗?谢谢你的提示 我一直在寻找同样的东西,却没有找到一个直接的答案。我知道这个问题已经有一年多的历史了,但仍然有一个好的答案供将来参考 在本地JV
这有可能吗?谢谢你的提示 我一直在寻找同样的东西,却没有找到一个直接的答案。我知道这个问题已经有一年多的历史了,但仍然有一个好的答案供将来参考 在本地JVM上运行单元测试时,android.util.Log类直接登录到Logcat,android.util.Log的实现不可用。在单元测试中尝试使用Log类时,您将收到一个错误,因为“用于运行单元测试的android.jar文件不包含任何实际代码(这些API仅由设备上的android系统映像提供)。”
因此,如果您真的想使用android.util.Log,您需要在本地模拟它,并利用System.out.print打印到控制台。首先,将PowerMockito添加到项目中。如果您使用的是Gradle,则只需添加以下依赖项:
testCompile 'junit:junit:4.12'
testCompile 'org.powermock:powermock:1.6.5'
testCompile 'org.powermock:powermock-module-junit4:1.6.5'
testCompile 'org.powermock:powermock-api-mockito:1.6.5'
接下来,我使用Steve的答案来了解如何使用Mockito返回传递到模拟对象中的参数
结果是:
import android.util.Log;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.Matchers.anyString;
import static org.powermock.api.mockito.PowerMockito.when;
@RunWith(PowerMockRunner.class)
@PrepareForTest({Log.class})
public class SomeUnitTest {
@Test
public void testSomething() {
System.out.println("Running test");
PowerMockito.mockStatic(Log.class);
// Log warnings to the console
when(Log.w(anyString(), anyString())).thenAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
if (args.length > 1) { //cause I'm paranoid
System.out.println("Tag:" + args[0] + " Msg: " + args[1]);
}
return null;
}
});
Log.w("My Tag", "This is a warning");
}
}
导入android.util.Log;
导入org.junit.Test;
导入org.junit.runner.RunWith;
导入org.mockito.invocation.invocationMock;
导入org.mockito.stubing.Answer;
导入org.powermock.api.mockito.PowerMockito;
导入org.powermock.core.classloader.annotations.PrepareForTest;
导入org.powermock.modules.junit4.PowerMockRunner;
导入静态org.mockito.Matchers.anyString;
导入静态org.powermock.api.mockito.PowerMockito.when;
@RunWith(PowerMockRunner.class)
@PrepareForTest({Log.class})
公共类单元测试{
@试验
公共空间{
System.out.println(“运行测试”);
mockStatic(Log.class);
//将警告记录到控制台
当(Log.w(anyString(),anyString())。然后回答(new Answer()){
@凌驾
公共Void应答(invocationmock调用)抛出可丢弃的{
对象[]args=invocation.getArguments();
如果(args.length>1){//因为我有妄想症
System.out.println(“标记:+args[0]+”消息:+args[1]);
}
返回null;
}
});
w(“我的标签”,“这是一个警告”);
}
}
希望这对别人有帮助 在单元测试中不会看到Logcat输出,因为Logcat是Android功能-JUnit测试只能使用标准Java,因此Android功能无法工作 在单元测试中,您可以做的是将“测试加倍”注入到被测试的组件中。但是
Log.x
调用是静态的,因此您无法覆盖它们(如果不解析为例如PowerMock,您应该不惜一切代价避免)
因此,第一步是引入一个非静态类,它将充当Log.x
调用的代理:
/**
* This class is a non-static logger
*/
public class Logger {
public void e(String tag, String message) {
Log.e(tag, message);
}
public void w(String tag, String message) {
Log.w(tag, message);
}
public void v(String tag, String message) {
Log.v(tag, message);
}
public void d(String tag, String message) {
Log.d(tag, message);
}
}
现在在您有Log.x
调用的每个地方使用该类
第二步是编写重定向到标准输出的Logger
的测试双重实现:
public class UnitTestLogger extends Logger{
@Override
public void e(String tag, String message) {
System.out.println("E " + tag + ": " + message);
}
// similar for other methods
}
最后一步是在单元测试中插入UnitTestLogger
而不是Logger
:
@RunWith(MockitoJUnitRunner.class)
public class SomeClassTest {
private Logger mLogger = new UnitTestLogger();
private SomeClass SUT;
@Before
public void setup() throws Exception {
SUT = new SomeClass(/* other dependencies here */ mLogger);
}
}
如果您想严格遵守OOP概念,可以为Logger
和UnitTestLogger
提取公共接口
也就是说,我从来没有遇到过在单元测试中调查Log.x
调用的需要。我猜你也不需要它。您可以在调试模式下运行单元测试,并在调试器中逐行检查代码,这比尝试调查logcat输出要快得多
一般建议:
如果您正在测试的代码包含Log.x
静态调用,并且您的单元测试没有崩溃,那么您就有问题了
我猜要么所有的测试都是用Robolectric
运行的,要么build.gradle:unitTests.returnDefaultValues=true
中有这样的语句
如果您使用
Robolectric
运行所有测试,那么它就是无效的,但是如果所有Android调用都返回默认值,那么您的测试套件就不可靠。我建议您在继续之前解决此问题,因为它将以某种方式在未来对您造成伤害。我将采用@Vasily的方法,但需要做一点小小的更改。我们实际上可以使用configured with在测试输出屏幕上记录消息,而不是使用System.out.println
进行日志记录
这可以通过以下列出的简单步骤实现
第1步:
定义自己的日志级别
public enum LogLevel {
VERBOSE,
DEBUG,
INFO,
WARNING,
ERROR,
SILENT
}
第二步:
定义记录器抽象
public abstract class Logger {
....
public abstract void debug(String tag, String message);
public abstract void error(String tag, String message);
....
}
第三步:
基于java.util.Logging的记录器实现
public class JavaLogger extends Logger {
private java.util.logging.Logger mLogger;
public JavaLogger(LogLevel logLevel, String name) {
mLogger = java.util.logging.Logger.getLogger(name);
ConsoleHandler handler = new ConsoleHandler();
Level level = mapLogLevel(logLevel);
handler.setLevel(level);
mLogger.addHandler(handler);
mLogger.setLevel(level);
}
@Override
public void debug(String tag, String message) {
if (isLoggable(LogLevel.DEBUG)) {
mLogger.finer(message);
}
}
@Override
public void error(String tag, String message) {
if (isLoggable(LogLevel.ERROR)) {
mLogger.severe(message);
}
}
....
private Level mapLogLevel(LogLevel logLevel) {
Level level = Level.OFF;
switch (logLevel) {
case INFO:
level = Level.INFO;
break;
case WARNING:
level = Level.WARNING;
break;
case ERROR:
level = Level.SEVERE;
break;
case VERBOSE:
level = Level.FINEST;
break;
case DEBUG:
level = Level.FINER;
break;
case SILENT:
level = Level.OFF;
break;
default:
// no impl
}
return level;
}
}
public class AndroidLogger extends Logger {
public AndroidLogger(LogLevel logLevel) {
super(logLevel);
}
....
@Override
public void debug(String tag, String message) {
if (isLoggable(LogLevel.DEBUG)) {
Log.d(tag, message, null);
}
}
@Override
public void error(String tag, String message) {
if (isLoggable(LogLevel.ERROR)) {
Log.e(tag, message, tr);
}
}
....
第4步:
基于android.util.Log的记录器实现
public class JavaLogger extends Logger {
private java.util.logging.Logger mLogger;
public JavaLogger(LogLevel logLevel, String name) {
mLogger = java.util.logging.Logger.getLogger(name);
ConsoleHandler handler = new ConsoleHandler();
Level level = mapLogLevel(logLevel);
handler.setLevel(level);
mLogger.addHandler(handler);
mLogger.setLevel(level);
}
@Override
public void debug(String tag, String message) {
if (isLoggable(LogLevel.DEBUG)) {
mLogger.finer(message);
}
}
@Override
public void error(String tag, String message) {
if (isLoggable(LogLevel.ERROR)) {
mLogger.severe(message);
}
}
....
private Level mapLogLevel(LogLevel logLevel) {
Level level = Level.OFF;
switch (logLevel) {
case INFO:
level = Level.INFO;
break;
case WARNING:
level = Level.WARNING;
break;
case ERROR:
level = Level.SEVERE;
break;
case VERBOSE:
level = Level.FINEST;
break;
case DEBUG:
level = Level.FINER;
break;
case SILENT:
level = Level.OFF;
break;
default:
// no impl
}
return level;
}
}
public class AndroidLogger extends Logger {
public AndroidLogger(LogLevel logLevel) {
super(logLevel);
}
....
@Override
public void debug(String tag, String message) {
if (isLoggable(LogLevel.DEBUG)) {
Log.d(tag, message, null);
}
}
@Override
public void error(String tag, String message) {
if (isLoggable(LogLevel.ERROR)) {
Log.e(tag, message, tr);
}
}
....
}
第五步:
为over Logger实现创建一个简单的包装器类
public class AppLogger {
private static Logger sLogger;
public static void init(Logger logger) {
sLogger = logger;
}
public static void debug(final String tag, String message) {
sLogger.debug(tag, message);
}
public static void error(final String tag, String message) {
sLogger.error(tag, message, null);
}
public static void error(final String tag, String message, Throwable t) {
sLogger.error(tag, message, t);
}
...
}
步骤6:初始化
Android
创建方法的应用
AppLogger.init(new AndroidLogger(LogLevel.DEBUG));
Junit
AppLogger可以在@BeforeClass或@Before中初始化
AppLogger.init(new JavaLogger(LogLevel.DEBUG, "test-logger"));
现在您可以在Android Studio的测试运行窗口中观察日志消息了如果您仍然选择使用黑客解决方案(使用PowerMockito),我实现了以下类来模拟所有Android日志函数
导入android.util.Log;
导入org.junit.BeforeClass;
导入org.junit.runner.RunWith;
导入org.mockito.invocation.invocationMock;
导入org.mockito.stubing.Answer;
导入org.powermock.api.mockito.PowerMockito;
导入org.powermock.core.classloader.annotations.PrepareForTest;
接口信息处理器