android - Configuration change using Robolectric -


to retain asynctasks across configuration changes, use fragment-based solution setretaininstance(true), hosts each asynctask , calls listening activity, similar solution http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html

ultimately, purpose test asynctask's retention functionality throughout configuration changes using robolectric, need start setting actual configuration change correctly. however, seems can't mimic exact reference behavior occurs during configuration change.


real app: when running real app, on configuration change, activity destroyed , recreated while fragment retained, seems working. can see checking references before , after configuration change (example references used below):

  • real app, before: activity: abc fragment: xyz

  • real app, after: activity: bca fragment: xyz (properly retained , reattached)


case 1: when running recreate() on activity in robolectric test, however, activity doesn't seem have instance recreated (despite docs saying method performs lifecycle calls):

mactivitycontroller = robolectric.buildactivity(asynctasktestactivity.class).attach().create().start().resume().visible();  mactivity = mactivitycontroller.get(); mactivity.recreate(); 
  • robolectric recreate(), before: activity: abc fragment: xyz

  • robolectric recreate(), after activity: abc fragment: xyz

this leads me believe new activity instance isn't created , reattachment functionality therefore hasn't happened in real way.


case 2: if create test based on individual lifecycle calls instead:

mactivitycontroller = robolectric.buildactivity(asynctasktestactivity.class).attach().create().start().resume().visible(); mactivitycontroller.pause().stop().destroy(); mactivitycontroller = robolectric.buildactivity(asynctasktestactivity.class).attach().create().start().resume().visible(); 

in version, seems activity gets replaced scratch, fragment:

  • robolectric separate lifecycle calls, before activity: abc fragment: xyz

  • robolectric separate lifecycle calls, after activity: bca fragment: yzx


it seems i'm either reusing same activity (case 1) or replacing new instances, if there no underlying application retains fragment (case 2).

question: there way can set robolectric test mimic reference result when running app in actual android environment (as per real app result), or stuck either creating separate test app or settling robotium functional tests? tried https://stackoverflow.com/a/26468296 got same result case 2.

thanks in advance!

i have played around bit , came solution using robolectric 3.0 , mockito:

@runwith(robolectricgradletestrunner.class)  @config(constants = buildconfig.class, sdk = build.version_codes.kitkat, shadows = {exampleactivitytest.exampleactivityshadow.class}) public class exampleactivitytest {      @mock     private fragmentmanager fragmentmanagermock;      @before     public void setup() {         initmocks(this);         setupfragmentmanagermock();     }      @test     public void testrestoreafterconfigurationchange() {         // prepare         activitycontroller<exampleactivity> controller = robolectric.buildactivity(exampleactivity.class);         exampleactivity activity = controller.get();         exampleactivityshadow shadow = (exampleactivityshadow) shadows.shadowof(activity);         shadow.setfragmentmanager(fragmentmanagermock);          activitycontroller<exampleactivity> controller2 = robolectric.buildactivity(exampleactivity.class);         exampleactivity recreatedactivity = controller2.get();         exampleactivityshadow recreatedactivityshadow = (exampleactivityshadow) shadows.shadowof(recreatedactivity);         recreatedactivityshadow.setfragmentmanager(fragmentmanagermock);          // run & verify         controller.create().start().resume().visible();          activity.findviewbyid(r.id.inc_button).performclick();         activity.findviewbyid(r.id.inc_button).performclick();          assertequals(2, activity.lostcount.count);         assertequals(2, activity.retainedcount.count);          bundle bundle = new bundle();         controller.saveinstancestate(bundle).pause().stop().destroy();         controller2.create(bundle).start().restoreinstancestate(bundle).resume().visible();          assertequals(0, recreatedactivity.lostcount.count);         assertequals(2, recreatedactivity.retainedcount.count);     }      private void setupfragmentmanagermock() {         final hashmap<string, fragment> fragments = new hashmap<>();         doanswer(new answer<object>() {             @override             public object answer(invocationonmock invocation) throws throwable {                 return fragments.get(invocation.getarguments()[0]);             }         }).when(fragmentmanagermock).findfragmentbytag(anystring());          final hashmap<string, fragment> fragmentstobeadded = new hashmap<>();         final fragmenttransaction fragmenttransactionmock = mock(fragmenttransaction.class);         doanswer(new answer<object>() {             @override             public object answer(invocationonmock invocation) throws throwable {                 fragmentstobeadded.put((string) invocation.getarguments()[1], (fragment) invocation.getarguments()[0]);                 return fragmenttransactionmock;             }         }).when(fragmenttransactionmock).add(any(fragment.class), anystring());         doanswer(new answer<object>() {             @override             public object answer(invocationonmock invocation) throws throwable {                 fragments.putall(fragmentstobeadded);                 return null;             }         }).when(fragmenttransactionmock).commit();          when(fragmentmanagermock.begintransaction()).thenreturn(fragmenttransactionmock);     }      @implements(activity.class)     public static class exampleactivityshadow extends shadowactivity {          private fragmentmanager fragmentmanager;          @implementation         public fragmentmanager getfragmentmanager() {             return fragmentmanager;         }          public void setfragmentmanager(fragmentmanager fragmentmanager) {             this.fragmentmanager = fragmentmanager;         }     } } 

note have mocked methods of fragmentmanager (begintransaction() , findfragmentbytag()) , fragmenttransaction (add() , commit()) use in code, might need expand these depending on code.

i haven't done work robolectric yet, there may more elegant solution this, works me now.

you can see full source code , project setup here: https://github.com/rgeldmacher/leash (might worth if still need retain objects ;) )


Comments

Popular posts from this blog

node.js - Mongoose: Cast to ObjectId failed for value on newly created object after setting the value -

[C++][SFML 2.2] Strange Performance Issues - Moving Mouse Lowers CPU Usage -

ios - Possible to get UIButton sizeThatFits to work? -