> For the complete documentation index, see [llms.txt](https://docs.mozark.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.mozark.ai/no-code-studio/authoring-tests/test-data.md).

# Test data

One test case, different data on every device. This is what turns a recording into a test suite.

## The model: lookup by key

Read this section before anything else on this page. It is the one thing about App Studio that is genuinely different from what most people expect.

{% hint style="warning" %}
Test data binds by **looking up a row on a key column**. It does **not** iterate over rows.
{% endhint %}

Suppose you have a data set of movie names:

| device\_serial    | movie\_name  |
| ----------------- | ------------ |
| 0002721R60000D2A  | Interstellar |
| R5CT10ABCDE       | The Batman   |
| 00008110-001E2D3C | Dune         |

Run one test case on those three devices and you get **one run per device**, each reading **its own row**:

```
Pixel 7      (0002721R60000D2A)   ──► types "Interstellar"
Galaxy S23   (R5CT10ABCDE)        ──► types "The Batman"
iPhone 14    (00008110-001E2D3C)  ──► types "Dune"
```

Three devices, three different inputs, one test case, one run.

What it does **not** do is run three times on one device, once per row. If you want a value used on a particular device, put it in that device's row.

### Why it works this way

It maps onto how a device fleet actually works. Each device runs the suite in parallel, and each one knows its own serial number. The key column is how a row finds its device.

### What you can key on

The three reserved context names are supplied automatically for every run:

| Key column        | Matches on                                | Use it for                                    |
| ----------------- | ----------------------------------------- | --------------------------------------------- |
| `device_serial`   | The serial of the device running the test | Different data per device. The common case    |
| `test_name`       | The name of the test case being run       | Different data per test, same on every device |
| `iteration_index` | The iteration number                      | Repeat runs                                   |

## Variables

A variable is a named value written `${name}`. There are four sources.

| Source  | Value comes from                                | Created                                                                    |
| ------- | ----------------------------------------------- | -------------------------------------------------------------------------- |
| Static  | A literal you typed                             | By you, on the Variables screen                                            |
| Lookup  | A cell in a data set, matched on the key column | Automatically, when you bind a column to a step                            |
| Runtime | Read off the screen by an earlier step          | Automatically, by *Extract value into variable*                            |
| Context | Injected by the runner                          | Always available: `${test_name}`, `${device_serial}`, `${iteration_index}` |

Most of the time you never visit the Variables screen. Variables are created for you as a side effect of binding data or capturing a value. The screen is where you go to see what exists, check where a value comes from, or create a static one.

### Naming rules

Names must start with a letter or underscore, and contain only letters, digits and underscores. Names share one namespace across variables, elements and data sets, so two things cannot have the same name.

| Valid        | Invalid                     |
| ------------ | --------------------------- |
| `movie_name` | `movie name` (space)        |
| `order_id`   | `2nd_order` (leading digit) |
| `_temp`      | `order-id` (hyphen)         |

### Create a static variable

**Variables → New variable.** Give it a name and a value:

<figure><img src="https://952269762-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FRBaaAIJoDzwDclOOVa8Z%2Fuploads%2FjAUV3lKzGJpIoQglviSC%2F24-new-variable.png?alt=media&amp;token=1b72671b-8276-4228-9636-cf1322c0c0a6" alt=""><figcaption></figcaption></figure>

```
Name    support_email
Value   support@example.com
```

Use it anywhere with `${support_email}`. Change it in one place and every test picks up the new value on its next run.

## Create a data set

**Data Sets → New data set.**

1. Name it, for example `movie_names`.
2. Enter the value columns (comma-separated), for example movie\_name.
3. Choose which row a test reads: Per device (key column device\_serial) or Per test case (key column test\_name).
4. Save, then open it and fill in the grid.

### Import instead of typing

The grid accepts a CSV or Excel file (`.csv`, `.xlsx`, `.xls`). Column headers become columns. Existing spreadsheets of test data usually drop straight in.

<figure><img src="https://952269762-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FRBaaAIJoDzwDclOOVa8Z%2Fuploads%2FaPyHeSAQZLSyN3fDy8vc%2F08-new-data-set.png?alt=media&amp;token=1b9906b5-2200-418b-bd7e-d32ca21661cc" alt=""><figcaption></figcaption></figure>

### Getting device serials

The serial in your key column has to match the device that will run the test. You can read a device's serial from the Test Creation screen, or from the Info tab of any past session in Test Reports.

### Secret columns

Mark a column **secret** to mask its values. Use it for passwords, PINs and card numbers.

| device\_serial   | username                | password 🔒 |
| ---------------- | ----------------------- | ----------- |
| 0002721R60000D2A | <test.user@example.com> | ••••••••    |

Secret values are masked in the UI and in reports. They still reach the device (the app under test needs them to work), so treat a secret column as "not shoulder-surfable", not as a vault.

## Bind a data-set column to a step

This is the whole point. Walkthrough, using the movie example.

**Goal:** an **Enter Text** step that types a different movie name on every device.

1. **Record the step normally.** On a live device, right-click the search field → **Enter Text**. Type anything for now, or leave it empty.
2. **Focus the Text field.** Click into the step's **Text** parameter. The field you have focused is where the next inserted reference will land.
3. **Open the data-set picker.** Click the **data set** button at the top of the step list. If the project has one data set, you go straight to its columns. Otherwise pick the data set first, for example `movie_names`. The picker lists the **value columns** only. Your key column is not offered, because that is the value you are matching *on*, not a value you are reading. Next to each column, the picker shows the value from **the row this device will actually match**, not simply the first row. What you see is what will be typed.
4. **Insert.** Select `movie_name` and click **Insert**. The step's Text field becomes `${movie_name}`.

That is it. Behind the scenes App Studio created a **lookup variable** for you:

```
Name     movie_name
Source   Lookup
Rule     read movie_name from movie_names
         where device_serial = the device running the test
```

You can see it on the **Variables** screen. Bind the same column again from another step and the existing variable is reused. You never end up with five variables pointing at the same cell.

5. **Mix it with literals.** A field takes any combination of text and references:

```
Search: ${movie_name}
Hello ${first_name}, order ${order_id} is ready
```

## Run it with data

1. Go to the **Test Suites → New test suite**.
2. Select your test case.
3. Select the devices, the ones whose serials are in the data set.
4. Select the **Application**.
5. **Test data**: see the note below.
6. **Run now**.

### About the Test data dropdown

If your data set is keyed on `device_serial`, App Studio finds it automatically. You do not have to select it: the columns your test references are loaded per device either way.

Select it explicitly when:

* the data set is keyed on something other than `device_serial`, or
* you want to be certain which data set a run used, or
* you have several data sets and want to be unambiguous.

Selecting it never hurts. It is safe to always pick the data set your test uses.

### What you get on the run

You do not have to do anything else. When the test runs, each device looks up its own row and every `${column}` you bound is filled in before the first step touches the app.

If you want to see exactly what a test will do with its data, download its bundle from the **Test Cases** screen. Your data grids are included, as saved. See [Running tests](https://docs.mozark.ai/no-code-studio/running-and-reporting/running-tests).

## Runtime variables: carrying a value between steps

Data sets supply values you know in advance. **Runtime variables** capture values the app generates.

1. Right-click the element showing the value.
2. **All Commands → Tools → Extract value into variable.**
3. Name it, for example `order_id`.

Any later step can use `${order_id}`. A runtime variable only exists during the run, and only after the step that captured it. Using it earlier raises a scope warning on the step.

## Common patterns

### Different credentials per device

Key: `device_serial`. Devices never collide over the same account, so tests can run in parallel.

| device\_serial   | username                | password 🔒 |
| ---------------- | ----------------------- | ----------- |
| 0002721R60000D2A | <test.user@example.com> | ••••••••    |

### Different expectations per test case

Key: `test_name`. Both tests use the same recorded steps and the same modules; only the data differs.

| test\_name           | search\_term | expected         |
| -------------------- | ------------ | ---------------- |
| Search finds a match | Interstellar | 1 result         |
| Search finds nothing | Zzzzqqq      | No results found |

### An environment value shared by everything

A static variable, not a data set:

```
Name   base_url
Value  https://staging.example.com
```

## Troubleshooting test data

| Symptom                                                    | Cause                                                                               | Fix                                                                       |
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| Run rejected: *Some ${references} could not be resolved*   | A step uses a name nothing supplies, usually a renamed variable or a deleted column | The unresolved names are listed under the form. Fix them and submit again |
| A step types nothing, or types the literal `${movie_name}` | No row matched this device                                                          | Check the serial in the key column matches the device exactly             |
| Every device types the same value                          | The data set is keyed on something constant                                         | Key it on `device_serial`                                                 |
| The picker will not let you insert                         | The data set has no key column                                                      | Open it and set one                                                       |
| A number comparison behaves oddly                          | Numeric comparisons use whole numbers                                               | Use integers, not decimals                                                |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.mozark.ai/no-code-studio/authoring-tests/test-data.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
