Page updated Feb 6, 2024

Mocking and testing

It is highly recommended that you complete the Getting Started section of Amplify setup before using local mocking.

In order to quickly test and debug without pushing all changes in your project to the cloud, Amplify supports Local Mocking and Testing for certain categories including API (AWS AppSync), Storage (Amazon DynamoDB and Amazon S3), and Functions (AWS Lambda). This includes using directives from the GraphQL Transformer, editing & debug resolvers, hot reloading, JWT mocking of authorization checks, and even performing S3 operations such as uploading and downloading content.

Java is required on your development workstation to use Local Mocking in Amplify

NOTE: Currently, on Apple Silicon Macs, Amplify sometimes fails to start mocks when using certain JDK versions built for ARM processors.

If you encounter this issue, please try using the official openJDK 16.0.1 from the Java website: JDK Download

Blog walk-through with sample app.

API mocking setup

After running amplify init you can immediately add a GraphQL API and begin mocking without first pushing to the cloud. REST APIs are not yet supported. For example:

1amplify init
2amplify add api # Select GraphQL, use API key
3amplify mock api

When you run amplify mock api the codegen process will run and create any required GraphQL assets such as queries, mutations, and subscriptions as well as TypeScript or Swift classes for your app. Android requires a build step for Gradle to create required classes after the codegen process completes, as well as an extra configuration in your AndroidManifest.xml.

If you do not wish to test your app locally, you can still use the local GraphQL console as well as edit, debug, and test your VTL resolvers locally against the mock endpoint.

When adding a schema use an API Key at first to ensure everything works, though you can authenticate against a Cognito User Pool and the local testing server will honor the JWT tokens. You can also mock the JWT tokens in the local console (outlined below), however in that case you will need to do an amplify push first to create the User Pool.

When defining a schema you can use directives from the GraphQL Transformer in local testing as well as local code generation from the schema for types. The following directives are currently supported in local testing:

Mocking the Lambda triggers on @model types

If you have DynamoDB Lambda triggers set up on @model types in your GraphQL schema, by following steps listed here, then you can test those Lambda triggers locally via amplify mock or amplify mock api.

