en / de
AI
Expertisen
Methoden
Dienstleistungen
Referenzen
Jobs & Karriere
Firma
Technologie-Trends TechCast WebCast TechBlog News Events Academy

Log redirection in Android unit tests

If you’re using JUnit, Mockito and PowerMock to write unit tests in Android Studio you probably often need to mock calls to android.util.Log. That’s because by default in Android Studio unit tests are executed against a modified version of the android.jar library which contains stubs only. If anywhere in your tested code you have a log statement that you don’t mock you will get a RuntimeException.

For example a call like

Log.d("tag", "message");

will throw a RuntimeException:

RuntimeException: Method d in android.util.Log not mocked. See http://g.co/androidstudio/not-mocked for details.

Wouldn’t it be nice if just all the log message would be redirected automatically to the unit test output?

Fortunately PowerMockito provides the possibility to create MockPolicies. Using a mock policy we can setup the log redirection once and then just use an annotation (on the class containing the test cases) to apply it.

Usage example:

@RunWith(PowerMockRunner.class)
@MockPolicy(LogRedirection.class)
public class MyTests {

    @Test
    public void test1() {
    }
}

To create a log redirection mock policy we need to implement the PowerMockPolicy interface. The two methods on this interface allow us to apply a class loading policy and an interception policy.

public interface PowerMockPolicy {

	void applyClassLoadingPolicy(MockPolicyClassLoadingSettings settings);

	void applyInterceptionPolicy(MockPolicyInterceptionSettings settings);
    
}

In the first method we need to tell PowerMock which classes should be modified by the mock class loader before these classes are loaded. In our case it’s just the android.util.Log class. In the second method we create the mocks for the methods of the Log class which should just redirect the messages to the standard output.

The following code shows a sample implementation for a mock policy that redirects all log calls to the standard output.

import android.util.Log;
import org.powermock.core.spi.PowerMockPolicy;
import org.powermock.mockpolicies.MockPolicyClassLoadingSettings;
import org.powermock.mockpolicies.MockPolicyInterceptionSettings;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LogRedirection implements PowerMockPolicy {

    @Override
    public void applyClassLoadingPolicy(MockPolicyClassLoadingSettings settings) {
        settings.addFullyQualifiedNamesOfClassesToLoadByMockClassloader(Log.class.getName());
    }

    @Override
    public void applyInterceptionPolicy(MockPolicyInterceptionSettings settings) {
        try {

            // Mock Log.v(String tag, String msg)
            settings.proxyMethod(Log.class.getMethod("v", String.class, String.class), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String logLevel = method.getName().toUpperCase();
                    String tag = args[0].toString();
                    String message = args[1].toString();
                    return redirect(logLevel, tag, message, null);
                }
            });

            // Mock Log.v(String tag, String msg, Throwable tr)
            settings.proxyMethod(Log.class.getMethod("v", String.class, String.class, Throwable.class), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String logLevel = method.getName().toUpperCase();
                    String tag = args[0].toString();
                    String message = args[1].toString();
                    Throwable throwable = (Throwable) args[2];
                    return redirect(logLevel, tag, message, throwable);
                }
            });

            // Mock Log.d(String tag, String msg)
            settings.proxyMethod(Log.class.getMethod("d", String.class, String.class), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String logLevel = method.getName().toUpperCase();
                    String tag = args[0].toString();
                    String message = args[1].toString();
                    return redirect(logLevel, tag, message, null);
                }
            });

            // Mock Log.d(String tag, String msg, Throwable tr)
            settings.proxyMethod(Log.class.getMethod("d", String.class, String.class, Throwable.class), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String logLevel = method.getName().toUpperCase();
                    String tag = args[0].toString();
                    String message = args[1].toString();
                    Throwable throwable = (Throwable) args[2];
                    return redirect(logLevel, tag, message, throwable);
                }
            });

            // Mock Log.i(String tag, String msg)
            settings.proxyMethod(Log.class.getMethod("i", String.class, String.class), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String logLevel = method.getName().toUpperCase();
                    String tag = args[0].toString();
                    String message = args[1].toString();
                    return redirect(logLevel, tag, message, null);
                }
            });

            // Mock Log.i(String tag, String msg, Throwable tr)
            settings.proxyMethod(Log.class.getMethod("i", String.class, String.class, Throwable.class), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String logLevel = method.getName().toUpperCase();
                    String tag = args[0].toString();
                    String message = args[1].toString();
                    Throwable throwable = (Throwable) args[2];
                    return redirect(logLevel, tag, message, throwable);
                }
            });

            // Mock Log.w(String tag, String msg)
            settings.proxyMethod(Log.class.getMethod("w", String.class, String.class), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String logLevel = method.getName().toUpperCase();
                    String tag = args[0].toString();
                    String message = args[1].toString();
                    return redirect(logLevel, tag, message, null);
                }
            });

            // Mock Log.w(String tag, String msg, Throwable tr)
            settings.proxyMethod(Log.class.getMethod("w", String.class, String.class, Throwable.class), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String logLevel = method.getName().toUpperCase();
                    String tag = args[0].toString();
                    String message = args[1].toString();
                    Throwable throwable = (Throwable) args[2];
                    return redirect(logLevel, tag, message, throwable);
                }
            });

            // Mock Log.w(String tag, Throwable tr)
            settings.proxyMethod(Log.class.getMethod("w", String.class, Throwable.class), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String logLevel = method.getName().toUpperCase();
                    String tag = args[0].toString();
                    String message = getStackTraceString((Throwable) args[1]);
                    return redirect(logLevel, tag, message, null);
                }
            });

            // Mock Log.e(String tag, String msg)
            settings.proxyMethod(Log.class.getMethod("e", String.class, String.class), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String logLevel = method.getName().toUpperCase();
                    String tag = args[0].toString();
                    String message = args[1].toString();
                    return redirect(logLevel, tag, message, null);
                }
            });

            // Mock Log.e(String tag, String msg, Throwable tr)
            settings.proxyMethod(Log.class.getMethod("e", String.class, String.class, Throwable.class), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String logLevel = method.getName().toUpperCase();
                    String tag = args[0].toString();
                    String message = args[1].toString();
                    Throwable throwable = (Throwable) args[2];
                    return redirect(logLevel, tag, message, throwable);
                }
            });

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    private static int redirect(String logLevel, String tag, String message, Throwable throwable) {
        if (throwable == null) {
            System.out.println(String.format("%s - %s: %s", logLevel, tag, message));
        } else {
            System.out.println(String.format("%s - %s: %s", logLevel, tag, message + '\n' + getStackTraceString(throwable)));
        }
        return 0;
    }

    private static String getStackTraceString(Throwable tr) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        tr.printStackTrace(pw);
        pw.flush();
        return sw.toString();
    }
}
Kommentare

Schreiben Sie einen Kommentar

Ihre E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Newsletter - aktuelle Angebote, exklusive Tipps und spannende Neuigkeiten

 Jetzt anmelden

Copyright © 2025 Noser Engineering AG – Alle Rechte vorbehalten.

NACH OBEN
Privacy Policy Cookie Policy
Zur Webcast Übersicht