--- sidebar_position: 6 slug: /modeling/testing description: Testing Models --- import { DocumentationNotice, ProductName, ProductNameFormat, RelatedSection, } from '@components/Docs'; # Testing Models Every model should be tested before deployment to ensure your authorization model is correctly designed. The `.fga.yaml` contains tests for authorization models. If you are using Visual Studio Code as your IDE, install the [OpenFGA extension](https://marketplace.visualstudio.com/items?itemName=openfga.openfga-vscode) to enable syntax coloring and validation. For complete details on the `.fga.yaml` store file format, see [Store File Format](./store-file-format.mdx). ## Define the model and tuples `.fga.yaml` files have the following top level items: | Object | Description | | -------- | -------- | | `name` (optional) | A descriptive name for the test file | | `model` or `model_file` | An model or a reference to an external model file in `fga`, `json` or `mod` format | |`tuples` or `tuple_file` or multiple `tuple_files` (optional) | A set of tuples or a reference to an external tuple file in `json`, `yaml` or `csv` format. These are considered for all tests. | |`tests` | A set of tests that verify the return values of API calls | The example below defines a model and tuples: ```yaml name: Model Tests # optional # model_file: ./model.fga # you can specify an external .fga file, or include it inline model: | model schema 1.1 type user type organization relations define member : [user] define admin : [user with non_expired_grant] condition non_expired_grant(current_time: timestamp, grant_time: timestamp, grant_duration: duration) { current_time < grant_time + grant_duration } # You can provide relationship tuples in one of the following ways: # - As a single external file using 'tuple_file' # - As multiple external files using 'tuple_files' # - Inline directly using 'tuples' # # Examples: # tuple_file: ./tuples.yaml # Single external file # tuple_files: # Multiple external files # - ./tuples_2.yaml # - ./tuples_3.yaml tuples: # Inline tuple definitions go here # Anne is a member of the Acme organization - user: user:anne relation: member object: organization:acme # Peter has the admin role from February 2nd 2024 0AM to 1AM - user: user:peter relation: admin object: organization:acme condition: name: non_expired_grant context: grant_time : "2024-02-01T00:00:00Z" grant_duration : 1h ``` ## Write tests Always write tests to verify that the calls your application will make return the results you expect. A good test covers scenarios that verify every relation. Tests have the following structure: | Object | Description | | -------- | -------- | |`name` (optional) | A descriptive name for the test, like “Organization Membership” | |`tuple_file` or `tuple_files` or `tuples` | A set of tuples that are only considered for the test | |`check` | A set of tests for Check calls, each with a user/object and a set of assertions | |`list_objects` | A set of tests for ListObjects calls, each one with a user/type and a set of assertions for any number of relations| |`list_users` | A set of tests for ListUsers calls, each one with an object and user filter and a set of assertions for the users for any number of relations | ## Write Check tests Check tests verify the results of the [check API](../getting-started/perform-check.mdx) calls to validate access requirements for a user. Each check verification has the following structure: | Object | Description | | -------- | -------- | |`user` | The user type and user id you are checking for access | |`object` | The object type and object id related to the user | |`context` | A set of tests for contextual parameters used to evaluate [conditions](./conditions.mdx)| |`assertions` | A list of `relation:expected-result` pairs | |`: ` | The name of the relation you want to verify and the expected result | The following example adds multiple check verifications in every test: ```yaml tests: - name: Test check: - user: user:anne object: organization:acme assertions: member: true admin: false - user: user:peter object: organization:acme context: current_time : "2024-02-01T00:10:00Z" assertions: member: false admin: true ``` ## Write List Objects tests A good test covers scenarios that specify every relation for every object type that your application will need to call the [list-objects API](../getting-started/perform-list-objects.mdx) for. The following verifies the expected results using the `list_objects` option in tests: ```yaml list_objects: - user: user:anne type: organization assertions: member: - organization:acme admin: [] - user: user:peter type: organization context: current_time : "2024-02-01T00:10:00Z" assertions: member: [] admin: - organization:acme ``` The example above checks that `user:anne` has access to the `organization:acme` as a member and is not an admin of any organization. It also checks that `user:peter`, given the current time is February 1st 2024, 0:10 AM, is not related to any organization as a member, but is related to `organization:acme` as an admin. ## Write List Users tests List users tests verify the results of the [list-users API](../getting-started/perform-list-users.mdx) to validate the users who or do not have access to an object Each list users verification has the following structure: | Object | Description | | -------- | -------- | |`object` | The object to list users for | |`user_filter` | Specifies the type or userset to filter with, this must only contain one entry | |`user_filter.type` | The specific type of results to return with response | |`user_filter.relation` | The specific relation of results to return with response. Specify to return usersets (optional) | |`context` | A set of tests for contextual parameters used to evaluate [conditions](./conditions.mdx)| |`assertions` | A list of assertions to make | |`` | The name of the relation you want to verify | |`.users` | The users who should have the stated relation to the object | In order to simplify test writing, the following syntax is supported for the various object types included in `users` from the API response: * `:` to represent a userset that is a user * `:#` to represent a userset that is a relation on a type * `:*` to represent a userset that is a type bound public access for a type The following is an example of using the `list_users` option in tests: ```yaml list_users: - object: organization:acme user_filter: - type: user context: current_time : "2024-02-02T00:10:00Z" assertions: member: users: - user:anne admin: users: [] ``` The example above checks that the `organization:acme`, given the current time is February 2nd 2024, it has 'user:anne' as a `member`, nobody as an `admin`. If we tried with current time being February 1st 2024, then `user:peter` would be listed as an `admin` ## Testing with Modular Models If you are using [Modular Models](./modular-models.mdx), you need to use the `fga.mod` as the `model_file`. You can define tests for each model in separate `.fga.yaml` files, all of which should reference the common `fga.mod` model. Shared relationship tuples can be placed in a separate file and included using the `tuple_file` option. If needed, you can split tuples across multiple shared files and include them with the `tuple_files` option. Additionally, each `.fga.yaml` file can include module-specific tuples inline. ## Running tests Tests are run using the `model test` CLI command. For instructions on installing the OpenFGA CLI, visit the [OpenFGA CLI Github repository](https://github.com/openfga/cli). ```shell fga model test --tests .fga.yaml ``` When all tests pass, a summary with the number of tests passed is displayed. When a test fails, a line for every test is displayed. ```shell $ fga model test --tests docs.fga.yaml # Test Summary # Tests 2/2 passing Checks 4/4 passing ListObjects 4/4 passing $ fga model test --tests docs.fga.yaml (FAILING) : ListUsers(1/2 passing) ⅹ ListUsers(object={Type:organization Id:acme},relation=member,user_filter={Type:user Relation:}, context:&map[current_time:2024-02-02T00:10:00Z]): expected={Users:[user:ann]}, got={Users:[user:anne]} --- # Test Summary # Tests 1/2 passing Checks 4/4 passing ListObjects 4/4 passing ``` ## Running tests using GitHub Actions Use the [OpenFGA Model Testing Action](https://github.com/marketplace/actions/openfga-model-testing-action) to run tests from CI/CD flows in GitHub. Set the path to the `.fga.yaml` file as the `store-file-path` parameter when configuring the action: ```yaml name: Test Action on: workflow_dispatch: pull_request: branches: - main jobs: test: name: Run test runs-on: ubuntu-latest steps: - name: Checkout Project uses: actions/checkout@v4 - name: Run Test uses: openfga/action-openfga-test@v0.1.0 with: store-file-path: ./example/model.fga.yaml ``` ## Related Sections