Metadata-Version: 2.4
Name: bean-test
Version: 0.1.0
Summary: Tiny test framework
Author-email: numen-0 <numen.0x1dea@gmail.com>
License: MIT License
        
        Copyright (c) `2026` `numen-0`
        
        Permission is hereby granted, free of charge, to any person obtaining a copy of
        this software and associated documentation files (the "Software"), to deal in
        the Software without restriction, including without limitation the rights to
        use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
        of the Software, and to permit persons to whom the Software is furnished to do
        so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: homepage, https://github.com/numen-0/bean
Project-URL: repository, https://github.com/numen-0/bean
Project-URL: issues, https://github.com/numen-0/bean/issues
Project-URL: documentation, https://github.com/numen-0/bean/bean-http/src/bean/test.py
Keywords: bean,bean.test
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Typing :: Typed
Requires-Python: >=3.14
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# bean.test

`bean.test` is a tiny test framework.

> Just enough to pick apart rotten beans.

---

## Overview

With `bean.test` you get:

- A small, fluent assertion API.
- Test data generation helpers.
- Simple test registration and reporting.
- test discovery

```py
from bean import test

@test.register()
def test_something():
    test.expect(1 + 1).Equal(2)
```

> **Note**:
>
> To quickly inspect the public API:
>
> ```sh
> python -c "
>     import bean.test as bean
>     for v in sorted(dir(bean)):
>         print(f'- {v}')
> "
> ```

## Installation

Using `pip`:

```sh
pip install --upgrade bean-test
```

Using `curl` (direct download):

```sh
curl -Ls \
    https://raw.githubusercontent.com/numen-0/bean/main/bean-test/src/bean/test.py
```

## Command Line

`bean.test` can be used directly as a test runner.

```sh
python -m bean.test tests/
```

By default, files matching `*.py` are discovered. Use `--pattern` to change the
pattern:

```sh
python -m bean.test tests/ --pattern "test_*.py"
```

Run only specific namespaces:

```sh
python -m bean.test tests/ -n unit integration
```

> **Note**:
>
> For more information, run:
>
> ```sh
> python -m bean.test --help
> ```

## API

This is a quick reference for the main `API`.

For full details, see the [source code](/bean-test/src/bean/test.py).

### Test Registration

Tests can be registered under different namespaces.

```py
@register("math")
def test_addition(): ...

@register("http")
def test_request(): ...
```

### Data Generation

`rand` provides small helpers for generating and composing random test data.

```py
from bean.test import rand

list(rand.cases(               # generator for tuple[bool, tuple[str, int]]
    rand(bool),                # <- retrieve bool
    rand.choice(*rand.product( # <- chose a tuple[str, int]
        ("a", "b", "c"),
        (1, 2, 3),
    )),
    count=5,
    seed=42,
))
```

### The Lab

`Lab` is the singleton responsible for running tests and generating reports

| member                    | descrition                                       |
|:-------------------------:|:-------------------------------------------------|
| `report(ns)`              | Generate report for all or the given namespaces  |
| `print(ns)`               | Generate a report and write it to stdout         |

### Expect Assertions

Fluent assertions for inspecting values.

