Skip to content

Commit

Permalink
doc/md: add testing functions guide (#2955)
Browse files Browse the repository at this point in the history
  • Loading branch information
hilakashai committed Jul 11, 2024
1 parent dd5cd17 commit a50d344
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 1 deletion.
8 changes: 7 additions & 1 deletion doc/md/guides/guides.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,14 @@ import { Card, CardImage } from "../components/card";
/>
<Card
name="Testing Views"
description="This guide is for writing and executing tests for database views in."
description="This guide is for writing and executing tests for database views."
url="/guides/testing/views"
image={CardImage.Testing}
/>
<Card
name="Testing Functions"
description="This guide is for writing and executing tests for database functions."
url="/guides/testing/functions"
image={CardImage.Testing}
/>
</div>
204 changes: 204 additions & 0 deletions doc/md/guides/testing/testing-functions.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
---
id: testing-functions
title: Testing Database Functions
slug: /guides/testing/functions
---

Testing your database schema and migrations is crucial to ensure code behaves as expected, catch bugs early,
and prevent regressions. Databases enforce logic, constraints, and complex relationships,
so testing ensures these elements work correctly and remain intact after changes.

In this guide we will learn how to use Atlas's [`schema test`](/testing/schema) command to test database functions.

## Database Functions

Functions are predefined operations stored in a database that can be invoked to perform calculations, manipulate data,
or execute tasks.

:::info [Atlas Pro Feature](/features#pro-plan)
Functions are currently available only to [Atlas Pro users](/features#pro-plan). To use this feature, run:
```
atlas login
```
:::

## Project Setup

### Schema File
For this example, let's assume we have the following schema, including a function:

```hcl title="schema.hcl"
schema "public" {
}
table "users" {
schema = schema.public
column "id" {
type = int
}
column "name" {
type = text
}
primary_key {
columns = [
column.id
]
}
}
table "transactions" {
schema = schema.public
column "id" {
type = int
}
column "user_id" {
type = int
}
column "amount" {
type = decimal
}
// highlight-start
column "is_income" {
type = boolean
as {
expr = "positive(amount)"
}
// highlight-end
}
primary_key {
columns = [column.id]
}
foreign_key "user_fk" {
columns = [column.user_id]
ref_columns = [table.users.column.id]
on_delete = CASCADE
on_update = NO_ACTION
}
}
// highlight-start
function "positive" {
schema = schema.public
lang = SQL
arg "v" {
type = decimal
}
return = boolean
as = "SELECT v > 0"
}
// highlight-end
```

In the schema above we have a simple `users` table and a `transactions` table. In `transactions`, the `is_income` column
checks if the `amount` is positive by calling the `positive` function.

Note that `is_income` is a [generated column](/atlas-schema/hcl#generated-columns), meaning its value is
computed using other columns or deterministic expressions (in this case, via a function).

### Project File

Before we begin testing, create a [project file](/atlas-schema/projects#project-files) named
`atlas.hcl`.

In this file we will create an environment, specify the source of our schema,
and a URL for our [dev database](/concepts/dev-database).

We will also create a file named `schema.test.hcl` to write our tests, and
add it to the `atlas.hcl` file in the test block.

```hcl title="atlas.hcl"
env "dev" {
src = "file://schema.hcl"
dev = "docker://postgres/15/dev?search_path=public"
# Test configuration for local development.
test {
schema {
src = ["schema.test.hcl"]
}
}
}
```

## Writing Tests

### Simple Test

Let's start off with a simple test that will check that the function correctly recognizes positive numbers.

```hcl title="schema.test.hcl"
test "schema" "positive_func" {
parallel = true
assert {
sql = "SELECT positive(1)"
}
log {
message = "First assertion passed"
}
assert {
sql = <<SQL
SELECT NOT positive(0);
SELECT NOT positive(-1);
SQL
}
log {
message = "Second assertion passed"
}
}
```

The test first checks if positive(1) returns TRUE, and then verifies in the second assertion that
positive(0) and positive(-1) return false.

Run the test by running:

```bash
atlas schema test --env dev
```

The output should look similar to:
```testoutput title="Test Output"
-- PASS: positive_func (2ms)
schema.test.hcl:74: First assertion passed
schema.test.hcl:83: Second assertion passed
PASS
```

### Table Driven Test

Another alternative is to write a [table driven test](/testing/schema#table-driven-tests). This
test uses the `for_each` meta-argument, which accepts a map or a set of values and is used to generate
a test case for each item in the set or map.

Following similar logic to the test above, we will check the function for the integers: 0, 1, and -1.

```hcl title="schema.test.hcl"
test "schema" "positive_func" {
parallel = true
for_each = [
{input: 1, expected: "t"},
{input: 0, expected: "f"},
{input: -1, expected: "f"},
]
exec {
sql = "SELECT positive(${each.value.input})"
output = each.value.expected
}
}
```

Run the test by running:

```bash
atlas schema test --env dev
```

The output should look similar to:
```testoutput title="Test Output"
-- PASS: positive_func/2 (5ms)
-- PASS: positive_func/3 (10ms)
-- PASS: positive_func/1 (10ms)
PASS
```
5 changes: 5 additions & 0 deletions doc/website/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,11 @@ module.exports = {
type: 'doc',
id: 'guides/testing/testing-views',
label: 'Testing Views'
},
{
type: 'doc',
id: 'guides/testing/testing-functions',
label: 'Testing Functions'
}
]
},
Expand Down

0 comments on commit a50d344

Please sign in to comment.