思ってることってこんなもんだよ
How to use the End-to-End testing library for Google Assistant apps

There are many apps in the world. Currently, most apps have a graphical user interface. But, most developers have a problem how to test the user interface. Especially, it is difficult to do an End-to-End test for GUI apps. Some solutions to do the End-to-End test have been provided, but the End-to-End testing is a big theme in the information industry.

In all developers who are building apps for the Google Assistant, the End-to-End testing is very important thing. Nick Felker (Google Developer Advocate) has started a project to bring the End-to-End testing for apps of the Google Assistant. The project is actions-on-google-testing-nodejs. The developers can write codes to do the End-to-End test with the library. Nick, thank you for the great work!

I would like to show you how to use this library and how to write the End-to-End test code with the library here.

Prepare a new directory and files

First, you need to create a new directory. The directory will have some files to do the End-to-End testing.

$ cd $HOME
$ mkdir actions-test
$ cd actions-test

Create a new node project

Next, execute the npm command to create and initialize a new node project. You can put any name for the project. Basically, you can apply default values for each question.

$ npm init
<Press enter, enter, ...>

After creating the new project, you can see the package.json file was created.

Install dependent libraries

Now, you have the node project to build the End-to-End test environment. It is a time to install the actions-on-google-testing library and other dependent libraries by the following command:

$ npm install actions-on-google-testing --save
$ npm install chai --save

We install the chai library to use each assertion function here.

Create a new AoG project

Here, lets leave from your terminal and go to your web browser. To use the Google Assistant for testing, you need to create a new Actions on Google project.

Visit the Actions on Google console. Then, click the Add/import project button.

Enter your project name, and click the CREATE PROJECT button.

The actions-on-google-testing library needs an AoG project which applied Device registration. You can find the Device registration menu item on the bottom of the page. Click the Device registration menu item.

On the next page, click the REGISTER MODEL button.

After clicking the button, you will see the popup dialog. Fill in the following fields. Actually, these field values are not important.

  • Product name - Any product name (ex. “my product”)
  • Manufacturer name - Any manufacturer name (ex. “my company”)
  • Device type - Light (Of course, you can select other item)

Then, click the REGISTER MODEL button.

Download the credential file

If the Device registration is successfully, you can download the credential file from the next page. To download the file, click the Download credentials.json button.

Specify the directory which was created by the previous step as the target directory to download the file. Then, click the NEXT button.

The next page is to specify traits. But, it is not necessary here. Click the SKIP.

After clicking the SKIP, you will see the registered device model on the next screen like the following:

Enable Google Assistant API

The actions-on-google-testing library uses the Google Assistant API to communicate with the Google Assistant. Therefore, you need to enable the Google Assistant API for your project. When creating the AoG project, the GCP project also has been created at the same time. You can enable the API from the GCP console.

With your web browser, access to the following URL:

https://console.cloud.google.com/apis/library/embeddedassistant.googleapis.com?q=Assistant

If your target project is not selected, you need to select the target project on the top of the page. Then, click the ENABLE button to enable the Google Assistant API.

Convert the credential file

You need to convert the downloaded credential file. For instance, the access token is necessary to use the Google Assistant API. In the actions-on-google-testing library, the convert tool has been providing. Run the following command to convert the credential file.

$ mkdir test
$ node ./node_modules/actions-on-google-testing/generate-credentials.js ./credentials.json

The generate-credentials.js command outputs the URL to authorize to use the Google Assistant API (the actions-on-google-testing library communicates with the Google Assistant via the Google Assistant API).

Reading data for project "testing-actions-*****"
Please go to the following link to authorize, then copy the code below
https://accounts.google.com/o/oauth2/v2/auth?...
Authorization code: 

Open the URL on your web browser. First, you need to select your Google account.

Then, authorize to use the Google Assistant API.

After the authorization, you will see the authorization code on the next page. Copy the code, and paste your terminal. The access token and other parameters will be written in the test/test-credentials.json file.

Saved user credentials in "test/test-credentials.json"
You are now ready to run tests
Make sure you DO NOT commit this file

Write your test code

Finally, you got the End-to-End testing environment with the actions-on-google-testing library. Now you can start writing your test code. Here, create the test code as the file named test.js.

$ vi test.js

Load dependent modules

In the actions-on-google-testing, you need to load some dependent modules. And, here, use chai library to assert each result.

Write the following code into the test.js file:

"use strict";
  
const {ActionsOnGoogleAva} = require("actions-on-google-testing");
const {expect} = require("chai");
const testCredentials = require("./test/test-credentials.json");
const action = new ActionsOnGoogleAva(testCredentials);

Start a conversation

Then, you need to start a test and to start a conversation with your app you want to test. To start a test, you can use the startTest() function. Also, to start a conversation, you can use the startConversation() function. The code will be like the following:

action.startTest("Happy path", action => {
  return action.startConversation()
    .then(
      ...
    );
});

Probably, you will use the startTest() function for each test scenario. For example, the sample code above defines the “Happy path” scenario. You will be able to also define other scenario named “Invoke help” and so on.

When you don’t specify any arguments for the startConversation() function, the actions-on-google-testing library tries to invoke a test app. For example, the “my test app” is used as the app name in en-US locale. That is, the actions-on-google-testing says “Talk to my test app” to invoke an app.

