在Android Studio JUnit测试中记录日志消息
问题的出现原因是在Android Studio的JUnit测试中,无法直接使用android.util.Log类进行日志记录,因为android.util.Log的实现在本地JVM上运行的单元测试中不可用。这是因为"用于运行单元测试的android.jar文件不包含任何实际的代码(这些API仅由设备上的Android系统映像提供)"。所以如果想要在JUnit测试中使用android.util.Log,需要在本地模拟它,并利用System.out.print打印到控制台。
解决方法是使用PowerMockito来进行模拟。首先,在项目中添加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在这里的答案来返回传递给模拟对象的参数。具体实现代码如下:
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 { 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>() { 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"); } }
希望这对某些人有所帮助!同时,建议尽量避免使用PowerMock,而是使用标准的面向对象编程实践来实现所需的行为。
问题:在Android Studio的JUnit测试中,日志消息无法在控制台中输出。
原因:在JUnit测试中,Android Studio默认不会输出日志消息。因此,需要创建一个自定义的Log类来替代Android中的Log类,以便在JUnit测试中输出日志消息。
解决方法:按照以下步骤创建自定义的Log类,并将其放置在正确的位置。
1. 创建一个名为"Log.java"的文件。
2. 将该文件放置在"test/java/android/util/"目录下。
3. 在"Log.java"文件中,添加以下代码:
package android.util; public class Log { public static int d(String tag, String msg) { System.out.println("DEBUG: " + tag + ": " + msg); return 0; } public static int i(String tag, String msg) { System.out.println("INFO: " + tag + ": " + msg); return 0; } public static int w(String tag, String msg) { System.out.println("WARN: " + tag + ": " + msg); return 0; } public static int e(String tag, String msg) { System.out.println("ERROR: " + tag + ": " + msg); return 0; } }
4. 在JUnit测试中,使用自定义的Log类来输出日志消息。
通过以上步骤,可以在Android Studio的JUnit测试中输出日志消息,并在控制台中查看输出结果。
在Android Studio的JUnit测试中,无法看到Logcat的输出,因为Logcat是Android的特性,JUnit测试只能使用标准的Java,因此Android的特性无法正常工作。
在单元测试中,你可以将“测试替身”注入到被测试的组件中。但是,Log.x调用是静态的,因此无法重写它们(除非使用PowerMock等工具,但应尽量避免使用)。
因此,第一步是引入一个非静态类,作为Log.x调用的代理:
/** * 这个类是一个非静态的日志记录器 */ 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{ public void e(String tag, String message) { System.out.println("E " + tag + ": " + message); } // 其他方法类似 }
最后一步是在单元测试中将UnitTestLogger注入到Logger的位置:
(MockitoJUnitRunner.class) public class SomeClassTest { private Logger mLogger = new UnitTestLogger(); private SomeClass SUT; public void setup() throws Exception { SUT = new SomeClass(/* 其他依赖项 */ mLogger); } }
如果你想严格遵守面向对象编程的概念,可以为Logger和UnitTestLogger提取一个公共接口。
话虽如此,我从未遇到过需要在单元测试中调查Log.x调用的情况。我怀疑你也不需要这样做。你可以在调试模式下运行单元测试,并逐行调试代码,这比尝试调查logcat输出要快得多...
一般建议:
如果你正在测试的代码中包含Log.x静态调用,并且你的单元测试没有崩溃-那就有问题了。
我猜测要么所有的测试都是用Robolectric运行的,要么你在build.gradle中有这样的语句:unitTests.returnDefaultValues = true。
如果你用Robolectric运行所有的测试,那么这只是效率低下,但如果所有的Android调用都返回默认值,那么你的测试套件就不可靠。我建议你在继续之前解决这个问题,因为它迟早会给你带来麻烦。
你可以通过将Logger实例命名为Log而不是mLogger来避免完全改变Log调用的需要。
我会将UnitTestLogger重命名为ConsoleLogger以增加清晰度。
我同意,ConsoleLogger会是一个更具描述性的名称。