{"id":14971494,"url":"https://github.com/moertel/squcumber-postgres","last_synced_at":"2025-10-26T15:30:42.748Z","repository":{"id":45947487,"uuid":"86909189","full_name":"moertel/sQucumber-postgres","owner":"moertel","description":"Cucumber-based framework for defining and executing SQL unit, integration and acceptance tests for PostgreSQL databases","archived":false,"fork":false,"pushed_at":"2022-10-18T15:22:26.000Z","size":104,"stargazers_count":3,"open_issues_count":4,"forks_count":6,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-30T05:26:06.442Z","etag":null,"topics":["cucumber","cucumber-framework","postgresql","postgrest","sql","test-automation","test-driven-development","test-framework"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moertel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-01T11:33:51.000Z","updated_at":"2022-12-14T17:15:22.000Z","dependencies_parsed_at":"2022-09-03T04:42:46.726Z","dependency_job_id":null,"html_url":"https://github.com/moertel/sQucumber-postgres","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moertel%2FsQucumber-postgres","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moertel%2FsQucumber-postgres/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moertel%2FsQucumber-postgres/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moertel%2FsQucumber-postgres/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moertel","download_url":"https://codeload.github.com/moertel/sQucumber-postgres/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238357369,"owners_count":19458561,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cucumber","cucumber-framework","postgresql","postgrest","sql","test-automation","test-driven-development","test-framework"],"created_at":"2024-09-24T13:45:17.197Z","updated_at":"2025-10-26T15:30:42.446Z","avatar_url":"https://github.com/moertel.png","language":"Ruby","readme":"# sQucumber Postgres\n\n[![Build Status](https://travis-ci.org/moertel/sQucumber-postgres.svg)](https://travis-ci.org/moertel/sQucumber-postgres) [![Gem Version](https://badge.fury.io/rb/squcumber-postgres.svg)](https://badge.fury.io/rb/squcumber-postgres)\n\nBring the BDD approach to writing SQL for your Postgres instance and be confident that your scripts do what they're supposed to do. Define and execute SQL unit, acceptance and integration tests and let them serve as a living documentation for your queries. It's Cucumber - for SQL!\n\n * [Example](#example)\n * [How it works](#how-it-works)\n * [Installation](#installation)\n * [Usage](#usage)\n   * [Running with Docker](#docker)\n   * [Environment variables](#environment-variables)\n * [Step definitions](#available-steps)\n\n## Example\n\nSuppose you want to test that `kpi_reporting.sql` is producing correct results; its `.feature` file could look as follows:\n```cucumber\n# features/kpi_reporting.feature\n\nFeature: KPI Reporting\n\n  Scenario: There are some visitors and some orders\n    Given the existing table \"access_logs\":\n      | req_date   | req_time | request_id |\n      | 2016-07-29 | 23:45:16 | 751fa12d-c51e-4823-8362-f85fde8b7fcd |\n      | 2016-07-31 | 22:13:54 | 35c4699e-c035-44cb-957c-3cd992b0ad73 |\n      | 2016-07-31 | 11:23:45 | 0000021d-7e77-4748-89f5-cddd0a11d2f9 |\n    And the existing table \"orders\":\n      | order_date | product |\n      | 2016-07-31 | Premium |\n    When the SQL file \"kpi_reporting.sql\" is executed\n    And the resulting table \"kpi_reporting\" is queried\n    Then the result exactly matches:\n      | date       | visitors | orders |\n      | 2016-07-29 | 1        | 0      |\n      | 2016-07-31 | 2        | 1      |\n```\n\n## How It Works\n\nFeature files are written in the \u003ca href=\"https://github.com/cucumber/cucumber/wiki/Gherkin\" target=\"_blank\"\u003eGherkin language\u003c/a\u003e. A feature, such as an SQL file, consists of several scenarios. A scenario describes how the script behaves under some particular conditions which are outlined using \u003ca href=https://github.com/cucumber/cucumber/wiki/Given-When-Then\u003e**Given - When - Then**\u003c/a\u003e steps.\n\nUnder the hood, sQucumber connects to your Postgres instance, creates a new database (prefixed with `test_env_` followed by a random number) and copies over the schemas and tables you specify in the `Given` steps. The advantage of this is that you always test against the current live version of your database. If your script can be executed here, it is guaranteed to be able to be executed in production. With further `Then` steps you'll add mock data. Other than with actual live data, mocked table data gives you the freedom to define edge cases exactly as you like, even if they don't (yet) exist in production.\n\nThe `When` steps execute your SQL files and fetch their results. These can now be compared against expectations as specified in the `Then` steps. Whether you expect an empty result, want to check for the occurrence of partial results or make sure that actual and expected output match _exactly_, everything's possible.\n\nShould a scenario fail, the differences between the expected and actual results will be displayed clearly, so you can fix your SQL script accordingly. Optionally, an HTML file with the test results is created, that can serve as a living documentation that can be passed on to stakeholders who want to use the script and understand how it works.\n\nA full specification of all the available `Given - When - Then` steps can be found [here](#available-steps).\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'squcumber-postgres'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install squcumber-postgres\n\n## Usage\n\nPut your `.feature` files in the directory `feature` in your project's root. (You may use subfolders.)\nIn order to take advantage of auto-generated Rake tasks, add this to your `Rakefile`:\n```ruby\nrequire 'squcumber-postgres/rake/task'\n```\n\nThe following folder structure\n```\n└── features\n    ├── marketing\n    │   ├── sales.feature\n    │   └── kpi.feature\n    └── development\n        └── logs\n            └── aggregate.feature\n```\nLeads to the following Rake tasks:\n```\n$ rake -T\nrake test:sql:marketing                                         # Run SQL tests for all features in marketing\nrake test:sql:marketing:sales[scenario_line_number]             # Run SQL tests for feature marketing/sales\nrake test:sql:marketing:kpi[scenario_line_number]               # Run SQL tests for feature marketing/kpi\nrake test:sql:development                                       # Run SQL tests for all features in development\nrake test:sql:development:logs                                  # Run SQL tests for all features in development/logs\nrake test:sql:development:logs:aggregate[scenario_line_number]  # Run SQL tests for feature development/logs/aggregate\n```\n\nRun a whole suite of features by executing a Rake task on the folder level:\n```\nrake test:sql:marketing\n```\n\nOr execute a specific scenario only by specifying its line number in the corresponding `.feature` file:\n```\nrake test:sql:marketing:sales[12]\n```\n\n### Docker\n\nInstead of installing the Gem in your project, you can run the SQL tests inside a Docker container. There's an automated build for [`moertel/squcumber-postgres`](https://hub.docker.com/r/moertel/squcumber-postgres/) tagged with the releases of the Gem. Alternatively, you can use the version from the `master` branch with the `latest` tag. To run the tests, mount your SQL and feature files into the container and provide the environment variables to access your Postgres database:\n\n```bash\ndocker run \\\n  -v /local/path/to/sql:/sql \\\n  -v /local/path/to/features:/features \\\n  -v /local/path/to/custom_step_definitions:/custom_step_definitions \\\n  -e DB_HOST=postgres.example.com \\\n  -e DB_PORT=5432 \\\n  -e DB_USER=someuser \\\n  -e DB_PASSWORD=secret \\\n  -e DB_NAME=somedb \\\n  -it moertel/squcumber-postgres:latest\n```\n\n### Environment Variables\n\nMake sure the following environment variables are set when running sQucumber's Rake tasks:\n\n| Name | Description |\n| ---- | ----------- |\n| DB_HOST | Hostname of the Postgres instance |\n| DB_PORT | Postgres port to connect to |\n| DB_USER | Name of the Postgres user to use to create a testing database, must be a superuser |\n| DB_PASSWORD | Password of the Postgres user |\n| DB_NAME | Name of the DB on the Postgres instance |\n\nOptional environment variables:\n\n| Name | Value | Description | Default |\n| ---- | ----- | ----------- | ------- |\n| SPEC_SHOW_STDOUT | 1 | Show output of statements executed on the Postgres instance | 0 |\n| KEEP_TEST_DB | 1 | Do not drop the database after test execution (useful for manual inspection) | 0 |\n| TEST_DB_NAME_OVERRIDE | _String_ | Define a custom name for the testing database created on the instance. Setting this to `foo` will result in the database `test_env_foo` being created | random 5-digit integer |\n| CUSTOM_STEPS_DIR | /path/to/dir | Path to a directory where custom step definitions are stored | |\n| SQUCUMBER_OPTIONS_EXTRA | `--strict` | Specify extra configuration options to the run command | |\n\n\n## Available Steps\n### `Given the SQL files in the path \"{path}\"`\n  * Allows to add the `path` to SQL scripts that need to be executed to produce the result set\u003cbr/\u003e\n    Path can be either relative to the project root or absolute\u003cbr/\u003e\n    Files are executed in the order they are given, using [`When the given SQL files are executed`](#when-the-given-sql-files-are-executed)\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    For the files `project/src/sql/some_query.sql` and `project/src/sql/some_query.sql`:\n\n    ```cucumber\n    Given the SQL files in the path \"project/src/sql\":\n      | file              |\n      | some_query.sql    |\n      | another_query.sql |\n    When the given SQL files are executed\n    And the resulting table \"some_schema.some_table\" is queried\n    Then the result is empty\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `Given the SQL file path \"{path}\"`\n  * Allows to add the `path` to SQL scripts that need to be executed during the steps\u003cbr/\u003e\n    Path can be either relative to the project root or absolute\u003cbr/\u003e\n    Works the same as [`Given the SQL files in the path \"{path}\"`](#given-the-sql-files-in-the-path-path) but doesn't take a list of queries\u003cbr/\u003e\n    Scripts can be executed on a per-file basis using [`When the SQL file \"{file}\" is executed`](#when-the-sql-file-file_name-is-executed)\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    For the path `project/src/sql`:\n\n    ```cucumber\n    Given the SQL file path \"project/src/sql\"\n    When the SQL file \"some_file.sql\" is executed\n    And the resulting table \"some_schema.some_table\" is queried\n    Then result exactly matches:\n      | some_column | another_column |\n      | foo         | bar            |\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `Given their table dependencies`\n  * Tables used by the SQL script(s). As the testing framework operates on a new, empty database, this makes sure that all dependencies are present upon script execution. Initially, tables defined here will be empty. You can add data to them by using the step [`Given the existing table \"{schema_and_table}\"`](#given-the-existing-table-schema_and_table)\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    ```cucumber\n    Given the SQL files in the path \"project/src/sql\":\n      | file      |\n      | query.sql |\n    And their table dependencies:\n      | table                  |\n      | some_schema.some_table |\n    When the given SQL files are executed\n    And the resulting table \"some_schema.another_table\" is queried\n    Then the result exactly matches:\n      | some_column | another_column |\n      | foo         | bar            |\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `Given their schema dependencies`\n  * Schemas used by the SQL scripts if not already specified by the step [`Given their table dependencies`](#given-their-table-dependencies). This step usually makes sense if your script wants to write data to an own schema.\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    ```cucumber\n    Given the SQL files in the path \"project/src/sql\":\n      | file      |\n      | query.sql |\n    And their table dependencies:\n      | table                  |\n      | some_schema.some_table |\n    And their schema dependencies:\n      | schema         |\n      | another_schema |\n    When the given SQL files are executed\n    And the resulting table \"another_schema.some_table\" is queried\n    Then the result exactly matches:\n      | some_column | another_column |\n      | foo         | bar            |\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `Given the following defaults for \"{schema_and_table}\" (if not stated otherwise)`\n  * Allows to set default values for particular table columns. This is useful if values are not allowed to be `NULL` but would clutter test scenarios or make tables very wide. Default values can always be overwritten in test steps. This step makes most sense as a `Background` step.\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    ```cucumber\n    Background:\n      Given the following defaults for \"some_schema.some_table\" (if not stated otherwise):\n        | some_column | another_column |\n        | foo         | bar            |\n\n    Scenario:\n      Given the SQL files in the path \"project/src/sql\":\n        | file      |\n        | query.sql |\n      And their table dependencies:\n        | table                  |\n        | some_schema.some_table |\n      And the existing table \"some_schema.some_table\":\n        | yet_another_column |\n        | 123                |\n      When the given SQL files are executed\n      And the resulting table \"another_schema.some_table\" is queried\n      Then the result exactly matches:\n        | some_column | another_column |\n        | foo         | bar            |\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `Given a clean environment`\n  * Truncates all tables currently present in the database. This is usually called implicitly before a new scenario is executed.\u003cbr/\u003e\n\n---\n\n### `Given the existing table \"{schema_and_table}\":`\n  * Allows to insert data into the database. You only need to specify the columns that your SQL script relies on. All other column values will be set to `NULL`. Specifying a column but leaving the value empty will also result in a `NULL` value. All constraints and defaults are removed from the mock database, so it's your responsibility to ensure data integrity.\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    ```cucumber\n    Background:\n      Given the SQL files in the path \"project/src/sql\":\n        | file      |\n        | query.sql |\n      And their table dependencies:\n        | table                  |\n        | some_schema.some_table |\n\n    Scenario:\n      Given the existing table \"some_schema.some_table\":\n        | some_column | another_column |\n        | 123         | foo            |\n        | 345         |                |\n        |             | bar            |\n      When the given SQL files are executed\n      And the resulting table \"some_schema.another_table\" is queried\n      Then the result exactly matches:\n        | foo_count | bar_count |\n        | 1         | 0         |\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `When the given SQL files are executed`\n  * Takes all files provided in the [`Given the SQL files in the path \"{path}\"`](#given-the-sql-files-in-the-path-path) step and executes them in the specified order.\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    ```cucumber\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `When the SQL file \"{file_name}\" is executed`\n  * Executes the given file at `{file_name}`. This can be an absolute file path or relative to the project root. If a path has been specified in the [`Given the SQL file path {path}`](#given-the-sql-file-path-path) step, it will be respected.\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    ```cucumber\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `When the resulting table \"{schema_and_table}\" is queried`\n  * Fetches the contents of the table specified in `{schema_and_table}`. Note that the result is not necessarily ordered, so in case you expect more than one row and want to compare that a table matches _exactly_, consider ordering the result with the [`When the resulting table \"{schema_and_table}\" is queried, ordered by \"{column_name}\"`](#when-the-resulting-table-schema_and_table-is-queried-ordered-by-column_name) step.\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    ```cucumber\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `When the resulting table \"{schema_and_table}\" is queried, ordered by \"{column_name}\"`\n  * Fetches the contents of the table specified in `{schema_and_table}` and orders the result by the `{column_name}` given. This is useful when the result is expected to match _exactly_ a particular result because rows will in this case be compared one by one in the given order.\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    ```cucumber\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `Then the result starts with`\n  * Checks the results of the [`When the resulting table \"{schema_and_table}\" is queried`](#when-the-resulting-table-schema_and_table-is-queried) step and compares only the first row. An optional explanation can be added at the end, to make clear what is expected in the result.\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    ```cucumber\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `Then the result includes`\n  * Checks the results of the [`When the resulting table \"{schema_and_table}\" is queried`](#when-the-resulting-table-schema_and_table-is-queried) step and makes sure the given expected rows do occur in the actual result, in any order. An optional explanation can be added at the end to clarify what is expected in the result.\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    ```cucumber\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `The result does not include`\n  * Checks the results of the [`When the resulting table \"{schema_and_table}\" is queried`](#when-the-resulting-table-schema_and_table-is-queried) step and makes sure the given rows do not occur in the actual result. An optional explanation can be added at the end to clarify what is expected in the result.\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    ```cucumber\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `The result exactly matches`\n  * Checks the results of the [`When the resulting table \"{schema_and_table}\" is queried`](#when-the-resulting-table-schema_and_table-is-queried) step and makes sure the given rows exactly match the actual result. Ordering is not important. An optional explanation can be added at the end to clarify what is expected in the result.\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    ```cucumber\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n### `Then the result is empty`\n  * Checks that the results of the [`When the resulting table \"{schema_and_table}\" is queried`](#when-the-resulting-table-schema_and_table-is-queried) step is empty. An optional explanation can be added at the end to clarify what is expected in the result.\u003cbr/\u003e\n    \u003cdetails\u003e\u003csummary\u003e \u003cb\u003eExample\u003c/b\u003e (click to expand) \u003c/summary\u003e\u003cp\u003e\n\n    ```cucumber\n    ```\n    \u003c/p\u003e\n    \u003c/details\u003e\n\n---\n\n## Contributing\n\n1. Fork it ( https://github.com/moertel/squcumber-postgres/fork )\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoertel%2Fsqucumber-postgres","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoertel%2Fsqucumber-postgres","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoertel%2Fsqucumber-postgres/lists"}