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
Copy file name to clipboardExpand all lines: README.md
+113-1Lines changed: 113 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -590,9 +590,121 @@ See [shtab](https://github.com/iterative/shtab) for more details.
590
590
591
591
Note, that due to the (typically) global zsh completions directory, this can create some friction points with different virtual (or conda) ENVS with the same executable name.
592
592
593
+
# General Suggested Testing Model
594
+
595
+
At a high level, `pydantic_cli` is (hopefully) a thin bridge between your `Options` defined as a Pydantic model and your
596
+
main `runner(opts: Options)` func that has hooks into the startup, shutdown and error handling of the command line tool.
597
+
It also supports loading config files defined as JSON. By design, `pydantic_cli` explicitly doesn't expose, or leak the argparse instance
598
+
because it would add too much surface area and it would enable users' to start mucking with the argparse instance in all kinds of unexpected ways.
599
+
The use of `argparse` internally is an hidden implementation detail.
600
+
601
+
Testing can be done by leveraging the `to_runner` interface.
602
+
603
+
604
+
605
+
1. It's recommend trying to do the majority of testing via unit tests (independent of `pydantic_cli`) with your main function and different instances of your pydantic data model.
606
+
2. Once this test coverage is reasonable, it can be useful to add a few smoke tests at the integration level leveraging `to_runner` to make sure the tool is functional. Any bugs at this level are probably at the `pydantic_cli` level, not your library code.
607
+
608
+
Note, that `to_runner(Opts, my_main)` returns a `Callable[[List[str]], int]` that can be used with `argv` to return an integer exit code of your program. The `to_runner` layer will also catch any exceptions.
609
+
610
+
```python
611
+
import unittest
612
+
613
+
from pydantic import BaseModel
614
+
from pydantic_cli import to_runner
615
+
616
+
617
+
classOptions(BaseModel):
618
+
alpha: int
619
+
620
+
621
+
defmain(opts: Options) -> int:
622
+
if opts.alpha <0:
623
+
raiseException(f"Got options {opts}. Forced raise for testing.")
624
+
return0
625
+
626
+
627
+
classTestExample(unittest.TestCase):
628
+
629
+
deftest_core(self):
630
+
# Note, this has nothing to do with pydantic_cli
631
+
# If possible, this is where the bulk of the testing should be
632
+
self.assertEqual(0, main(Options(alpha=1)))
633
+
634
+
deftest_example(self):
635
+
f = to_runner(Options, main)
636
+
self.assertEqual(0, f(["--alpha", "100"]))
637
+
638
+
deftest_expected_error(self):
639
+
f = to_runner(Options, main)
640
+
self.assertEqual(1, f(["--alpha", "-10"]))
641
+
```
642
+
643
+
644
+
645
+
For more scrappy, interactive local development, it can be useful to add `ipdb` or `pdb` and create a custom `exception_handler`.
646
+
647
+
```python
648
+
import sys
649
+
from pydantic import BaseModel
650
+
from pydantic_cli import default_exception_handler, run_and_exit
651
+
652
+
653
+
classOptions(BaseModel):
654
+
alpha: int
655
+
656
+
657
+
defexception_handler(ex: BaseException) -> int:
658
+
exit_code = default_exception_handler(ex)
659
+
import ipdb; ipdb.set_trace()
660
+
return exit_code
661
+
662
+
663
+
defmain(opts: Options) -> int:
664
+
if opts.alpha <0:
665
+
raiseException(f"Got options {opts}. Forced raise for testing.")
The core design choice in `pydantic_cli` is leveraging composable functions `f(g(x))` style providing a straight-forward mechanism to plug into.
702
+
593
703
# More Examples
594
704
595
-
[More examples are provided here](https://github.com/mpkocher/pydantic-cli/tree/master/pydantic_cli/examples)
705
+
[More examples are provided here](https://github.com/mpkocher/pydantic-cli/tree/master/pydantic_cli/examples) and [Testing Examples can be seen here](https://github.com/mpkocher/pydantic-cli/tree/master/pydantic_cli/tests).
706
+
707
+
The [TestHarness](https://github.com/mpkocher/pydantic-cli/blob/master/pydantic_cli/tests/__init__.py) might provide examples of how to test your CLI tool(s)
0 commit comments