| **Method**                | **Descrition**                                   |
|:--------------------------|:-------------------------------------------------|
| *misc*                    | -                                                |
| `Set(value)`              | Replace asserted value                           |
| `Map(fn)`                 | Transform asserted value                         |
| `MapEach(fn)`             | Transform asserted value items                   |
| `Check(predicate)`        | Assert value satisfies predicate                 |
| *context*                 | -                                                |
| `Raises(exception)`       | Context manager asserting that an exception is raised |
| *turthiness*              | -                                                |
| `Truthy()`                | Assert value is truthy                           |
| `Falsy()`                 | Assert value is falsy                            |
| `Empty()`                 | Assert value has length zero                     |
| `NotEmpty()`              | Assert value has non-zero length                 |
| *equality*                | -                                                |
| `Equal(expected)`         | Assert value == expected                         |
| `NotEqual(expected)`      | Assert value != expected                         |
| `Greater(expected)`       | Assert value < expected                          |
| `GreaterEqual(expected)`  | Assert value <= expected                         |
| `Less(expected)`          | Assert value > expected                          |
| `LessEqual(expected)`     | Assert value >= expected                         |
| `AlmostEqual(expected)`   | Assert numeric values are approximately equal    |
| `NotAlmostEqual(expected)` | Assert numeric values are not approximately equal |
| *type* identity           | -                                                |
| `Is(expected)`            | Assert value is expected                         |
| `IsNot(expected)`         | Assert value is not expected                     |
| `IsNone()`                | Assert value is `None`                           |
| `IsNotNone()`             | Assert value is not `None`                       |
| `IsInstance(cls)`         | Assert value is an instance of a type            |
| `IsNotInstance(cls)`      | Assert value is not an instance of a type        |
| `IsNumeric()`             | Assert value is numeric                          |
| `IsString()`              | Assert value is a string                         |
| `IsEnum()`                | Assert value is a enum                           |
| `IsIterable()`            | Assert value is iterable                         |
| *strings*                 | -                                                |
| `StartsWith(expected)`    | Assert value starts with given prefix            |
| `NotStartsWith(expected)` | Assert value doesn't start with given prefix     |
| `EndsWith(expected)`      | Assert value ends with given suffix              |
| `NotEndsWith(expected)`   | Assert value doesn't end with given suffix       |
| `Regex(expected)`         | Assert value matches regular expression          |
| `NotRegex(expected)`      | Assert value doesn't matche regular expression   |
| *quantifiers*             | -                                                |
| `All(predicate)`          | Assert every item satisfies predicate            |
| `Any(predicate)`          | Assert at least one item satisfies predicate     |
| `NotAny(predicate)`       | Assert no item satisfies predicate               |
| `IsSorted()`              | Assert value is sorted                           |
| *quantifiers*             | -                                                |
| `In(expected)`            | Assert value is contained in expected            |
| `NotIn(expected)`         | Assert value isn't contained in expected         |
| `HasAttr(expected)`       | Assert value has an attribute                    |
| `NotHasAttr(expected)`    | Assert value hasn't an attribute                 |
| *collections*             | -                                                |
| `SequenceEqual(expected)` | Assert equal items in the same order             |
| `ContainerEqual(expected)` | Assert equal items, ignoring order and preserving multiplicity |
| `SetEqual(expected)`      | Assert equal items using set semantics           |
| `NotSetEqual(expected)`   | Assert containers are not set-equal              |
| `Subset(expected)`        | Assert every item is contained in expected       |
| `NotSubset(expected)`     | Assert at least one item is outside expected     |
| `Superset(expected)`      | Assert value contains every expected item        |
| `NotSuperset(expected)`   | Assert at least one expected item is missing     |
| `Intersects(expected)`    | Assert containers have no items in common        |
| `Disjoint(expected)`      | Assert containers have at least one item in common |

> **Note**: Every method returns `self`, so multiple assertions can be chained

## Example

```py
from bean import test

@test.register()
def test_addition():
    test.expect(1 + 1).Equal(2)

@test.register()
def test_string():
    test.expect("hello world") \
        .IsString() \
        .NotEmpty() \
        .StartsWith("hello") \
        .EndsWith("world")

@test.register()
def test_collections():
    test.expect([1, 2, 3]).SequenceEqual([1, 2, 3])
    test.expect([3, 1, 2]).SetEqual([1, 2, 3])
    test.expect([1, 2]).Subset([1, 2, 3])

@test.register()
def test_exception():
    with test.expect.Raises(ValueError):
        int("bean")

test.Lab.print()
```

## License

All the repo falls under the [MIT License](/LICENSE).

