> For the complete documentation index, see [llms.txt](https://thryec.gitbook.io/dev-compedium/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://thryec.gitbook.io/dev-compedium/sui/object-programming/unit-testing.md).

# Unit Testing

After you define the `create` function, you can test it in Sui Move using unit tests without having to go all the way through sending Sui transactions. Since [Sui manages global storage separately outside of Move](https://docs.sui.io/learn/sui-move-diffs#object-centric-global-storage), there is no direct way to retrieve objects from global storage within Move. This poses a question: after calling the `create` function, how do we check that the object is properly transferred?

To assist easy testing in Sui Move, we provide a comprehensive testing framework in the [test\_scenario](https://github.com/MystenLabs/sui/tree/main/crates/sui-framework/packages/sui-framework/sources/test/test_scenario.move) module that allows us to interact with objects put into the global storage. This allows us to test the behavior of any function directly in Sui Move unit tests. A lot of this is also covered in our [Move testing](https://docs.sui.io/build/move/build-test#sui-specific-testing) topic.

The `test_scenario` emulates a series of Sui transactions, each sent from a particular address. You can start the first transaction using the `test_scenario::begin` function that takes the address of the user sending this transaction as an argument, and returns an instance of the `Scenario` struct representing a test scenario.

An instance of the `Scenario` struct contains a per-address object pool emulating Sui's object storage, with helper functions provided to manipulate objects in the pool. After the first transaction completes, you can start subsequent transactions using the `test_scenario::next_tx` function that takes an instance of the `Scenario` struct representing the current scenario and an address of a (new) user as arguments.

Next, write a test for the `create` function. Tests that need to use `test_scenario` must be in a separate module, either under a `tests` directory, or in the same file but in a module annotated with `#[test_only]`. This is because `test_scenario` itself is a test-only module, and can be used only by test-only modules.

Start the test with a hardcoded test address, which gives you a transaction context as if you sent the transaction that starts with `test_scenario::begin` from this address. You can then call the `create` function, which creates a `ColorObject` and transfers it to the test address:

```rust
let owner = @0x1;
// Create a ColorObject and transfer it to @owner.
let scenario_val = test_scenario::begin(owner);
let scenario = &mut scenario_val;
{
    let ctx = test_scenario::ctx(scenario);
    color_object::create(255, 0, 255, ctx);
};
```

**Note:** There is a "`;`" after "`}`". You must include `;` to sequence a series of expressions, and even the block `{ ... }` is an expression. Refer to the [Move book](https://move-book.com/syntax-basics/expression-and-scope.html) for a detailed explanation.

After the first transaction completes (**and only after the first transaction completes**), address `@0x1` owns the object. First, make sure it's not owned by anyone else:

```rust
let not_owner = @0x2;
// Check that not_owner does not own the just-created ColorObject.
test_scenario::next_tx(scenario, not_owner);
{
    assert!(!test_scenario::has_most_recent_for_sender<ColorObject>(scenario), 0);
};
```

`test_scenario::next_tx` switches the transaction sender to `@0x2`, which is a new address different from the previous one. `test_scenario::has_most_recent_for_sender` checks whether an object with the given type actually exists in the global storage owned by the current sender of the transaction. In this code, we assert that we should not be able to remove such an object, because `@0x2` does not own any object.

**Note:** The second parameter of `assert!` is the error code. In non-test code, you usually define a list of dedicated error code constants for each type of error that could happen in production. For unit tests, it's usually unnecessary because there are too many assertions. The stack trace upon error is sufficient to tell where the error happened. You can just put `0` for assertions in unit tests.

Finally, check that `@0x1` owns the object and the object value is consistent:

```rust
test_scenario::next_tx(scenario, owner);
{
    let object = test_scenario::take_from_sender<ColorObject>(scenario);
    let (red, green, blue) = color_object::get_color(&object);
    assert!(red == 255 && green == 0 && blue == 255, 0);
    test_scenario::return_to_sender(scenario, object);
};
test_scenario::end(scenario_val);
```

`test_scenario::take_from_sender` removes the object of given type from global storage that's owned by the current transaction sender (it also implicitly checks `has_most_recent_for_sender`). If this line of code succeeds, it means that `owner` indeed owns an object of type `ColorObject`. Also check that the field values of the object match with what you set in creation. You must return the object back to the global storage by calling `test_scenario::return_to_sender` so that it's back to the global storage. This also ensures that if any mutations happened to the object during the test, the global storage is aware of the changes.

<br>
