You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The primary purpose of this library is to simplify the testing of SQL data models and queries by allowing users to mock input data and create tests for various scenarios. It provides a consistent and convenient way to test the execution of your query without the need to process a massive amount of data.
10
10
11
-
# Documentation
12
-
13
-
A full documentation can be found [on the documentation page](https://deeplcom.github.io/sql-mock/)
14
-
15
-
The library currently supports the following databases.
*[Specifying the query to test](/docs/your_sql_query_to_test.md)
19
+
*[Result assertions](/docs/result_assertion.md)
20
+
* System specific usage
21
+
*[Use with BigQuery](/docs/bigquery.md)
22
+
*[Use with Clickhouse](/docs/clickhouse.md)
23
+
*[Use with Redshift](/docs/redshift.md)
24
+
*[Use with Snowflake](/docs/snowflake.md)
25
+
*[Use with dbt](/docs/dbt.md)
26
+
27
+
You can find some examples in the [examples folder](https://github.com/DeepLcom/sql-mock/tree/main/examples).
28
+
29
+
### Installation
22
30
23
31
The library can be installed from [PyPI](https://pypi.org/project/sql-mock/) using pip:
24
32
@@ -42,11 +50,105 @@ If you need to modify this source code, install the dependencies using poetry:
42
50
poetry install --all-extras
43
51
```
44
52
53
+
#### Recommended Setup for Pytest
54
+
55
+
If you are using pytest, make sure to add a `conftest.py` file to the root of your project.
56
+
In the file add the following lines:
57
+
58
+
```python
59
+
import pytest
60
+
pytest.register_assert_rewrite('sql_mock')
61
+
```
62
+
63
+
This allows you to get a rich comparison when using the `.assert_equal` method on the table mock instances.
64
+
65
+
We also recommend using [pytest-icdiff](https://github.com/hjwp/pytest-icdiff) for better visibility on diffs of failed tests.
66
+
67
+
### Quickstart
68
+
69
+
Before diving into specific database scenarios, let's start with a simplified example of how SQL Mock works behind the scenes.
70
+
71
+
1. You have an original SQL query, for instance:
72
+
73
+
```sql
74
+
-- path/to/query_for_result_table.sql
75
+
SELECT id FROMdata.table1
76
+
```
77
+
78
+
2. Using SQL Mock, you define table mocks. You can use the built-in column types provided by SQL Mock. Available column types include `Int`, `String`, `Date`, and more. Each database type has their own column types. Define your tables by subclassing a mock table class that fits your database (e.g. `BigQueryTableMock`) and specifying the column types along with default values. In our example we use the `ClickHouseTableMock` class
79
+
80
+
```python
81
+
from sql_mock.clickhouse import column_mocks as col
82
+
from sql_mock.clickhouse.table_mocks import ClickHouseTableMock
3. **Creating mock data:** Define mock data for your tables using dictionaries. Each dictionary represents a row in the table, with keys corresponding to column names. Table column keys that don't get a value will use the default.
96
+
97
+
```python
98
+
user_data = [
99
+
{}, # This will use the defaults for both id and name
100
+
{'id': 2, 'name': 'Martin'},
101
+
{'id': 3}, # This will use defaults for the name
102
+
]
103
+
104
+
input_table_mock = Table.from_dicts(user_data)
105
+
```
106
+
107
+
4. **Getting results for a table mock:** Use the `from_mocks` method of the table mock object to generate mock query results based on your mock data.
108
+
109
+
```python
110
+
res = ResultTable.from_mocks(input_data=[input_table_mock])
111
+
```
112
+
113
+
5. Behind the scene SQL Mock replaces table references (e.g. `data.table1`) in your query with Common Table Expressions (CTEs) filled with dummy data. It can roughly be compared to something like this:
114
+
115
+
```sql
116
+
WITH data__table1 AS (
117
+
-- Mocked inputs
118
+
SELECT
119
+
cast('1'AS'String') ASid,
120
+
cast('Peter'AS'String') AS name
121
+
UNIONALL
122
+
SELECT
123
+
cast('2'AS'String') ASid,
124
+
cast('Martin'AS'String') AS name
125
+
UNIONALL
126
+
SELECT
127
+
cast('3'AS'String') ASid,
128
+
cast('Peter'AS'String') AS name
129
+
)
130
+
131
+
result AS (
132
+
-- Original query with replaced references
133
+
SELECTidFROM data__table1
134
+
)
135
+
136
+
SELECT
137
+
cast(idAS'String') ASid
138
+
FROM result
139
+
```
140
+
141
+
6. Finally, you can compare your results to some expected results using the `assert_equal` method.
142
+
143
+
```python
144
+
expected = [{'id': '1'},{'id': '2'},{'id': '3'}]
145
+
res.assert_equal(expected)
146
+
```
147
+
45
148
## Pydantic V1 vs. V2
46
149
47
150
SQL Mock's published version supports Pydantic V2. You might run into issues when your code depends on Pydantic V1.
48
-
We have an alternative branch you can install from that supports Pydantic V1 in the meanwhile: https://github.com/DeepLcom/sql-mock/tree/pydantic-v1
49
-
151
+
We have an alternative branch you can install from that supports Pydantic V1 in the meanwhile: <https://github.com/DeepLcom/sql-mock/tree/pydantic-v1>
50
152
51
153
## Contributing
52
154
@@ -56,10 +158,10 @@ We welcome contributions to improve and enhance this open-source project. Whethe
56
158
57
159
If you encounter a bug, have a feature request, or face any issues with the project, we encourage you to report them using the project's issue tracker. When creating an issue, please include the following information:
58
160
59
-
- A clear and descriptive title.
60
-
- A detailed description of the problem or suggestion.
61
-
- Steps to reproduce the issue (if applicable).
62
-
- Any error messages or screenshots that help clarify the problem.
161
+
* A clear and descriptive title.
162
+
* A detailed description of the problem or suggestion.
163
+
* Steps to reproduce the issue (if applicable).
164
+
* Any error messages or screenshots that help clarify the problem.
63
165
64
166
### Feature Requests
65
167
@@ -79,3 +181,64 @@ The SQL Mock Buddy can be accessed here: [https://chat.openai.com/g/g-FIXNcqu1l-
79
181
SQL Mock Buddy should help you to get started quickly withSQL Mock.
80
182
81
183
It is still in beta mode and you should definitely double-check its output!
184
+
185
+
## FAQ
186
+
187
+
### My database system is not supported yet but I want to use SQL Mock. What should I do?
188
+
189
+
We are planning to add more and more supported database systems. However, if your system isnot supported yet, you can still use SQL Mock. There are only 2 things you need to do:
190
+
191
+
#### Create your `TableMock` class
192
+
193
+
First, you need to create a `TableMock`classfor your database system that inherits from`sql_mock.table_mocks.BaseTableMock`.
194
+
195
+
That class needs to implement the `_get_results` method which should make sure to fetch the results of a query (e.g. produced by `self._generate_query()`) andreturn it aslist of dictionaries.
196
+
197
+
Look at one of the existing client libraries to see how this could work (e.g. [BigQueryTableMock](https://github.com/DeepLcom/sql-mock/blob/main/src/sql_mock/bigquery/table_mocks.py)).
198
+
199
+
You might want to create a settings classas well in case you need some specific connection settings to be available within the `_get_results` method.
200
+
201
+
#### Create your `ColumnMocks`
202
+
203
+
Your database system might support specific database types. In order to make them available as column types, you can use the `sql_mock.column_mocks.BaseColumnMock`classas a base and inherit your specific column types from it.
204
+
For most of your column mocks you might only need to specify the `dtype` that should be used to parse the inputs.
205
+
206
+
A good practise is to create a `BaseColumnMock`class that is specific to your database and inherit all your column types from it, e.g.:
207
+
208
+
```python
209
+
from sql_mock.column_mocks import BaseColumnMock
210
+
211
+
class MyFanceDatabaseColumnMock(BaseColumnMock):
212
+
# In case you need some specific logic that overwrites the default behavior, you can do so here
213
+
pass
214
+
215
+
class Int(MyFanceDatabaseColumnMock):
216
+
dtype = "Integer"
217
+
218
+
class String(MyFanceDatabaseColumnMock):
219
+
dtype = "String"
220
+
```
221
+
222
+
#### Contribute your database setup
223
+
224
+
There will definitely be folks in the community that are in the need of support for the database you just created all the setup for.
225
+
Feel free to create a PR on this repository that we can start supporting your database system!
226
+
227
+
### I am missing a specific BaseColumnMock type for my model fields
228
+
229
+
We implemented some basic column types but it could happen that you don't find the one you need.
230
+
Luckily, you can easily create those with the tools provided.
231
+
The only thing you need to do is to inherit from the `BaseColumnMock` that is specific to your database system (e.g. `BigQueryColumnMock`) and write classes for the column mocks you are missing. Usually you only need to set the correct `dtype`. This would later be used in the `cast(col to <dtype>)` expression.
232
+
233
+
```python
234
+
# Replace the import with the database system you are using
235
+
from sql_mock.bigquery.column_mock import BigQueryColumnMock
236
+
237
+
class MyFancyMissingColType(BigQueryColumnMock):
238
+
dtype = "FancyMissingColType"
239
+
240
+
# In case you need to implement additional logic for casting, you can do so here
241
+
...
242
+
```
243
+
244
+
**Don't forget to create a PR in case you feel that your column mock type could be useful for the community**!
0 commit comments