By now everyone has heard about the Marshmallow permission model. You havent? Where have you been? Go and read Requesting Permissions at Run Time and come back when you've finished. It's ok. I can wait...
So it's all well and good except when you're trying to run tests. When you're using Espresso there's just no way to tap on the allow permission dialog.
It's possible to deny and grant permissions using adb (as you know because you read the Requesting Permissions link didn't you? Didn't you!? But how to work that into an automated test?
Let's just take the simple case; grant all permissions. Yes I know that a proper test should test when permissions are denied as well but you have to give your QAs something to do.
I came across this
post
which details how to add a task into your build.gradle
to grant
permissions. It's a great post and I suggest you read it. However it
has some shortcomings.
Firstly it doesn't really explain that it only really works if the app
is already installed on your device. The grantPermissions
task is
made to be a dependency of assembleDebugAndroidTest
. However at
this point the app hasn't been pushed onto the test device. So unless
the app is already on there from a previous test, the granting of
permissions doesn't work.
However the bigger problem for me is that it doesn't work if you have flavoured builds. (Yes, I'm British. I put a 'u' in flavour. Deal with it!)
The app I've been working on has a dev flavour, a qa flavour, a staging flavour and the production flavour. (For more on flavours, see the section "Define product flavors in the build file" over at Configuring Gradle Builds.)
When you use flavours, the names of your tasks change to incorporate
them. So assembleDebugAndroidTest
becomes
assembleDevDebugAndroidTest
(note the addition of Dev in there)
for your dev flavour.
So to get around this, here's the fragment from my build.gradle that adds in a permissions grant task for each flavour.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
This chuck of code loops around over all the build flavours (line 1).
It creates a new task for each flavour called
grantFlavourPermissions
(line 5) where Flavour is the name of your
flavour. So you'd get grantDevPermissions
, grantQaPermissions
,
etc. (By the way, in the gradle task names the flavours are
capitalised, so if your flavour is called "lemon" the task is
"grantLemonPemissions".)
The comnands in the block (lines 6 and 7) are the grant commands. You can have as many as you like in here.
The def applicationId
on line 2 is there because, in our app, each
flavour has a different applicationId so they can all be installed on
one device at the same time. The grant
sub-command of pm requires
an applicationId to know what package to grant permissions to. The
def adb
on line 3 is to get the full path to the execuable adb
.
Line 9 just sets a description for the task and lines 11 to 16 add the make the equivalently flavoured assemble task depend on the new grant task. More on this below.
In his post Josh uses a shell script to run all his grants, and he makes it intelligent to loop over all connected devices. I'm lazier and assume that I'm only running against one device. I also like to have the grants in the build.gradle rather than a shell script, but that's just me.
Whilst I still make my grant...Permission
task a dependency
for assemble...DebugAndroidTest
, as mentioned above, if you
run a test and the app isn't already installed, this won't work; the
permissions grant has to be done after the app is installed. To get
around this we do
./gradlew uninstallAll installDevDebug grantDevPermissions spoonDevDebugAndroidTest
I like to uninstall any previous builds and tests before running a new test suite, hence why I need to redo the grant each time. As always, replace Dev with the name of your flavour in the above commands.
(We use Spoon from Square and the
Stanfy gradle plugin
to run our instrumentation tests. You can probably replace
spoonDevDebugAndroidTest
with connectedDevDebugAndroidTest
if you
like.)
Just to tie up a loose end; both spoonDevDebugAndroidTest
or
connectedDevDebugAndroidTest
seemed to install the app themselves so
there was no way to create a gradle dependency that would run the
grantDevPermissions
task at the right time. As Josh also mentions
as a comment in his code, Android Studio also only seems to run the
assemble
task if you run your tests from within the IDE. So using
the assemble
tasks for dependency seems to work, with the caveat
that I've said twice already; this only works once the app has been
installed once. After that the permissions grants are remembered
(until you uninstall).
Oh, and before I forget, I haven't tested any of this on un-flavoured builds. If it doesn't work for you - and it probably won't - Josh's solution likely will.
Darren @ Æ
Comments
comments powered by Disqus