Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking Android AssetManager

I have a piece of code that accepts Context and passes this context to a private method. The private method calls the getAssets().open() to read a file present in the assets folder of my app.

public void methodA(Context ctx) throws IOException{
     // do some stuff here...
     Object data[] = getFileContents(ctx);
     // use the data[] returned here...

}

private Object[] getFileContents(Context ctx) throws IOException{
     Object[] data;
     BufferedInputStream is = new BufferedInputStream(context.getAssets().open("test.txt"));
     // parse file and create array of Objects[]
     return data[];
}

I am writing Unit tests to test the methodA() using Mockito so that i can test passing junk data or throw exceptions in my testcases.

The problem is that i cannot mock AssetManager class in android (it being Final).

I tried to use InstrumentationTestCase to inject real and test context, but that only works for few scenarios. How do I control the BufferedInputStream so that I can provide it any input I want (using mocks or otherwise) ?

like image 931
Akshat Avatar asked Sep 04 '15 22:09

Akshat


2 Answers

I had to deal with the same problem. This is how I managed to access configuration file from my unit test programm without using Instrumentation test. (I use Android Studio 2.1).

Code of the unit test:

public class ParametersTest {

    Parameters parameters = null;

    @Mock
    Context context;
    @Mock
    AssetManager assetManager;
    @Mock
    InputStream inputStream;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);//create all @Mock objetcs
        doReturn(assetManager).when(context).getAssets();
        //parametersandroid.xml is located in test/resources directory
        //parametersandroid.xml does not refer to a DTD file configuration.dtd
        //get the full path name of the file
        URL resource = ParametersTest.class.getClassLoader().getResource("parametersandroid.xml");
        // to be used  MyClass
        // inside the method I want to be tested there is this statement :
        // InputStream inputStream = this.assetManager.open(xmlFile);
        InputStream inputStream=new FileInputStream(resource.getPath());
        doReturn(inputStream).when(assetManager).open(anyString());
       // AssetManager assetManager = context.getAssets();
       // parameters = new Parameters(assetManager, resource.getPath());
        parameters = new Parameters(context, resource.getPath());

    }
    @Test
    public void testExtract() throws Exception {
       assertEquals(parameters.extract("//database/index[@name='TeamNameIdx']/create").replaceAll("[^a-z,A-Z,;,.,?,']", ""),"createindexTeamNameIdxonTeamEntnameasc;");
    }
}

Code of the class to be tested :

public class Parameters extends fr.acnice.valade.eric.gemara.utilities.Parameters {
    private AssetManager assetManager = null;

    public Parameters(Context context, String xmlFile) {
        super(xmlFile);
        this.assetManager = context.getAssets();
    }
    @Override
    public String extract(String request) throws XPathExpressionException,
            Exception {
        InputStream inputStream = this.assetManager.open(super.xmlFile);
        String result = (String) xPath.evaluate(request, new InputSource(inputStream),
                XPathConstants.STRING);
        if (result.isEmpty()) {
            throw new Exception(
                    "Xpath result empty !!! check configuration file");
        }
        return result;
    }

}
like image 114
Eric Valade Avatar answered Nov 09 '22 17:11

Eric Valade


Try retrieving the context by calling the codes below:

Context context = InstrumentationRegistry.getTargetContext();
BufferedInputStream is = new BufferedInputStream(context.getAssets().open("test.txt"));
like image 29
noahutz Avatar answered Nov 09 '22 17:11

noahutz