Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking up WifiManager for Android Unit Testing

I'm trying to implement some unit tests for a couple of classes that rely on WifiManager and the returned ScanResults. What I'd like to do is be able to control the ScanResults that I'm receiving in order to test a variety of different conditions.

Unfortunately it's been quite difficult for me to successfully mock up WifiManager (though I suppose I can pass its constructor null references in my MockWifiManager). This will only be my first problem as once I have a MockWifiManager to play with (if this even works!) I will have to successfully create my test ScanResults which does not have a public constructor (Imagine it's created by some factory somewhere).

Questions: With it not having a public constructor can I even extend it?

Am I going about this all wrong? I often get asked questions about how to do a specific task but really they're trying to solve a different problem the wrong way, maybe that's what I'm doing here?

I'm very new to android so having to mock up all of this functionality has been trying to say the least.

Thanks for your inputs!

Edit: I'm having a hell of a time instantiating a MockWifiManager as well. The constructor for wifi manager is expecting an IWifiManager a type which does not appear to exist in the Android SDK.

like image 367
Brian Avatar asked Apr 06 '11 01:04

Brian


2 Answers

Create an abstraction around WifiManager. Use this for your mocking. Mocking stuff you don't own is hard and brittle. If done right you should be able to switch the internals, plus you'll end up with a better mockable API.

For your testing you can stub/fake the manager to you hearts content. For production you'll pass in a concrete instance.

With regards to your point about changing your code just to make it testable that is incorrect. Firstly you should mock roles not types as discussed in the paper below. Google for more info.

Secondly creating an abstraction around third party code is a best practice as stated by the dependency inversion principle in SOLID. You should always depend on abstractions rather than concrete implementations whether you are unit testing or not.

http://www.objectmentor.com/resources/articles/dip.pdf http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

like image 182
Finglas Avatar answered Nov 11 '22 17:11

Finglas


You could try to create the ScanResult instances by using reflection to access the private constructors. The code might look something like this:

        try {
            Constructor<ScanResult> ctor = ScanResult.class.getDeclaredConstructor(null);
            ctor.setAccessible(true);
            ScanResult sr = ctor.newInstance(null);
            sr.BSSID = "foo";
            sr.SSID = "bar";
            // etc... 
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

For other ways of testing, I most of times convert the information from instances like ScanResult and encapsulate only the information I need into my own objects. These I feed to the method doing the hard work. This makes testing easier as you can easily build these intermediate objects without relying on the real ScanResult objects.

like image 44
Stephan Avatar answered Nov 11 '22 15:11

Stephan