Android Studio单元测试报错:java.lang.RuntimeException: Method i in android.util.Log not mocked
Hey there! Let's tackle that frustrating error you're seeing while running unit tests for your BeatBox app. I've been in this spot before, so let's break down why it happens and how to fix it.
Why This Error Happens
When you run local unit tests in Android Studio, they execute on your computer's JVM—not on an Android device or emulator. The Android SDK provides empty "mock" implementations for most framework classes (like android.util.Log) to keep tests fast, but these mocks throw a RuntimeException if you try to call their methods directly. That's exactly what's happening when your code calls Log.i().
Solutions to Try
1. Quick Fix: Let Tests Return Default Values
If you don't need to verify that Log methods are being called, the fastest way to get your tests running is to tell the test framework to return default values for unused Android methods instead of throwing exceptions.
Add this to your module-level build.gradle (not the project-level one):
android { testOptions { unitTests.returnDefaultValues = true } }
This will make Log.i() do nothing silently (void methods) and return default values for other framework methods (like 0 for ints, false for booleans). Perfect for getting tests up and running quickly.
2. Verify Log Calls with Mockito
If your test needs to check that Log.i() is being called with the right tag and message, you can mock the static Log class using Mockito (requires Mockito 3.4 or higher).
Here's how to implement it in your test:
import org.junit.Test; import org.mockito.MockedStatic; import static org.mockito.Mockito.*; @Test public void yourBeatBoxTest() { // Mock the Log class for the duration of the test try (MockedStatic<android.util.Log> mockedLog = mockStatic(android.util.Log.class)) { // Set up mock behavior: make Log.i() return 0 (its normal return value) mockedLog.when(() -> android.util.Log.i(anyString(), anyString())).thenReturn(0); // Run your test code here (e.g., create BeatBox instance, call methods) BeatBox beatBox = new BeatBox(); beatBox.doSomethingThatLogs(); // Verify that Log.i was called with expected values mockedLog.verify(() -> android.util.Log.i("BeatBox", "Expected log message")); } }
This gives you full control over verifying Log interactions without relying on real Android framework code.
3. Best Practice: Wrap Log in a Custom Interface
For long-term testability, decouple your code from the Android Log class by creating a wrapper interface. This follows the dependency inversion principle and makes your code easier to test and maintain.
Step 1: Create a Logger Interface
public interface Logger { void info(String tag, String message); }
Step 2: Implement an Android Logger
public class AndroidLogger implements Logger { @Override public void info(String tag, String message) { android.util.Log.i(tag, message); } }
Step 3: Inject the Logger into Your BeatBox Class
public class BeatBox { private final Logger logger; // Use constructor injection to pass in the Logger public BeatBox(Logger logger) { this.logger = logger; } public void someMethod() { // Use your custom logger instead of direct Log.i() calls logger.info("BeatBox", "Doing something important"); } }
Step 4: Mock the Logger in Tests
import org.junit.Before; import org.junit.Test; import static org.mockito.Mockito.*; public class BeatBoxTest { private Logger mockLogger; private BeatBox beatBox; @Before public void setUp() { mockLogger = mock(Logger.class); beatBox = new BeatBox(mockLogger); } @Test public void someMethod_LogsCorrectMessage() { beatBox.someMethod(); // Verify your logger was called with the right values verify(mockLogger).info("BeatBox", "Doing something important"); } }
This approach completely isolates your business logic from Android framework dependencies, making tests faster and more reliable.
Which One Should You Choose?
- Use Option 1 if you just need to get tests running quickly and don't care about Log verification.
- Use Option 2 if you need to check Log calls but don't want to refactor your code.
- Use Option 3 for cleaner, more testable code in the long run—it's the best practice for scalable Android apps.
内容的提问来源于stack exchange,提问作者snailp4el