Specify the app name explicitly

When you have already registered some app name, you cannot invoke the app with the “my test app”. Instead, you can use the startConversationWith() function to invoke your app using the app name. That is, you can specify the app name as the first argument for the startConversationWith() function. The following code invokes an app with the “Personal Chef” name.

action.startTest("Happy path", action => {
  return action.startConversationWith("Personal Chef")
    .then(
      ...
    );
});

Specify the invocation phrase

If your app supports an explicit invocation, you need to specify some invocation phrase at invoking your app. Both the startConversation() and startConversationWith() functions are supporting the invocation phrase. You can use them as like the following:

  • startConversation("to find a homemade cannoli recipe") - “Talk to my test app to find a homemade cannoli recipe”
  • startConversationWith("Personal Chef", "to find a homemade cannoli recipe") - “Talk to Personal Chef to find a homemade cannoli recipe”

Say something to your app

In your test case, you need to say something phrase to your app. The actions-on-google-testing library provides you the send() function to say something from your test code to your app. The send() function is a flexible function. Actually, the startConversation() and startConversationWith() call the send() function internally to say the phrase to invoke your app.

You can use the send() function as like the following:

action.startTest("Happy path", action => {
  return action.send("Talk to Personal Chef")
    .then(
      ...
    );
});

Their functions send some phrase to your app. And, your app returns each response. You can receive the responses with a callback function passed to the then() function. Using the then() function, you can continue the conversation by sending phrases on after another as like the following:

action.startTest("Happy path", action => {
  return action.startConversationWith(
      "Personal Chef", "to find a homemade cannoli recipe")
    .then(response => {
      return action.send("Start the cooking");
    })
    .then(response => {
      return action.send("Next step");
    })
    .then(response => {
      return action.send("Ok, thanks");
    });
});

Validate responses

Callback functions passed the then() function are called with each responses from the app. Each response has some properties including the textToSpeech, the ssml, the suggestions and so on. In your test code, you can validate each values. The following code validates each response value with the chai library.

action.startTest("Happy path", action => {
  return action.startConversationWith(
      "Personal Chef", "to find a homemade cannoli recipe")
    .then(response => {
      expect(response.textToSpeech[0]).to.have.string(
          "Ok, let's start the cooking of cannoli. Are you ready?"); 
      expect(response.micOpen).to.be.true;
      return action.send("Start the cooking");
    })
    .then(response => {
      expect(response.textToSpeech[0]).to.have.string(
          "Thank you. First step is ...");
      expect(response.micOpen).to.be.true;
      return action.send("Next step");
    })
    .then(response => {
      expect(response.textToSpeech[0]).to.have.string(
          "... That's all. Would you find other recipe?");
      expect(response.micOpen).to.be.true;
      return action.send("No, thanks");
    })
    .then(response => {
      expect(response.textToSpeech[0]).to.have.string("Bye");
      expect(response.micOpen).to.be.false;
    })
});

Internationalization

The actions-on-google-testing library supports an internationalization. By default, the “en-US” locale is applied. The Google Assistant has been supporting many languages. Of course, invocation triggers are different from each language. The actions-on-google-testing library has currently supported 22 languages. The language list is the following:

  • en-US, en-GB, en-AU, en-SG, en-CA, fr-FR, fr-CA, ja-JP, de-DE, ko-KR, es-ES, es-419, pt-BR, it-IT, ru-RU, hi-IN, th-TH, id-ID, da-DK, no-NO, nl-NL, sv-SE

The actions-on-google-testing library provides the setLocale() function to set the locale you want to test. If you set the “fr-FR” locale with the setLocale() function and you use the startConversation() function, the actions-on-google-testing library will say the following invocation phrase in French.

  • “Parler avec mon application test”

The following code shows you how to use the setLocale() function:

action.setLocale("fr-FR");
action.startTest("Happy path", action => {
  return action.startConversationWith("Chef personnel")
    .then(response => {
      expect(response.textToSpeech[0]).to.have.string(
          "Bienvenue dans l'application Personal Chef. Que veux-tu cuisiner?");
      return action.send("Du boeuf.");
    })
    ...
});

How to run the test code

The actions-on-google-testing library uses the ava JavaScript test runner. When installing the actions-on-google-testing library with the npm command, the ava library is installed at the same time. If you create the file for testing named test.js, you can run the test code by the following command:

$ ./node_modules/.bin/ava -c 1 -s ./test.js

If the execution succeeded, you will see the output log like the following:

** Starting test Happy path **
> Talk to Hexadecimal conversion about 10
{ micOpen: false,
  textToSpeech: 
   [ '<speak>The decimal number 10 is a <say-as interpret-as="char...' ],
  displayText: 
   [ 'The decimal number 10 is a a hexadecimal number.The hex...' ],
  ssml: [],
  suggestions: [] }
test passes
> cancel
{ micOpen: false,
  textToSpeech: [],
  displayText: [],
  ssml: [],
  suggestions: [] }
test ends

  1 passed

Conclusion

The actions-on-google-testing library is still a developer preview. Therefore, you can propose your idea anytime. Of course, breaking changes are also welcomed. As the ecosystem of the Google Assistant and the Actions on Google, this library is very important. I expect that many developers post many proposals, send many pull requests and send a lot of feedback.