export const meta = {
  title: `Mocking and testing`,
  description: `Learn how to quickly test and debug without pushing all changes in your Amplify project to the cloud. Use local mocking and testing for certain categories including API (AWS AppSync), Storage (Amazon DynamoDB and Amazon S3), and Functions (AWS Lambda).`,
};

  

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

<InternalLinkButton href="/start">
  <span slot="text">Get Started</span>
</InternalLinkButton>

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.

<Callout>
Java is required on your development workstation to use Local Mocking in Amplify
</Callout>

> **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](https://github.com/aws-amplify/amplify-cli/issues/7411), please try using the official openJDK 16.0.1 from the Java website: [JDK Download](https://jdk.java.net/16/)

[Blog walk-through with sample app](https://aws.amazon.com/blogs/mobile/amplify-framework-local-mocking/).

## 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:

```bash
amplify init
amplify add api # Select GraphQL, use API key
amplify 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](#android-config).

If you do not wish to test your app locally, you can still use the [local GraphQL console](#graphql-local-console) as well as [edit, debug, and test your VTL resolvers](#graphql-resolver-debugging) 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:

- [@auth](/cli/graphql/authorization-rules)
- [@primaryKey, @index](/cli/graphql/data-modeling)
- [@hasOne, @hasMany, @belongsTo, @manyToMany](/cli/graphql/data-modeling)
- [@function](/cli/graphql/custom-business-logic#lambda-function-resolver)
- [@searchable](/cli/graphql/search-and-result-aggregations/)

### 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](/cli/usage/lambda-triggers/#as-a-part-of-the-graphql-api-types-with-model-annotation),
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](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_GetRecords.html).

The environment variables that are listed below in [Function mock environment variables](/cli/usage/mock/#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](/cli/graphql/search-and-result-aggregations/).

We create the following artifacts when you mock the API with searchable models:
- [OpenSearch](https://opensearch.org/) 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`.

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

> __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:

```bash
amplify init
amplify add storage # This will prompt you to add auth
amplify push
amplify mock storage
```

To use an iOS application with the local S3 endpoint you will need to [modify your Info.plist file](#ios-config). To use an Android application with the local S3 endpoint you will need an extra [configuration in your AndroidManifest.xml](#android-config).

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](/cli/graphql/custom-business-logic#lambda-function-resolver) can be mocked using [`amplify mock api`](#api-mocking-setup).
For example, if you have a function named **quoteOfTheDay** and a schema like:

```graphql
type Query {
  getQuote: String @function(name: "quoteOfTheDay-${env}")
}
```

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

```graphql
query {
  getQuote
}
```

### Function mock environment variables

`amplify mock function`populates 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](#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](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime) 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](https://github.com/aws-amplify/amplify-cli/blob/main/packages/amplify-util-mock/src/utils/lambda/populate-lambda-mock-env-vars.ts#L39)

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:

```ini
AWS_REGION=us-fake-1
DDB_ENDPOINT=http://localhost:62224
AWS_ACCESS_KEY_ID=fake
AWS_SECRET_ACCESS_KEY=fake
API_<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:

```ini
API_FLIGHTSTATS_AIRPLANETABLE_NAME=AirplaneTable
```

2. Configure the DynamoDB client in your function as follows:

```js
const ddb = new aws.DynamoDB.DocumentClient({
  endpoint: process.env.DDB_ENDPOINT,
});
const result = await ddb.put({
  TableName: process.env.API_FLIGHTSTATS_AIRPLANETABLE_NAME,
  Item: {
    id: '1234567890',
    name: 'F-22',
    description: 'Cool fighter jet',
  },
}).promise();
```

3. Run `amplify mock api` to activate the local DynamoDB endpoint
4. 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:

```ini
IS_MOCK=true
```

And configure the DynamoDB client with:

```js
const clientConfig = process.env.IS_MOCK ? {
  region: 'us-fake-1',
  endpoint: 'http://localhost:62224',
  credentials: new aws.Credentials({
    accessKeyId: 'fake',
    secretAccessKey: 'fake',
  }),
} : undefined;
const 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: 

```bash
amplify mock
```

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

```console
? Select the category …  (Use arrow keys or type to filter)
❯● GraphQL API
 ○ Function
 ○ 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

```js
const awsmobile = {
  "aws_project_region": "us-east-1",
  "aws_appsync_graphqlEndpoint": "http://localhost:20002/graphql",
  "aws_appsync_region": "us-east-1",
  "aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS",
  "aws_appsync_apiKey": "da2-fakeApiId123456",
  "aws_appsync_dangerously_connect_to_http_endpoint_for_testing": true,
  "aws_cognito_identity_pool_id": "us-east-1:270445b2-cc92-4d46-a937-e41e49bdb892",
  "aws_cognito_region": "us-east-1",
  "aws_user_pools_id": "us-east-1_excPT39ZN",
  "aws_user_pools_web_client_id": "4a950rsq08d2gi68ogdt7sjqub",
  "oauth": {},
  "aws_user_files_s3_bucket": "local-testing-app-2fbf0a32d1896419b88f004c2755d084c-dev",
  "aws_user_files_s3_bucket_region": "us-east-1",
  "aws_user_files_s3_dangerously_connect_to_http_endpoint_for_testing": true
};
```

### awsconfiguration.json example

```json
"AppSync": {
  "Default": {
    "ApiUrl": "http://localhost:20002/graphql",
    "Region": "us-east-1",
    "AuthMode": "AMAZON_COGNITO_USER_POOLS",
    "ClientDatabasePrefix": "deddd_AMAZON_COGNITO_USER_POOLS",
    "DangerouslyConnectToHTTPEndpointForTesting": true
  }
},
"S3TransferUtility": {
  "Default": {
    "Bucket": "local-testing-app-2fbf0a32d1896419b88f004c2755d084c-dev",
    "Region": "us-east-1",
    "DangerouslyConnectToHTTPEndpointForTesting": true
  }
}
```

## 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](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/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"`:

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <application android:networkSecurityConfig="@xml/network_security_config" />
</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:

```xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="true">10.0.2.2</domain>
  </domain-config>
</network-security-config>
```

Then use a [Build Variant](https://developer.android.com/studio/build/build-variants) 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](https://developer.android.com/studio/build/manifest-merge).

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**.

```xml
<application
  android:icon="@mipmap/ic_launcher"
  android:label="@string/app_name"
  android:theme="@style/AppTheme"
  android:usesClearTextTraffic="true" >

  <!--other code-->
</application>
```

## GraphQL Local Console

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

```bash
amplify 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](https://github.com/OneGraph/graphiql-explorer) 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](https://github.com/AlexCovizzi/vscode-sqlite). 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:

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

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:

```console
Unknown directive "mode".

GraphQL request (1:11)
1: type Todo @mode{
             ^
2:   id: ID!

    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`.
