How to create custom shadows in robolectric 3.0?

8.8k Views Asked by At

I need to mock some custom class (create for it a shadow). I have already read on http://robolectric.org/custom-shadows/ how to do this.

so, i have some class:

public class MyClass {

  public static int regularMethod() { return 1; }
}

I create a shadow:

@Implements(MyClass.class)
public class MyShadowClass {

  @Implementation
  public static int regularMethod() { return 2; }
}

And i set the shadow in Test-class:

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, shadows={MyShadowClass.class})
public class MyTest {

  @Test
  public void testShadow() {
      assertEquals(2, MyClass.regularMethod());
  }
}

But the shadow is not used.

java.lang.AssertionError: 
Expected :2
Actual   :1

How to make any custom shadow visible for RobolectricGradleTestRunner?

I have already tried:

  1. http://www.codinguser.com/2015/06/how-to-create-shadow-classes-in-robolectric-3/
  2. https://github.com/jiahaoliuliu/RobolectricSample/blob/master/app-tests/src/main/java/com/jiahaoliuliu/robolectricsample/RobolectricGradleTestRunner.java
  3. Mock native method with a Robolectric Custom shadow class

but i get various compilation errors, such as

  • InstrumentingClassLoaderConfig not found
  • Setup not found

how to use custom shadows correctly in robolectric 3.0?

2

There are 2 best solutions below

0
On

Custom shadows should be avoided and must be a last ditch resort. It should only be used if you cannot do much refactor in your code which is preventing you from running your tests like a native method call. It's better to mock the object of that class or spy using powermock or mockito than custom shadow it. If it's a static method, then use powermock.

In our project, we had a class which had some native methods and it was the config class used everywhere in the app. So we moved the native methods to another class and shadowed that. Those native methods were failing the test cases.

Anyways here's how you can custom shadow in robolectric 3.0:

Create a custom test runner that extends RobolectricGradleTestRunner:

public class CustomRobolectricTestRunner extends RobolectricGradleTestRunner {


public CustomRobolectricTestRunner(Class<?> klass) throws InitializationError {
    super(klass);
}

public InstrumentationConfiguration createClassLoaderConfig() {
    InstrumentationConfiguration.Builder builder = InstrumentationConfiguration.newBuilder();
    builder.addInstrumentedPackage("com.yourClassPackage");
    return builder.build();
}

Make sure that that the package doesn't contain any test cases that you are running using robolectric.

1
On

I am Jiahao, the creator of the second repository that you are referring.

First of all thanks for to check my code. I do many researches on Android and I am glad that my research is useful for someone else.

Then, the Shadow about Robolectric. I am using Robolectric 3.1 in this project, to test how Robolectric 3 works with MarshMallow: https://github.com/jiahaoliuliu/robolectricForMarshmallow

I have been testing the new Runtime Permission Manager, as well as shadowing application and activities.

Here is sample code of the shadowed activity:

import android.content.Context;
import com.jiahaoliuliu.robolectricformarshmallow.controller.MainController;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;

/**
 * Created by Jiahao on 7/18/16.
 */
@Implements(MainController.class)
public class MainControllerShadow {

    public void __constructor__ (Context context) {
        // Not do anything
    }

    @Implementation
    public String getTextToDisplay(boolean permissionGranted) {
        return "Test";
    }
}

https://github.com/jiahaoliuliu/robolectricForMarshmallow/blob/master/app/src/test/java/com/jiahaoliuliu/robolectricformarshmallow/shadow/MainControllerShadow.java

And this is how I am using it in the unit test:

package com.jiahaoliuliu.robolectricformarshmallow;

import com.jiahaoliuliu.robolectricformarshmallow.shadow.MainControllerShadow;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;

import static org.junit.Assert.*;

/**
 * Created by Jiahao on 6/30/16.
 */
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, manifest = Config.NONE, application = FoolApplication.class,
    shadows = { MainControllerShadow.class}, sdk = 18)
public class MainActivityTest {

    private MainActivity mMainActivity;

    @Before
    public void setUp() throws Exception {
        mMainActivity = Robolectric.setupActivity(MainActivity.class);
    }

    @After
    public void tearDown() throws Exception {

    }

    @Test
    public void testOnCreate() throws Exception {
        // Simple test to know that it works
        assertTrue(true);
    }
}

https://github.com/jiahaoliuliu/robolectricForMarshmallow/blob/master/app/src/test/java/com/jiahaoliuliu/robolectricformarshmallow/MainActivityTest.java

As you can see, I am not using customized Gradle Test Runner. I have checked the source code of Robolectric, for version 3.0 and 3.1 (latest) it is good enough to just specify the shadow classes in the header.

I hope it helps