Writing Integration Tests | MuleSoft Documentation
# Writing Integration Tests
Flex Gateway Policy Development Kit (PDK) provides an automated testing framework to detect integration regressions in your custom policy.
The integration testing framework provided by PDK only supports Flex Gateway running in Local Mode in a Docker container. If you plan to deploy or policy to Flex Gateway running in Connected Mode or to a different platform, you should still first test your policy in PDK by using Flex Gateway running in Local Mode in a Docker container.
You can run Flex Gateway in Local Mode on Docker in Windows only to develop and test your policies. However, Flex Gateway does not support Windows in production environments.   
---  
To test your policy, complete the following tasks:
  1. Review Before You Begin.
  2. Review Integration Tests Directory Structure.
  3. Register a Flex Gateway Instance in Local Mode.
  4. Configure Your Custom Policy.
  5. Create a Test Function.
  6. Run an Integration Test.
  7. Configure Services for Integration Testing.
  8. Review Service Logs.


## Before You Begin
Ensure that you have:
  * 

## Integration Tests Directory Structure
By default, the `<root-directory>/tests` directory contains the following files:
```
tests
├── common.rs
├── requests.rs
└── common
  └── logging.yaml
  └── registration.yaml
```

The `/tests` directory contains the integration tests. By default, the folder contains:
  * `requests.rs` file: An individual test module and executable.
  * `commons.rs` file: A test module that contains functionalities, API configurations, and policy configurations that must be included in every integration test module.


## Register a Flex Gateway Instance in Local Mode
To begin using the `make test` command to debug your custom policy, register a Flex Gateway in Local Mode.
For the `make test` command to work, a Flex Gateway `registration.yaml` file must exist in the `<root-directory>/tests/common` directory. Create this file by running the registration command in the directory, or move a `registration.yaml` file of a previously registered Flex Gateway to the directory.
To register Flex Gateway, see:
  * Register and Run with a Username and Password in a Docker Container
  * Register and Run with a Connected App in a Docker Container
  * Register and Run with a Token in a Docker Container


Run only the registration command. Do not run the Docker start command because the `make test` command completes this step.   
---  
Create a different registration file on each device testing the policy.
The registration file is ignored for storage in remote repositories and is listed in the `.gitignore` file located at the root of the project directory.
## Configure Your Custom Policy
Each test function requires the configuration of the API and policies for that request.
To configure the custom policy:
  1. Print the policy ID by running the `show-policy-ref-name`:
```
make show-policy-ref-name
```

  2. Copy the result.
  3. Override `{{project-name}}` in `commons.rs` with your policy ID, for example:
```
pub const POLICY_NAME: &str = "<custom-policy-id>";
```

  4. Configure the policy’s configuration parameters.
To find your configuration parameters, see the `gcl.yaml` file.


## Create a Test Function
Add a new test module by placing a new test function in an existing test module, for example `/tests/requests.rs`.
PDK test functions are `async` functions decorated with the `#[pdk_test]` attribute:
```
use pdk_test::pdk_test;
#[pdk_test]
async fn say_hello() {
}
```

Because the body is empty, this test function never fails. Run `make test` to ensure everything is working fine.   
---  
## Run an Integration Test
To run the integrations tests modules in the `/tests` folder, execute the `make test` command:
Run this test as you add additional features to your integration test to ensure proper configuration.
The `make test` command compiles the policy before running the integration tests.   
---  
## Configure Services for Integration Testing
PDK integration tests contain a composite of services running inside a Docker network. You can register service instances in a `TestComposite` object. Every service has a configuration, hostname, and instance.
The `pdk-test` crate contains all the functionality for writing integration tests for custom policies in PDK and is included in the `[dev-dependencies]` section of the `Cargo.toml` file.
### Supported Services
PDK’s test library supports the following services:
Service | Description  
---|---  
Flex Gateway service instance.  
Service binding to the Rust’s httpmock server.  
The supported services cover the majority of use cases for integration testing. It is not possible to define different types of service.
### Configure an httpmock Service
`httpmock` is a mock server that enables you to write request mocks in Rust. Configure an httpmock service by using an `HttpMockConfig` value. It is a best practice to return a `Result` from the test function. The anyhow Rust crate enables you to return different default error types.
To configure an `httpmock` service, see the following code example:
```
use pdk_test::{pdk_test, TestComposite};
use pdk_test::services::httpmock::HttpMockConfig;
#[pdk_test]
async fn say_hello() -> anyhow::Result<()> {
  // Configure HttpMock service
  let backend_config = HttpMockConfig::builder()
    .hostname("backend")
    .port(80) // Port where the service will accept requests
    .build();
  // Register HTTPBin service and start the docker network
  let composite = TestComposite::builder()
    .with_service(backend_config)
    .build()
    .await?;
  Ok(())
}
```

If the Docker engine is properly configured, this test passes. Run `make test` to ensure proper configuration.   
---  
### Configure an Alternative httpmock Docker Image
To use an `httpmock` Docker image from another source, instead of the provided default, the configuration builder provides an `image_name()` method to define it explicitly.
To configure an alternative `httpmock` Docker image, update your `HttpMockConfig::builder()` definition as in this example:
```
// Configure HttpMock service
let backend_config = HttpMockConfig::builder()
  .image_name("myrepo/httpmock") // Custom image name setting
  .hostname("backend")
  .port(80) // Port where the service will accept requests
  .build();
```