The connected Lambda triggers are automatically invoked locally if you perform a CRUD operation on the @model type using either the local DynamoDB endpoint (http://localhost:62224) or the local AppSync console (should be http://localhost:20002). The structure of the event that is sent to the lambda trigger can be found here.

The environment variables that are listed below in Function mock environment variables, are available for each invocation of the Lambda trigger.

In addition, you can use a .env file within the function directory (ie. <project root>/amplify/backend/function/<function name>/.env) to override any environment variables for local mocking.

Mocking @model types with @searchable

If you use the @searchable directive on @model types in your GraphQL schema, then you can test the search GraphQL queries that are generated for you locally via amplify mock or amplify mock api. To learn more about the search queries, refer to this guide.

We create the following artifacts when you mock the API with searchable models:

  • OpenSearch version 1.3.0 is downloaded.
  • A Python Lambda trigger is stored locally at mock-api-resources/searchable/searchable-lambda-trigger. This Lambda trigger is automatically invoked locally if you perform a CRUD operation on the searchable model types. It updates the data in the corresponding search index used by the local OpenSearch cluster.
  • A data folder to store the OpenSearch indices is created at mock-api-resources/searchable/searchable-data.

This feature is available only for Linux and Mac systems. Windows-based systems will continue to receive empty list output for search GraphQL queries.

Note: IAM authorization rules in Mock get are treated as Auth role if the request is signed with AccessKey ASIAVJKIAM-AuthRole. Otherwise the request is treated as made by an unAuth user.

Storage mocking setup

For S3 storage mocking, after running amplify init you must first run through amplify add auth, either explicitly or implicitly if adding storage first, and then run an amplify push. This is because mocking storage in client libraries requires credentials for initial setup. Note however that S3 authorization rules, such as those placed on a bucket policy, are not checked by local mocking at this time.

Once you have done an initial push you can run the mock server and hit the local endpoint:

1amplify init
2amplify add storage # This will prompt you to add auth
3amplify push
4amplify mock storage

To use an iOS application with the local S3 endpoint you will need to modify your Info.plist file. To use an Android application with the local S3 endpoint you will need an extra configuration in your AndroidManifest.xml.

For DynamoDB storage, setup is automatically done when creating a GraphQL API with no action is needed on your part. Resources for the mocked data, such as the DynamoDB Local database or objects uploaded using the local S3 endpoint, inside your project under amplify/mock-data.

Function mocking setup

After adding a function to your project with amplify add function you can test it using amplify mock function.

amplify mock function supports the following arguments:

  • <function name> - The name of the function to mock. Must immediately follow amplify mock function, eg. amplify mock function myFunctionName
  • --event "<path to event JSON file>" - Use the specified JSON file as the event to pass to the Lambda handler
  • --timeout <number of seconds> - Override the default 10 second function response timeout with a custom timeout value

Additionally, running amplify mock function allows selecting and testing multiple functions.

Function mocking with GraphQL

A GraphQL Lambda resolver connected to your schema using @function can be mocked using amplify mock api. For example, if you have a function named quoteOfTheDay and a schema like:

1type Query {
2 getQuote: String @function(name: "quoteOfTheDay-${env}")
3}

Then when running amplify mock api, the local GraphQL endpoint will invoke this function locally when running a GraphQL query such as:

1query {
2 getQuote
3}

Function mock environment variables

amplify mock functionpopulates environment variables that mimic what will be present when deployed in the cloud. Amplify parses the function's CloudFormation template and attempts to resolve any environment variables specified there (also review function mock limitations).

CloudFormation parameters will be resolved by:

  • Resolving values specified in the parameters.json file for the function
  • Resolving values specified in team-provider-info.json within the <env>.categories.function.<function name> block, where <env> is the currently checked out environment and <function name> is the function being mocked
  • AWS::Region, AWS::AccountID, AWS::StackName, and AWS::StackId are resolved by parsing the awscloudformation configuration of the current environment in team-provider-info.json
  • Parameters constructed from dependencies on other resources in the project are resolved by parsing amplify-meta.json. Additionally, if a mock API is currently running and the function depends on the API, the local API URL will replace the cloud URL

The mock environment will also populate lambda runtime environment variables in the following way:

  • AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN will be populated using the AWS credentials that the Amplify project is currently configured to use
  • _HANDLER, AWS_REGION, AWS_LAMBDA_FUNCTION_NAME, LAMBDA_TASK_ROOT, and LAMBDA_RUNTIME_DIR will be set based on the function being mocked
  • Static defaults will be specified for all other runtime environment variables. The full list of static defaults can be found here

You can also override any mock environment variables in a .env file within the function directory (ie. <project root>/amplify/backend/function/<function name>/.env).

Connecting to a mock @model table

Connect a mock function that operates on a table generated by @model to the mock table when running amplify mock api by:

  1. Create a .env file in the function directory with the following:
1AWS_REGION=us-fake-1
2DDB_ENDPOINT=http://localhost:62224
3AWS_ACCESS_KEY_ID=fake
4AWS_SECRET_ACCESS_KEY=fake
5API_<api name>_<model>TABLE_NAME=<model>Table

Replace <api name> with the name of your API and <model> with the name of your model. The environment variable name should be all capitalized. For example, if you have an API named "FlightStats" and a model defined as type Airplane @model {...}, then then last line of the .env file should be:

1API_FLIGHTSTATS_AIRPLANETABLE_NAME=AirplaneTable
  1. Configure the DynamoDB client in your function as follows:
1const ddb = new aws.DynamoDB.DocumentClient({
2 endpoint: process.env.DDB_ENDPOINT
3});
4const result = await ddb
5 .put({
6 TableName: process.env.API_FLIGHTSTATS_AIRPLANETABLE_NAME,
7 Item: {
8 id: '1234567890',
9 name: 'F-22',
10 description: 'Cool fighter jet'
11 }
12 })
13 .promise();
  1. Run amplify mock api to activate the local DynamoDB endpoint
  2. Run amplify mock function which will now connect to the mock DynamoDB table

When running in the cloud, these environment variables will have valid values except DDB_ENDPOINT which will be undefined. In this case the DynamoDB client will use the default endpoint.

When running locally, these environment variables will be populated using the local .env file which specifies the fake values required to connect to the local database.

Note: While connected to the mock database, calls to other AWS resources within the mock function will not work because the AWS credential environment variables have been overwritten with fake credentials. To call a mock table as well as other AWS services, add logic to switch between the fake credentials and the real ones in the DynamoDB client configuration. In this case, you could configure your .env file with:

1IS_MOCK=true

And configure the DynamoDB client with:

1const clientConfig = process.env.IS_MOCK
2 ? {
3 region: 'us-fake-1',
4 endpoint: 'http://localhost:62224',
5 credentials: new aws.Credentials({
6 accessKeyId: 'fake',
7 secretAccessKey: 'fake'
8 })
9 }
10 : undefined;
11const ddb = new aws.DynamoDB.DocumentClient(clientConfig);

Function mock limitations

amplify mock function does not attempt to fully simulate the Lambda runtime environment. There may be some cases where the behavior of your function when mocking differs from executing in the cloud.

For example, mock runs on your local OS and does not attempt to emulate Amazon Linux which executes your function in the cloud. Testing with amplify mock function should be used to get quick feedback on the correctness of your function but should not be used as a substitute for testing in a cloud development environment.

Mocking multiple resources

For mocking API, Storage, or Functions together, run the following:

1amplify mock

The mock should display the following prompt, depending on the mockable resources added to the project:

1? Select the category … (Use arrow keys or type to filter)
2❯● GraphQL API
3 ○ Function
4 ○ Storage

Config files

When performing operations against the local mock endpoint, the Amplify CLI will automatically update your aws-exports.js and awsconfiguration.json with the local endpoints, fake values where necessary (e.g. fake API key), and disable SSL with an explicit value (DangerouslyConnectToHTTPEndpointForTesting) to indicate the functionality is only for local mocking and testing. This happens automatically when you run amplify mock and the server is running. Once you stop the mock server the config files are updated with the correct cloud endpoints for your project and DangerouslyConnectToHTTPEndpointForTesting is removed from the config file.

aws-exports.js example

1const awsmobile = {
2 aws_project_region: 'us-east-1',
3 aws_appsync_graphqlEndpoint: 'http://localhost:20002/graphql',
4 aws_appsync_region: 'us-east-1',
5 aws_appsync_authenticationType: 'AMAZON_COGNITO_USER_POOLS',
6 aws_appsync_apiKey: 'da2-fakeApiId123456',
7 aws_appsync_dangerously_connect_to_http_endpoint_for_testing: true,
8 aws_cognito_identity_pool_id:
9 'us-east-1:270445b2-cc92-4d46-a937-e41e49bdb892',
10 aws_cognito_region: 'us-east-1',
11 aws_user_pools_id: 'us-east-1_excPT39ZN',
12 aws_user_pools_web_client_id: '4a950rsq08d2gi68ogdt7sjqub',
13 oauth: {},
14 aws_user_files_s3_bucket:
15 'local-testing-app-2fbf0a32d1896419b88f004c2755d084c-dev',
16 aws_user_files_s3_bucket_region: 'us-east-1',
17 aws_user_files_s3_dangerously_connect_to_http_endpoint_for_testing: true
18};

awsconfiguration.json example

1"AppSync": {
2 "Default": {
3 "ApiUrl": "http://localhost:20002/graphql",
4 "Region": "us-east-1",
5 "AuthMode": "AMAZON_COGNITO_USER_POOLS",
6 "ClientDatabasePrefix": "deddd_AMAZON_COGNITO_USER_POOLS",
7 "DangerouslyConnectToHTTPEndpointForTesting": true
8 }
9},
10"S3TransferUtility": {
11 "Default": {
12 "Bucket": "local-testing-app-2fbf0a32d1896419b88f004c2755d084c-dev",
13 "Region": "us-east-1",
14 "DangerouslyConnectToHTTPEndpointForTesting": true
15 }
16}

iOS config

When running against the local mock S3 server with iOS you must update your Info.plist to not require SSL when on a local network. To enable this set NSAllowsLocalNetworking to YES under NSAppTransportSecurity. This will scope the security exception to only run on localhost domains as outlined in Apple Developer documentation for NSAllowsLocalNetworking.

Android config

When running against the local mock server with Android it is recommended to use additional Build Variants, such as a Debug and Release, to enable cleartext traffic only if the app is running on your local network. This will help ensure that you do not allow unsecured HTTP traffic in your Release Build Variant.

For example, in your Android Studio project create /src/debug/AndroidManifest.xml and in this file create a network configuration file reference android:networkSecurityConfig="@xml/network_security_config":

1<?xml version="1.0" encoding="utf-8"?>
2<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3 <application android:networkSecurityConfig="@xml/network_security_config" />
4</manifest>

Then create the network configuration file /src/debug/res/xml/network_security_config.xml and restrict to only run on your localhost IP range:

1<?xml version="1.0" encoding="utf-8"?>
2<network-security-config>
3 <domain-config cleartextTrafficPermitted="true">
4 <domain includeSubdomains="true">10.0.2.2</domain>
5 </domain-config>
6</network-security-config>

Then use a Build Variant and run the Debug build and only test this setting with your local mock server. To learn more about this please see the official Android documentation.

Alternatively, if you are running a non-production application and do not want to use multiple Build Variants, you can set android:usesClearTextTraffic="true" in your AndroidManifest.xml as in the code snippet below. This is not a recommended practice. Ensure you remove this once mocking is complete.

1<application
2 android:icon="@mipmap/ic_launcher"
3 android:label="@string/app_name"
4 android:theme="@style/AppTheme"
5 android:usesClearTextTraffic="true" >
6
7 <!--other code-->
8</application>

GraphQL Local Console

To start testing, before starting your JavaScript/Android/iOS application run the following command:

1amplify mock

Alternatively, you can run amplify mock api to only mock the API category. When prompted, ensure you select YES to automatically generate queries, mutations, and subscriptions if you are building a client application.

Once the server starts it will print a URL. Open this URL in your browser (it should be http://localhost:20002) and the OneGraph GraphQL console will open up in your browser. You can use the explorer on the left to build out a query/mutation or manually type your statements in the main window. Amplify mocking will use DynamoDB Local to persist the records on your system. If you wish, you can view these in Visual Studio code with SQLite Explorer. Follow the instructions in that repo for connecting to local databases.

When your API is configured to use Cognito User Pools, the local console provides a way to change Username, Groups, and email of the bundled JWT token. These values are used by GraphQL transformers Auth directive. Edit them by clicking Auth and saving your changes, then run operations in the console to test your rules.

GraphQL Resolver Debugging

You can edit VTL templates locally to see if they contain errors, including the line numbers causing problems, before pushing to AppSync. With the local API running navigate to amplify/backend/api/APINAME/resolvers where APINAME is the logical name that you used when running $amplify add api. You will see a list of resolver templates that the Transformer generated. Modify any of them and save, and they will be immediately loaded into the locally running API service with a message Mapping template change detected. Reloading.. If there is an error you will see something such as the following:

1Reloading failed Error: Parse error on line 1:
2...son($context.result
3----------------------^

If you stop the server locally, for instance to push your changes to the cloud, all of the templates in the ../APINAME/resolvers directory will be removed except for any that you modified. When you subsequently push to the cloud these local changes will be merged with your AppSync API.

Modify schema and test again

As you are developing your app, you can always modify the GraphQL schema which lives in amplify/backend/api/APINAME/schema.graphql. You can modify any types using any of the supported directives and save this file, while the local server is still running. The changes will be detected and if your schema is valid they will be hot reloaded into the local API. If there is an error in the schema an error will be printed to the terminal like so:

1Unknown directive "mode".
2
3GraphQL request (1:11)
41: type Todo @mode{
5 ^
62: id: ID!
7
8 at GraphQLTransform.transform

Amplify libraries when configured for these categories can use the local mocked endpoints for testing your application. When a mock endpoint is running the CLI will update your aws-exports.js or awsconfiguration.json to use the mock server and once stopped they will be updated to use the cloud endpoint once you have run an amplify push.