Getting feedback on your app's releases before they reach the store is critical. App Distribution makes it easy to share pre-release builds of your Android and iOS apps with your testers. You can distribute your apps to testers using the Firebase console, the Firebase CLI, or the App Distribution plugins for Gradle and fastlane.
We've also heard that you'd like to integrate your own tools directly with App Distribution, or implement custom pre-release testing workflows. That's where the Firebase App Distribution REST API comes in. With this API, you can build custom logic into your team's tools and services to add and remove testers, upload new app binaries, distribute your releases, update release notes, deleting releases, and more.
For example, let's say that your company gives all employees early access to the latest builds of your app. You also have a personnel management tool that automates the employee onboarding and offboarding process. Wouldn't it be nice if employee access was automatically granted and revoked as employees join and leave your company? With the REST API, you can build this functionality directly into your company's workflow.
Let's walk through the process to automatically grant and revoke access.
Next, in hooks that are triggered when an employee joins or leaves the company, use the testers.batchAdd and testers.batchRemove endpoints to add or remove the employee as a tester in App Distribution using an HTTP request to the API. For example, here's how you can add a new tester using curl:
Let's say you periodically audit access to your pre-release apps to make sure that only authorized internal testers have access. To automate this audit, you can use the testers.list endpoint to get the list of testers in your Firebase project. You can then write your own logic to compare that to your list of employee email addresses, or check the domains:
Tester access and auditing is just one example of custom functionality you can build using the API. Other examples of functionality that you can build include keeping release notes in sync with your bug tracking system, fetching details about your most recent release, adding testers to releases from third-party tools, deleting releases in bulk, or fetching test certificate info for your Android app bundle releases.
App Distribution helps you gain confidence in your releases, and the REST API empowers you to integrate pre-release testing into your existing workflows. So get started with the API today! To learn more, and to try out the API using the embedded API Explorer, see the Firebase App Distribution API documentation.
Try out the API using the API Explorer in the documentation.
When Test Lab was originally launched with Firebase in 2016, it supported only Android devices. At Google I/O 2018 in May, Test Lab launched closed beta support for iOS. This included a limited set of iOS devices and a basic UI.
Building comprehensive tests for Android involves writing code, using Espresso and UIAutomator, that acts as a sort of "remote control" for your app. Similarly, on iOS, testing is performed using XCTests. In both cases, Test Lab can run your tests against actual devices in a cloud-hosted device farm.
At the Firebase Summit in Prague at the end of October, the Test Lab team announced general availability of support for iOS, including ten models of iPhones and iPads running seven different versions of iOS, including iOS 12. We have also improved the iOS documentation and console experience for you.
Test Lab also launched a number of improvements to Robo, a tool which runs fully automated tests against your app running on Android devices. Here's what's new with Robo.
Monkey actions
Games are difficult to crawl because they often have a highly customized UI, rather than using system widgets. This makes it difficult for Robo to crawl the game's experience. Now, if Robo detects that the app under test is actually a game, it will perform random taps and swipes in an effort to interact with the game's UI. This can yield useful crash and performance data and is an early but significant step towards more meaningful automated game crawling.
Private APIs detection
Test Lab now detects and warns if your APK makes use of internal Android APIs. On Android P and newer, using such APIs can crash your app. Whenever such an API is accessed during a Robo crawl a stack trace is recorded in the device logs. This pinpoints the location in your app's code where the violation occurs.
"Stuck on porch" warnings
Test Lab now warns developers when it notices that Robo got stuck in a crawl. For example, if the user is presented with a complicated sign-up form or a login screen, it may be difficult for Robo to satisfy the requirements of the form. In situations like this, Robo will suggest an action to the developer to help it continue a full crawl, such as providing test credentials or writing a Robo Script.
If you aren't in the habit of regularly testing your app, consider giving Test Lab a try at no cost using the free daily quota of tests. No coding is necessary to run a Robo test on Android - just upload your APK to get started. And be sure to let us know what you think in the #test-lab channel of the Firebase Slack.
Last year, we launched the beta of Cloud Functions for Firebase to help you build backend functionality for your app without worrying about managing servers. It lets you write in JavaScript or Typescript, deploy to Google's Cloud infrastructure, and execute in response to app events. Today we're pleased to release version 1.0 of the Cloud Functions for Firebase SDK - along with new, frequently requested improvements for the development, testing and monitoring of Functions.
What Cloud Functions for Firebase can do
The Firebase SDK for Google Cloud Functions enables you to extend other Firebase products, giving users the features they need in your apps. One of our earliest customers, Posse, makers of the Hamilton app, needed to create a lottery system to give fans a chance to win tickets to the massively popular Broadway play - without the overhead of managing their own servers. When a user enters the lottery, Functions powered logic runs in the cloud with results stored directly to Cloud Firestore. Lottery results are pushed automatically to lucky fans' Hamilton mobile app.
Posse also needed to scale the Hamilton app. When the lottery opens, the app's usage can suddenly surge multifold before normalizing a few minutes later. Functions automatically scales without needing any action from Posse, and their client only pays for the resources they use.
In addition to ticket lotteries, Posse also used Functions to integrate with other Firebase products to:
Automate creation of user profiles with Firebase Auth
Send push notifications with Firebase Cloud Messaging
Auto-resize user photos with Cloud Storage
Process in-app payments using Stripe via HTTP webhooks
There's a great deal of things you can do with Functions. For more use cases and sample code, see our popular GitHub repo.
Today's v1.0 release comes with a number of new features based on your feedback - here's the lowdown!
Mobile client SDKs
One of your biggest requests was for an easy way to directly call Functions from your client apps. To facilitate that, we're adding a new type of function, along with SDKs for iOS, Android, and web clients. The new type is a Callable HTTPS function that manages an HTTPS request from the client SDK. Callable HTTPS functions drastically reduce the amount of boilerplate code you have to write to make HTTPS requests to Functions.
Here's how it works:
First, write a Callable HTTPS function that accepts a data object from the client. The function returns another data object to the client.
Then, in your mobile app, you use the client SDK to invoke the function. The client SDK manges the HTTPS connection and automatically serializes the input and output data objects. In the request, the SDK will include a user ID token from Firebase Authentication if a user is logged in. Also included is an instance ID token used by Firebase Cloud Messaging.
While Functions Shell gave you the ability to interact with your functions locally, it was not optimal for writing unit tests such as those that run in a CI system. That's why we are launching a new firebase-functions-test npm module that simplifies writing unit tests. firebase-functions-test takes care of the necessary setup and teardown, allowing easy mocking of test data. Now, you can either write completely offline tests that have no side effects, or tests that interact with a development Firebase project - where you can observe the success of actions such as database writes.
Official support for functions emulation
Last fall, we introduced the ability to emulate your functions locally via the Firebase CLI through both the "firebase serve --only functions" and "firebase experimental:functions:shell" command. These were experimental features, and now we are officially supporting them. "firebase experimental:functions:shell" has been renamed to "firebase functions:shell", and "firebase serve" will emulate all HTTPS functions by default without the need for the "--only" flag.
Improved monitoring tools
After deploying functions, you may be wondering: "What's happening with my functions? How is my app performing? Is anything broken?" With today's release, you can now keep tabs on any errors in your functions via the new health monitor:
Additionally, you can track your functions' performance, latency and memory usage:
API updates
The APIs for writing Cloud Functions with the Firebase SDK have changed in this v1.0 SDK release. This means that upgrading to the new SDK will require some updates to any code you've already written during the beta. But don't worry - this doesn't affect any functions you have already deployed. To see what has changed and how to adapt to the new format, just follow our migration guide!
Getting started with Cloud Functions for Firebase
If you're just getting started with the Firebase SDKs for Cloud Functions, try following our step-by-step Codelab and visiting the documentation. There is also a video tutorial to help get you set up using TypeScript as the recommended programming language.
We hope you find these new features helpful. While the Firebase SDKs for Cloud Functions are fully released, the Cloud Functions platform is still in beta. Stay tuned for more updates!
When I think back 10 years or so when I was writing code for a monolithic codebase written in C++, one of the worst memories I have of the time was waiting for a build to complete just to run a couple lines of new code. The pain was so common among engineers that it bore the creation of one of the all-time most popular XKCD comics:
This is a great idea, if executed safely.
My office at the time didn't have swords to play with, so waiting for a build to complete was really boring.
The pain is similar if I had to deploy my Cloud Functions code every time I want test a function. Fortunately, the Firebase CLI now has a local emulator that works for both HTTPS and other types of functions. This lets me run the functions on my development machine by emulating the events that would trigger it. You can read more about that in the documentation.
One of the emulator's handy features is the fact that it will reload any JavaScript files that change over time. So, if you modify the code of a function, you can test it right away in the emulator without having to restart it:
However, things become less easy when you switch from JavaScript to TypeScript. Running TypeScript on Cloud Functions requires a compilation phase to convert the TypeScript into JavaScript so that it can be executed by the emulator. It adds a bit of a hassle:
Switch to code editor, write TypeScript
Switch to command prompt, execute npm run build to generate JavaScript
Switch to emulator, run code
GOTO 1
There's an easier way to do this that doesn't involve running a command every time. The TypeScript compiler (tsc) has a special "watch mode" that lets it pick up changes to your TypeScript and automatically compile them. You can run it from the command line by changing to your functions folder and running this command:
./node_modules/.bin/tsc --watch
With this running alongside the local emulator, which automatically picks up changes to the compiled JavaScript, your development workflow goes back to this:
Switch to code editor, write TypeScript
Switch to emulator, run code
GOTO 1
And, if you're using VSCode, you can even have it directly run tsc in watch mode. You can start it up with Tasks -> Run Build Task..., then choosing "tsc: watch".
It will fire up a command shell right in the IDE, run tsc --watch, and show you the output. You'll see messages like this, along with any compilation errors, any time a TypeScript file changes:
Now, bear in mind that tsc in watch mode won't run TSLint against your code, so be sure to set that up in VSCode so you can always see its recommendations, as I showed in a prior blog post. If you're not linting your code, seriously consider setting that up, because you'll catch potential mistakes that could be costly in production.
It's December, folks, and you know what that means: holiday cheer!
The Firebase Test Lab
team is fully invested in making this season the greatest of seasons. Remember
Halloween?
What a hoot! Also, this happened:
(Note: these are actual Test Lab engineers, in costume,
actually beating each other up with foam sticks at a Halloween party.
Both get lumps of coal this year.)
We're getting ready for the holidays! So, sit back, pour yourself some eggnog,
and read about what's new for your Android testing enjoyment.
Like Robo tests? Then you'll love Robo scripts!
Many of you are using Robo to automatically test your apps in Test Lab. Since
you don't have to write any code to make it work, it's the gift
that keeps on giving. Even better, you can have it fill in specific form
fields and push buttons with some easy
configuration.
We've found that some apps require more of a nudge to navigate into the parts
that need the most testing. (Hey, even Santa needs help from a few elves!)
Now, with Robo
scripts, you can record a series of actions to take in your app, and play
that back before Robo takes over. It works a lot like Espresso
Test Recorder, except the output is a JSON file that you upload along with
your APK when running a Robo test. With these extra instructions, you can guide
your app past introductions or login screens.
Of course, your best bet to maximize the test coverage is writing Espresso
tests that drive your app. I heard that it's easier than driving
a team of reindeer!
Screenshots of a feather cluster together
Do you use the screenshots in Test Lab results to check if your app displays
correctly? It's a great way to see if you app renders "naughty or nice" on many
different types of screens. But if you test with lots of devices in a single
test matrix, it can be kind of a pain to sort through all the results to compare
the same screen among multiple devices. Now, Test Lab will cluster them
together in your test results, so you can see all the different screen sizes,
densities, and orientations from your test in a single place.
In with the new toys, out with the old
The Test Lab team is always busy at the North Pole (located at a data center in
Atlanta) bringing you new devices to test with. The latest additions are the
Sony Xperia XZ Premium, the Moto G4 Play, and the Huawei P8lite, delivered
straight to your digital stocking. However, sometimes old toys break and need
to be recycled. At the Test Lab workshop, we call that "device deprecation",
which means we take old devices out of commission as they become unreliable. To
see a (twice-checked) list of devices that are currently available, in addition
to those being deprecated, click through to this
page. Once a device is marked as "deprecated", it'll remain available for a
month, then removed.
Deprecated devices look like this in the Firebase console:
And like this in the gcloud
command line (note the "deprecated" tag in red):
You better not pout, you better not cry ‐ these devices served longer than their
expected lifetime!
Stay in touch - send us a holiday postcard!
Or, just join us on the Firebase Slack
in the #test-lab channel. We're all friendly there, so be good, for goodness
sake!
Developer Advocate
If you've been working with Cloud Functions for
Firebase, you've probably wondered how you could speed up the development of
your functions. This is possible for HTTPS type functions using the firebase
serve in the Firebase CLI, but this wasn't an option for other types of
functions. Now, local testing of all of your functions is easy with the
Firebase CLI. If you want to try out your code before you deploy it to Cloud
Functions, you can do that with the Cloud
Functions shell in the Firebase CLI starting at version 3.11.0 or later.
Here's how it works, in a nutshell. We'll use a Realtime Database trigger as an
example.
Imagine you have an existing project with a single function in it called
makeUppercase. It doesn't have to be deployed yet, just defined in your
index.js:
This onCreate database trigger runs when a new message is pushed under /messages
with a child called original, and writes back to that message a new child called
uppercased with the original value capitalized.
Now, if you can kick off the emulator shell from your command line using the
Firebase CLI:
$ cd your_project_dir
$ firebase experimental:functions:shell
Then, you'll see something like this:
i functions: Preparing to emulate functions.
✔ functions: makeUppercase
firebase>
That firebase prompt is waiting there for you to issue some commands to invoke
your makeUppercase function. The documentation
for testing database triggers says that you can use the following syntax to
invoke the function with incoming data to describe the event:
makeUppercase('foo')
This emulates the trigger of an event that would be generated when a new message
object is pushed under /messages that has a child named original with the string
value "foo". When you run this command in the shell, it will generate some
output at the console like this:
info: User function triggered, starting execution
info: Uppercasing pushId1 foo
info: Execution took 892 ms, user function completed successfully
Notice that the console log in the function is printed, and it shows that the
database path wildcard pushId was automatically assigned the value
pushId1 for you. Very convenient! But you can still specify the wildcard
values yourself, if you prefer:
After emulating this function, if you look inside the database, you should also
see the results of the function on display, with /messages/{pushId}/uppercased
set to the uppercased string string value "FOO".
You can simulate any database event this way (onCreate, onDelete, onUpdate,
onWrite). Be sure to read the docs to learn how to invoke them each correctly.
The Cloud Functions shell is currently an experimental offering, and as such,
you may experience some rough edges. If you encounter a problem, please let us
know by filing a bug
report. You can also talk to other Cloud Functions users on the Firebase Slack in the #functions channel.
Some tips for using the shell
Typing the function invocation each time can be kind of a pain, so be sure to
take advantage of the fact that you can navigate and repurpose your invocation
history much like you would your shell's command line using the arrow keys.
Also note that the shell is actually a full node REPL that you can use to
execute arbitrary JavaScript code and use special
REPL commands and keys. This can be useful for scripting some of your test
code.
Since you can execute arbitrary code, you can also dynamically load and execute
code from other files using the require() function that you're probably already
familiar with.
And lastly, if you're like me, and you prefer to use a programmer's editor such
as VS Code to write your all
JavaScript, you can easily emulate functions by sending code you want to run to
the Firebase CLI. This command will run test code from a file redirected
through standard input:
Sometimes the worst bugs to track down are the ones that seem to be impossible
to reproduce. Or worse, inconsistent performance problems that can cause "Application
Not Responding" errors in Android apps. No matter how much test code you
write, these types of errors seem to have a way of sneaking into your app and
causing problems for your users. However, with some clever use of Android's StrictMode
API, alongside Firebase Test
Lab for Android, you can find out about these problems before they reach
production!
Over the years since Android was first available, a number of best practices
have been observed for writing solid apps. For example, you shouldn't be
performing blocking I/O on the main thread, and you shouldn't store references
to Activity objects that are held after the Activity is destroyed. While it's
likely that no one will force you to observe these practices (and you may never
see a problem during development), enabling StrictMode in your app will let you
know where you've made a mistake. These are called "policy violations", and you
configure these policies with code in your app.
Personally, I don't want any of my code to cause any StrictMode violations. In
fact, I'd like to consider them bugs that are just as serious as a regular
crash, because they could crash my app in some cases! And, in fact, I
can configure all StrictMode violations to crash the app. The Java code for
that looks like this:
private static final StrictMode.ThreadPolicy FATAL_THREAD_POLICY =
new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.penaltyDeath()
.build();
private static final StrictMode.VmPolicy FATAL_VM_POLICY =
new StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.penaltyDeath()
.build();
public static void enableFatalStrictMode() {
if (shouldEnableFatalStrictMode()) {
StrictMode.setThreadPolicy(FATAL_THREAD_POLICY);
StrictMode.setVmPolicy(FATAL_VM_POLICY);
}
}
Here I have both a ThreadPolicy
(for the main thread) and a VmPolicy
(for the entire app) that look for all known violations, and will both log that
violation and crash the app. Take a look through the javadoc for those builders
to learn about all the situations they can catch.
Also notice that I'm enabling the policies conditionally. You probably don't
want an accidental violation to crash on your users in production, so the method
shouldEnableFatalStrictMode() typically looks like this, which
activates it for your debug builds only:
What I'd like to do is take this further and have StrictMode crash my app
also when it's running in Firebase Test Lab. This is handy because the
crash will fail the test, and I'll get a report about that in the test results.
For that to happen, I can change the method to query a system setting on the
device that's unique to devices in Test Lab. Note that this requires an Android
Context to obtain a ContentResolver:
Now, my instrumented tests (and the automated Robo test)
will crash with any StrictMode violations, and I'll see those very clearly in my
Test Lab
report. Here's an app that crashed in Test Lab with a StrictMode violation
during a Robo test:
This doesn't tell you exactly what happened, though. Digging into the logs in
the test report, I see the details of the violation right before the crash:
D/StrictMode(6996): StrictMode policy violation; ~duration=80 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=327743 violation=2
D/StrictMode(6996): at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1415)
D/StrictMode(6996): at java.io.UnixFileSystem.checkAccess(UnixFileSystem.java:251)
D/StrictMode(6996): at java.io.File.exists(File.java:807)
D/StrictMode(6996): at android.app.ContextImpl.getDataDir(ContextImpl.java:2167)
D/StrictMode(6996): at android.app.ContextImpl.getPreferencesDir(ContextImpl.java:498)
D/StrictMode(6996): at android.app.ContextImpl.getSharedPreferencesPath(ContextImpl.java:692)
D/StrictMode(6996): at android.app.ContextImpl.getSharedPreferences(ContextImpl.java:360)
D/StrictMode(6996): at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:167)
D/StrictMode(6996): at com.google.firebasesandbox.MainActivity.onCreate(MainActivity.java:25)
D/StrictMode(6996): at android.app.Activity.performCreate(Activity.java:6982)
D/StrictMode(6996): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
D/StrictMode(6996): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
D/StrictMode(6996): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
D/StrictMode(6996): at android.app.ActivityThread.-wrap11(Unknown Source:0)
D/StrictMode(6996): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
D/StrictMode(6996): at android.os.Handler.dispatchMessage(Handler.java:105)
D/StrictMode(6996): at android.os.Looper.loop(Looper.java:164)
D/StrictMode(6996): at android.app.ActivityThread.main(ActivityThread.java:6541)
D/StrictMode(6996): at java.lang.reflect.Method.invoke(Native Method)
D/StrictMode(6996): at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
D/StrictMode(6996): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
So we've discovered here that an activity's onCreate() called
getSharedPreferences(), and this method ends up reading the device
filesystem. Blocking the main thread like this could be the source of some jank
in the app, so it's worth figuring out a good way to move that I/O to another
thread.
Now we have a way to check for StrictMode violations during a test, but there's
still a few more details to work out.
When should I enable StrictMode?
If you do this immediately when the app launches (in an Application subclass or
a ContentProvider), there is a subtle bug in some
versions of Android that may need to be worked around in order to avoid
losing your StrictMode policies when the first Activity appears.
When should I disable StrictMode?
There might be some times when you know there is a violation, but you can't fix
the code right away for whatever reason (for example, it's in a library you
don't control). In that case, you might want to temporarily suspend the
crashing behavior and simply log it instead. To temporarily suspend the crash,
you can define a couple other non-fatal policies and swap them in where you know
there's an issue to ignore:
private static final StrictMode.ThreadPolicy NONFATAL_THREAD_POLICY =
new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build();
private static final StrictMode.VmPolicy NONFATAL_VM_POLICY =
new StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build();
public static void disableStrictModeFatality() {
StrictMode.setThreadPolicy(NONFATAL_THREAD_POLICY);
StrictMode.setVmPolicy(NONFATAL_VM_POLICY);
}
Similarly, you may not want to track all the possible violations all the time.
In that case, you might want to instruct the policy builder to only detect a
subset of the potential issues.
Get out there and test your app!
While StrictMode violations are not always the worst thing for your users, I've
always found it educational to enable StrictMode in order to find out where my
app might be causing problems. In combination with Firebase Test lab, it's a
great tool for maximizing the quality of your app.
Testing your application is a great way to help maximize its quality, and many
of you know that Firebase Test Lab for
Android has some useful tools for testing Android apps. If you're the type
of engineer who likes to maximize test coverage by writing instrumented
tests (and regular unit
tests), you can send those to Test Lab for execution. Even if you don't
like writing tests, you have some options. You can record instrumented tests by
interacting with your app using Espresso
Test Recorder in Android Studio. And there's almost no effort required at
all to run a Robo test that
automatically crawls your app.
These tests are helpful for data-driven apps because there are robust test
frameworks that understand how to navigate the Android platform widgets used to
receive input and display data on screen. However, most games don't work with
platform widgets. Games typically take over the screen using their own UI
elements, and provide their own touch controls. As a result, it's extremely
difficult to write instrumented tests for testing games, and Robo test won't
know how to navigate the game at all.
To help deal with these challenges with testing games, the Test Lab team has
come up with a way for game developers to test their games effectively across
many of the devices that Test Lab offers. It's a new type of test called a Game Loop Test,
and it's available today in beta.
How does a Game Loop test work?
If you've seen arcade video games operate, you know that they're always showing
something on screen, typically some automated demo of the game, called "attract
mode". With Firebase Test Lab, game developers can now use this concept of
attract mode to construct test scenarios, and Test Lab will arrange for those
scenarios to be invoked in sequence. This gives developers the opportunity to
test a wide variety of game levels and situations in a single test run on all
the devices provided by Test Lab. If there are any problems with a scenario,
you'll find out which ones are problematic, so you can focus your efforts on
improving that specific case.
Even better, Test Lab now provides performance data with every report. Of
particular interest to game developers is a graph of the rendered frame rate
over time throughout the test, tied to a video of the device display. This
helps you quickly identify the parts of your game that aren't rendering at an
acceptable FPS.
In addition to FPS, there are other performance
metrics available for all apps. You'll be able to see your app's CPU
utilization, memory usage, and network ingress and egress. Notice how you can
jump directly to the point in the video where you're observing performance
problems.
If you're a game developer, now is a great time to check out Firebase Test Lab for
Android to see what it can do to help the quality of your game.