### Get a Service’s Handle
Every configured service instance in the composite has a handle for custom interaction. The handle returns the sockets where the service endpoints are available.
To find a service’s handle, see the following code example:
```
use pdk_test::{pdk_test, TestComposite};
use pdk_test::services::httpmock::{HttpMock, HttpMockConfig};
#[pdk_test]
async fn say_hello() -> anyhow::Result<()> {
  // Configure HTTPMock service
  let backend_config = HttpMockConfig::builder()
    .hostname("backend")
    .build();
  // Register HTTPMock service and start the docker network
  let composite = TestComposite::builder()
    .with_service(backend_config)
    .port(80) // Port where the service accepts requests
    .build()
    .await?;
  // Get the httpmock handle
  let httpmock: HttpMock = composite.service()?;
  Ok(())
}
```

If the `httpmock` instance is properly configured, this test passes. Run `make test` to ensure proper configuration.   
---  
### HTTP Endpoint Requests
To mock endpoints, use the httpmock When/Then API.
After mocking the endpoint, use an HTTP client to make endpoint requests.
The reqwest crate offers the most comprehensive and flexible Rust HTTP client.
To mock endpoints and make requests, see the following code example:
```
use pdk_test::{pdk_test, TestComposite};
use pdk_test::services::httpmock::{HttpMock, HttpMockConfig};
#[pdk_test]
async fn say_hello() -> anyhow::Result<()> {
  // Configure HttpMock service
  let backend_config = HttpMockConfig::builder()
    .hostname("backend")
    .port(80) // Port where the service will accept requests
    .build();
  // Register HTTPBin service and start the docker network
  let composite = TestComposite::builder()
    .with_service(backend_config)
    .build()
    .await?;
  // Get the httpmock handle
  let httpmock: HttpMock = composite.service()?;
  // Connect the mock server
  let mock_server = httpmock::MockServer::connect_async(httpmock.socket()).await;
  // Configure the endpoint mocks
  mock_server.mock_async(|when, then| {
    when.path_contains("/hello");
    then.status(202).body("World!");
  }).await;
  let base_url = mock_server.base_url();
  // Hit the endpoint
  let response = reqwest::get(format!("{base_url}/hello")).await?;
  // Assert on response
  assert_eq!(response.status(), 202);
  assert_eq!(response.text().await?, "World!");
  Ok(())
}
```
Run `make test` to ensure proper configuration.   
---  
### Configure a Flex Service Instance
Configure a `Flex` service by registering an instance of the `FlexConfig` struct in `TestComposite`. To complete the `FlexConfig` instance, you must also define a `ApiConfig` and `PolicyConfig` instance.
The `FlexConfig` struct has the following properties:
Property | Content  
---|---  
Flex Gateway version to test.  
`image_name` | Flex Gateway Docker image name, by default, "mulesoft/flex-gateway".  
Hostname of the `Flex` service, by default, "local-flex".  
`config_mounts` | A map of local directories where the `Flex` service finds configuration files. The `POLICY_DIR` and `COMMON_CONFIG_DIR` constants are defined in the `common.rs` module by default. If your test requires further configuration, add a directory with the specific configuration for the test functions.  
The `ApiConfiguration` of the API deployed to the `Flex` service instance. Call multiple times to configure multiple APIs.  
List of ports where the `Flex` service listens for requests. If any of your configuration directories included in `config_mount` directories expose an API, the port must match the ports configured in the `spec.address` property.  
The `ApiConfig` has the following properties:
Property | Content  
---|---  
Name of the API.  
The reference to the upstream service.  
Path to which the upstream requests are sent.  
Ports where the API listens for requests.  
A List of `PolicyConfig` instances applied to the API.  
The `PolicyConfig` has the following properties:
Property | Content  
---|---  
Policy to apply.  
`configuration` | Parameters for the policy.  
The following code example defines a complete `Flex` service:
```
mod common;
use common::*;
use pdk_test::{pdk_test, TestComposite};
use pdk_test::port::Port;
use pdk_test::services::flex::FlexConfig;
// Port where Flex listens for requests
const FLEX_PORT: Port = 8081;
#[pdk_test]
async fn say_hello() -> anyhow::Result<()> {
  // Configure an upstream service for the API
  let httpmock_config = HttpMockConfig::builder()
    .port(80)
    .version("latest")
    .hostname("backend")
    .build();
  // Configure a policy for the API
  let policy_config = PolicyConfig::builder()
    .name(POLICY_NAME)
    .configuration(serde_json::json!({"source": "http://backend/blocked", "frequency": 60}))
    .build();
  // Configure the API to deploy to the Flex
  let api_config = ApiConfig::builder()
    .name("ingress-http")
    .upstream(&httpmock_config)
    .path("/anything/echo/")
    .port(FLEX_PORT)
    .policies([policy_config])
    .build();
  // Configure the Flex service
  let flex_config = FlexConfig::builder()
    .version("1.6.1")
    .hostname("local-flex")
    .config_mounts([
      (POLICY_DIR, "policy"),
      (COMMON_CONFIG_DIR, "common")
    ])
    .with_api(api_config)
    .build();
  let composite = TestComposite::builder()
    .with_service(flex_config)
    .with_service(httpmock_config)
    .build()
    .await?;
}
```

### Configure an Alternative Flex Docker Image
To use a `flex` Docker image from another source, instead of the provided default, the configuration builder provides an `image_name()` method to define it explicitly.
To configure an alternative Flex Docker image, update your `FlexConfig::builder()` definition as in this example:
```
// Configure the Flex service
let flex_config = FlexConfig::builder()
  .image_name("myrepo/flex-gateway") // Custom image name setting
  .version("1.6.1")
  .hostname("local-flex")
  .config_mounts([
    (POLICY_DIR, "policy"),
    (COMMON_CONFIG_DIR, "common")
  ])
  .with_api(api_config)
  .build();
```

Run `make test` to ensure proper configuration.  
---  
### Flex Service Endpoint Requests
`Flex` service exposes endpoints with external base URLs dependent on the port. To access those URLs, the Flex service instance handle provides the `external_url()` method indexed by port, for example:
```
use pdk_test::{pdk_test, TestComposite, port::Port};
use pdk_test::services::flex::Flex;
const FLEX_PORT: Port = 8081;
async fn build_composite() -> anyhow::Result<TestComposite> {
  todo!("Configuration stuff comes here")
}
#[pdk_test]
async fn say_hello() -> anyhow::Result<()> {
  // Invoke a helper composite builder function for simplicity
  let composite = build_composite().await?;
  // Get the Flex service handle
  let flex: Flex = composite.service()?;
  // Get the URL for our configured port
  let flex_url = flex.external_url(FLEX_PORT).unwrap();
  let response = reqwest::get(format!("{flex_url}/hello")).await?;
  // Make assertions
  Ok(())
}
```

### Combine Multiple Services
Usually a PDK integration test includes a `Flex` service and at least one backend service.
The following code is the default test generated when the custom policy is created:
```
mod common;
use httpmock::MockServer;
use pdk_test::{pdk_test, TestComposite};
use pdk_test::port::Port;
use pdk_test::services::flex::{FlexConfig, Flex};
use pdk_test::services::httpmock::{HttpMockConfig, HttpMock};
use common::*;
// Directory with the configurations for the `hello` test.
const HELLO_CONFIG_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/requests/hello");
// Flex port for the internal test network
const FLEX_PORT: Port = 8081;
// This integration test shows how to build a test to compose a local-flex instance
// with a MockServer backend
#[pdk_test]
async fn hello() -> anyhow::Result<()> {
  // Configure an HttpMock service
  let httpmock_config = HttpMockConfig::builder()
    .port(80)
    .version("latest")
    .hostname("backend")
    .build();
  // Configure a policy for the API
  let policy_config = PolicyConfig::builder()
    .name(POLICY_NAME)
    .configuration(serde_json::json!({"source": "http://backend/blocked", "frequency": 60}))
    .build();
  // Configure the API to deploy to the Flex
  let api_config = ApiConfig::builder()
    .name("ingress-http")
    .upstream(&httpmock_config)
    .path("/anything/echo/")
    .port(FLEX_PORT)
    .policies([policy_config])
    .build();
  // Configure the Flex service
  let flex_config = FlexConfig::builder()
    .version("1.6.1")
    .hostname("local-flex")
    .config_mounts([
      (POLICY_DIR, "policy"),
      (COMMON_CONFIG_DIR, "common")
    ])
    .with_api(api_config)
    .build();
  // Compose the services
  let composite = TestComposite::builder()
    .with_service(flex_config)
    .with_service(httpmock_config)
    .build()
    .await?;
  // Get a handle to the Flex service
  let flex: Flex = composite.service()?;
  // Get an external URL to point the Flex service
  let flex_url = flex.external_url(FLEX_PORT).unwrap();
  // Get a handle to the HttpMock service
  let httpmock: HttpMock = composite.service()?;
  // Create a MockServer
  let mock_server = MockServer::connect_async(httpmock.socket()).await;
  // Mock a /hello request
  mock_server.mock_async(|when, then| {
    when.path_contains("/hello");
    then.status(202).body("World!");
  }).await;
  // Perform an actual request
  let response = reqwest::get(format!("{flex_url}/hello")).await?;
  // Assert on the response
  assert_eq!(response.status(), 202);
  Ok(())
}
```

## Review Service Logs
After an integration test fails, you can view the service logs to find errors. To improve the debugging experience, there is a dedicated service log in each test folder at `<root-directory>/target/pdk-test/<module-name>/<test>/<service>.log`.
For the example, the `say_hello` test has the following service log:
```
<root-directory>
├── Cargo.lock
├── Cargo.toml
├── playground
├── src
├── tests
└── target
  └── pdk-test
    └── requests
      └── say_hello
        └── <service>.log
```

## See Also
  * Debugging Custom Policies with the PDK Debugging Playground
  *