Welcome to Unittest2doc¶
English | 中文
Unittest2doc is a tool for converting Python unit test code into documentation. The unit test section in this documentation is generated using Unittest2doc.
Seeing is believing, let’s learn how to use Unittest2doc through examples in this documentation.
Project repository: https://github.com/Fmajor/unittest2doc
The project structure is:
unittest2doc/ # Project root directory
src/ # Source code directory
unittest2doc/ # Package directory
__init__.py
unittest2doc.py
...
sphinx-docs/ # Documentation directory
source/ # Documentation source files
conf.py # Configuration file, import test packages here
index.rst
unittests/ # Unittest2doc will generate rst files here
src/ # API documentation generated by sphinx autosummary
...
build/ # Documentation build directory, run make html to generate this documentation
tests/ # Test directory, where we use Unittest2doc
test_unittest2doc.py # Test file, including a Unittest2doc class and its running examples
test_pformat_json.py # Here we test a function for structured JSON output
# For a structured output function, the best way to test is to display the results and save as documentation
pyproject.toml
README.rst
In the project root directory, use
make unittest
Actually executes
python -m unittest discover -s tests -p '*.py' -b
This is general unit testing, we only care about the test results, not the test process
In the project root directory, use
make generate-unittest-docs
Actually executes
unittest2doc -s tests -p '*.py'
, which directly runs all test files that meet the conditionsEach test file can be executed individually, with runtime parameters that allow them to generate RST format sphinx documentation and save to the
/sphinx-docs/source/unittests/
directory, ultimately displayed in our documentation
Here we directly show the test files
tests/test_unittest2doc.py
¶
Generated documentation: unittest2doc.unittest2doc.Unittest2Doc
1import unittest
2import unittest2doc
3from unittest2doc import Unittest2Doc, docpprint
4import time
5import json
6import yaml
7import textwrap
8from pathlib import Path
9
10if 'test Unittest2Doc' and 1:
11 class Test(unittest.TestCase):
12 ''' docstring of class, new title
13 -----------------------------
14
15 * Sphinx have already use "=" as title marker
16 * to form a subtitle, we should use ``-`` as the title marker
17
18 '''
19 def test(s):
20 a = 1
21 b = 2
22 print("# this is a normal unittest.TestCase")
23 print("# we can use all its assertion methods")
24 s.assertEqual(a, 1)
25 s.assertNotEqual(a, b)
26 s.assertIs(a, 1)
27 s.assertIsNot(a, b)
28 s.assertIsNone(None)
29 s.assertIsNotNone(a)
30 s.assertTrue(True)
31 s.assertFalse(False)
32
33 def rst_test_doc(s):
34 ''' group of tests
35 --------------
36
37 function startswith rst will only provide its docstring to generate docs
38
39 we set the ``title_marker`` config to '^', and following tests will be grouped under this title
40
41 because rst title markers have these priorities:
42
43 * ``=`` (already used by upper Sphinx structure)
44 * ``-``
45 * ``^``
46
47 '''
48 unittest2doc.update_config(s, title_marker='^')
49
50 #@unittest2doc.stop_after
51 #@unittest2doc.only
52 #@unittest2doc.stop
53 def test_show_variable(s):
54 """ the title marker is `^` (set in previous function rst_test_doc)
55
56 here we test the ``Unittest2Doc.v`` method, to display variables
57 """
58 a = 1
59 b = '2'
60 c = {
61 'normal': 'some data',
62 'secret': 'should be masked',
63 'subsecret': {
64 'good': 1,
65 'bad': 0,
66 'sub': {
67 'good': 1,
68 'bad': 0,
69 }
70 }
71 }
72 d = [1,2,3]
73 unittest2doc.v(['a', 'b', 'c', 'd'], locals(), globals(), mask=[
74 'c.secret',
75 'c.subsecret.bad',
76 'c.subsecret.sub.bad',
77 ])
78 def test_add_more_doc_0(s):
79 """ {"open_input":false}
80
81 here we close the input block by json setting at first line of docstring
82
83 the title marker is still `^` (set in previous function rst_test_doc)
84
85 """
86 pass
87 def test_add_more_doc_1(s):
88 """ {"open_output": false}
89
90 here we close the output block by json setting at first line of docstring
91
92 the title marker is still `^` (set in previous function rst_test_doc)
93 """
94 print('here we close the output ')
95 def test_add_more_doc_2(s):
96 """ after this, set title level to '-', and the current group is finished
97 """
98 unittest2doc.update_config(s, title_marker='-') # set title level to '-' after this test
99 def test_add_more_doc_3(s):
100 """ this test back to top level (because title_marker is set to '-' at last function)
101 """
102 pass
103 def test_title_marker_for_single_test(s):
104 """ {"title_marker": "^"}
105
106 title marker set by above json is only effective in this function
107
108 """
109 print("# the title_marker ^ is only used in this function, and will not affect other tests")
110 print("# after this test, the title_marker is back to previous '-'")
111 def test_output_as_json(s):
112 """ {"output_highlight": "json"}
113
114 the output is highlighted as ``json``
115
116 the title marker here and below are all the default ``-``
117 """
118 print(json.dumps({"1":1, "2":"2", "3": 3.0, "4":4, "a":[{"1":1, "2":2}, {"3":3, "4":4}]}, indent=2))
119 def test_output_as_yaml(s):
120 """ {"output_highlight": "yaml"}
121
122 the output is highlighted as ``yaml``
123 """
124 # pprint({1:1, '2':'2', '3': 3.0, '4':4, 'a':[{1:1, 2:2}, {3:3, 4:4}]}, expand_all=True, indent_guides=False)
125 docpprint({1:1, '2':'2', '3': 3.0, '4':4, 'a':[{1:1, 2:2}, {3:3, 4:4}]})
126 def test_output_as_python(s):
127 """ {"output_highlight": "python"}
128 """
129 # print(pformat_json({1:1, '2':'2', '3': 3.0, '4':4, 'a':[{1:1, 2:2}, {3:3, 4:4}]}))
130 docpprint({1:1, '2':'2', '3': 3.0, '4':4, 'a':[{1:1, 2:2}, {3:3, 4:4}]})
131 from datetime import datetime
132 from collections import OrderedDict
133 d = [
134 {
135 'system_tags': [
136 OrderedDict([('a', 1), ('b', 2), ('c', 3)]),
137 ],
138 'date': datetime.now(),
139 }
140 ]
141 docpprint(d)
142 @unittest2doc.skip
143 def test_skipped(s):
144 raise Exception('this function should be skipped and we should not get this Exception')
145
146 @unittest2doc.expected_failure
147 def test_with_exception(s):
148 """ {"output_processors": ["no_home_folder"]}
149
150 test with exception, the output string will be processed by ``no_home_folder`` processor defined below
151 """
152 raise Exception('expected exception')
153
154 def test_add_foldable_output(s):
155 """ add extra foldable text at end of the doc page
156 """
157 print("# add some output")
158 unittest2doc.add_foldable_output(
159 s, # must pass self into add_foldable_output
160 name='some python code',
161 highlight='python',
162 output=textwrap.dedent('''
163 # some code ...
164 def func(*args, **kwargs):
165 pass
166 '''
167 )
168 )
169
170 # some nested data
171 data = {
172 'a': 1,
173 'b': 2,
174 'c': 3,
175 'd': {
176 'a': 1,
177 'b': 2,
178 'c': 3,
179 }
180 }
181 print("# add some output")
182
183 unittest2doc.add_foldable_output(
184 s, # must pass self into add_foldable_output
185 name='some yaml data',
186 highlight='yaml',
187 output=yaml.dump(data, indent=2)
188 )
189 print("# add some output")
190
191 def test_last(s):
192 """ we use decorator above, make sure that this test is the last one """
193 pass
194
195 class Test2(unittest.TestCase):
196 ''' docstring of class
197 ------------------
198
199 in this class, we test the decorator ``@Unittest2Doc.only``
200
201 '''
202 def setUp(s):
203 print("# this setup function is always called at beginning")
204 def tearDown(s):
205 print("# this tearDown function is always called at end")
206
207 @unittest2doc.only
208 def test_only_1(s):
209 """ when you use ``Unittest2Doc.generate_docs()``, this test will be executed
210
211 Note that if you use ``python -m unittest ...`` framework, all tests will be executed
212
213 Thus the `only` decorator should only be used during your development and testing,
214 e.g., you just want to test one function and want to skip others for speed
215
216 """
217 pass
218
219 def test_other(s):
220 """ when you use ``Unittest2Doc.generate_docs()``, this test will be skipped because of not @unittest2doc.only decorator
221
222 it will be executed anyway if you use ``python -m unittest ...`` framework
223
224 """
225 pass
226
227 @unittest2doc.only
228 def test_only_2(s):
229 """ when you use ``Unittest2Doc.generate_docs()``, this test will be executed
230 """
231 pass
232
233 class Test3(unittest.TestCase):
234 """ docstring of class
235 ------------------
236
237 in this class, we test the decorator ``@Unittest2Doc.stop``
238
239 """
240 def setUp(s):
241 print("# this setup function is always called at beginning")
242 def tearDown(s):
243 print("# this tearDown function is always called at end")
244
245 def test_3(s):
246 """ this should be the only test when you use ``Unittest2Doc.generate_docs()``
247
248 we have a @unittest2doc.stop decorator at next test
249
250 """
251 pass
252
253 @unittest2doc.stop
254 def test_2(s):
255 """ stop before this test when you use ``Unittest2Doc.generate_docs()``
256
257 Note that if you use ``python -m unittest ...`` framework, all tests will be executed
258
259 Thus the `stop` decorator should only be used during your development and testing,
260 e.g., you just want to test above function and want to skip others for speed
261
262 """
263 pass
264
265 def test_1(s):
266 pass
267
268 class Test4(unittest.TestCase):
269 """ docstring of class
270 ------------------
271
272 in this class, we test the decorator ``@Unittest2Doc.stop_after``
273
274 """
275 def setUp(s):
276 print("# this setup function is always called at beginning")
277 def tearDown(s):
278 print("# this tearDown function is always called at end")
279
280 def test_3(s):
281 """ this should be the executed when you use ``Unittest2Doc.generate_docs()`` """
282 pass
283
284 @unittest2doc.stop_after
285 def test_2(s):
286 """ stop after this test when you use ``Unittest2Doc.generate_docs()``
287
288 """
289 pass
290
291 def test_1(s):
292 """ this should be skipped when you use ``Unittest2Doc.generate_docs()``
293
294 Note that if you use ``python -m unittest ...`` framework, all tests will be executed
295
296 Thus the `stop_after` decorator should only be used during your development and testing,
297 e.g., you just want to test above function and want to skip others for speed
298
299 """
300 pass
301
302 class Test5(unittest.TestCase):
303 """ docstring of class
304 ------------------
305
306 in this class, we test the unittest decorator (not unittest2doc decorator)
307
308 """
309 def setUp(s):
310 print("# this setup function is always called at beginning")
311 def tearDown(s):
312 print("# this tearDown function is always called at end")
313
314 @unittest.skip
315 def test_skipped(s):
316 raise Exception('this function should be skipped and we should not get this Exception')
317
318 @unittest.expectedFailure
319 def test_with_exception(s):
320 raise Exception('expected exception')
321
322
323if __name__ == "__main__":
324 def no_home_folder(output):
325 # filter out `${HOME}/*/unittest2doc` to `${PROJECT_ROOT}/unittest2doc`
326 import os
327 home = os.environ.get('HOME')
328 import re
329 pattern = r"{home}/(?:[^/]+/)*?unittest2doc/".format(home=home)
330 replacement = r"${PROJECT_ROOT}/unittest2doc/"
331 output = re.sub(pattern, replacement, output)
332 return output
333 t = Unittest2Doc(
334 testcase=Test(),
335 name='unittest2doc.unittest2doc.Unittest2Doc.basic',
336 ref=':class:`unittest2doc.unittest2doc.Unittest2Doc`',
337 doc_root=Path(__file__).absolute().parent.parent / 'sphinx-docs/source/unittests',
338 output_processors=dict(
339 no_home_folder=no_home_folder,
340 )
341 )
342 t.generate_docs()
343
344 t2 = Unittest2Doc(
345 testcase=Test2(),
346 name='unittest2doc.unittest2doc.Unittest2Doc.test_decorator_only',
347 ref=':class:`unittest2doc.unittest2doc.Unittest2Doc`',
348 doc_root=Path(__file__).absolute().parent.parent / 'sphinx-docs/source/unittests',
349 )
350 t2.generate_docs()
351
352 t3 = Unittest2Doc(
353 testcase=Test3(),
354 name='unittest2doc.unittest2doc.Unittest2Doc.test_decorator_stop',
355 ref=':class:`unittest2doc.unittest2doc.Unittest2Doc`',
356 doc_root=Path(__file__).absolute().parent.parent / 'sphinx-docs/source/unittests',
357 )
358 t3.generate_docs()
359
360 t4 = Unittest2Doc(
361 testcase=Test4(),
362 name='unittest2doc.unittest2doc.Unittest2Doc.test_decorator_stop_after',
363 ref=':class:`unittest2doc.unittest2doc.Unittest2Doc`',
364 doc_root=Path(__file__).absolute().parent.parent / 'sphinx-docs/source/unittests',
365 )
366 t4.generate_docs()
367
368 t5 = Unittest2Doc(
369 testcase=Test5(),
370 name='unittest2doc.unittest2doc.Unittest2Doc.test_unittest_decorator',
371 ref=':class:`unittest2doc.unittest2doc.Unittest2Doc`',
372 doc_root=Path(__file__).absolute().parent.parent / 'sphinx-docs/source/unittests',
373 )
374 t5.generate_docs()
tests/test_pformat_json.py
¶
Generated documentation: unittest2doc.formatter
1import unittest
2import sys
3import os
4import json
5from pathlib import Path
6import unittest2doc
7from unittest2doc import Unittest2Doc, FLog
8from unittest2doc.formatter.json_formatter import pformat_json
9
10class TestJsonFormatter(unittest.TestCase):
11 """ Test cases for json_formatter module's pformat_json function
12 """
13
14 def setUp(self):
15 """ here we also test the FLog class, it is a helper class to print function inputs and outputs """
16 self.flog = FLog(do_print=True, output_suffix='\n', input_suffix=' # ===>')
17 self.frun = self.flog.frun
18
19 def test_basic_format(self):
20 """ {"output_highlight": "python"}
21 """
22 # Test basic dictionary formatting
23 data = {"name": "John", "age": 30, "city": "New York"}
24 # call pformat_json(data) and print the result
25 self.frun(pformat_json, data)
26
27 # Test basic list formatting
28 data = [1, 2, 3, "four", 5.0]
29 # call pformat_json(data) and print the result
30 self.frun(pformat_json, data)
31
32 # Test simple value
33 data = "simple string"
34 # call pformat_json(data) and print the result
35 self.frun(pformat_json, data)
36
37 def test_nested_structures(self):
38 """ {"output_highlight": "python"}
39 """
40 # Test nested dictionary and list
41 data = {
42 "person": {
43 "name": "Alice",
44 "details": {
45 "age": 28,
46 "occupation": "Engineer"
47 }
48 },
49 "hobbies": ["reading", "hiking", {"sport": "tennis"}]
50 }
51 self.frun(pformat_json, data)
52
53 def test_dict_title_comment(self):
54 """ {"output_highlight": "python"}
55 """
56 # Test using string comment for dict title
57 data = {"key1": "value1", "key2": "value2"}
58 self.frun(pformat_json, data, comments="Dictionary Title")
59
60 # Test using __dtitle__ special key
61 data = {"key1": "value1", "key2": "value2"}
62 comments = {"__dtitle__": "Dictionary Title With Special Key"}
63 self.frun(pformat_json, data, comments=comments)
64
65 # Test multi-line dict title
66 comments = {"__dtitle__": "First Line\nSecond Line\nThird Line"}
67 self.frun(pformat_json, data, comments=comments)
68
69 def test_list_title_comment(self):
70 """ {"output_highlight": "python"}
71 """
72 # Test using string comment for list title
73 data = ["item1", "item2", "item3"]
74 self.frun(pformat_json, data, comments="List Title")
75
76 # Test using __ltitle__ special key
77 comments = {"__ltitle__": "List Title With Special Key"}
78 self.frun(pformat_json, data, comments=comments)
79
80 # Test list prefix and suffix comments
81 comments = {
82 "__ltitle__": "List With Prefix and Suffix",
83 "__lprefix__": ["Prefix Line 1", "Prefix Line 2"],
84 "__lsuffix__": ["Suffix Line 1", "Suffix Line 2"]
85 }
86 self.frun(pformat_json, data, comments=comments)
87
88 def test_specific_element_comments(self):
89 """ {"output_highlight": "python"}
90 """
91 # Test comments for specific dict keys
92 data = {"name": "Bob", "age": 45, "city": "Boston"}
93 comments = {
94 "name": "Person's name",
95 "age": "Person's age in years",
96 "city": "City of residence"
97 }
98 self.frun(pformat_json, data, comments=comments)
99
100 # Test comments for specific list indices
101 data = ["Python", "Java", "JavaScript", "C++"]
102 comments = {
103 0: "My favorite language",
104 2: "Web development language"
105 }
106 self.frun(pformat_json, data, comments=comments)
107
108 def test_callable_comments(self):
109 """ {"output_highlight": "python"}
110 """
111 # Test callable comments for dict
112 data = {"price": 129.99, "quantity": 5, "discount": 0.15}
113
114 def calc_total(key, value):
115 if key == "price":
116 return f"Base price: ${value}"
117 elif key == "quantity":
118 return f"Order quantity of {value} units"
119 elif key == "discount":
120 return f"Discount rate of {int(value*100)}%"
121 return ""
122
123 comments = {
124 "price": calc_total,
125 "quantity": calc_total,
126 "discount": calc_total
127 }
128 self.frun(pformat_json, data, comments=comments)
129
130 def test_compact_mode(self):
131 """ {"output_highlight": "python"}
132 """
133 # Test compact mode for dict
134 data = {"a": 1, "b": 2, "c": 3, "d": 4, "e": 5, "f": 6}
135 comments = {
136 "__lcompact__": 30,
137 "a": "First item",
138 "c": "Third item",
139 "e": "Fifth item"
140 }
141 self.frun(pformat_json, data, comments=comments)
142
143 comments['__lcompact__'] = 25
144 self.frun(pformat_json, data, comments=comments)
145
146 comments['__lcompact__'] = 20
147 self.frun(pformat_json, data, comments=comments)
148
149 comments['__lcompact__'] = 15
150 self.frun(pformat_json, data, comments=comments)
151
152 comments.pop('__lcompact__')
153 self.frun(pformat_json, data, comments=comments)
154
155 self.frun(pformat_json, data, comments=comments, compact=15)
156
157 self.frun(pformat_json, data, comments=comments, compact=20)
158
159 self.frun(pformat_json, data, comments=comments, compact=30)
160
161
162 def test_recursive_comments(self):
163 """ {"output_highlight": "python"}
164 """
165 # Test __lsub__ for all dict elements
166 data = {
167 "user": {
168 "id": 12345,
169 "name": "Alice Smith",
170 "email": "alice@example.com"
171 },
172 "settings": {
173 "theme": "dark",
174 "notifications": True
175 }
176 }
177 comments = {
178 "__dtitle__": "User Profile",
179 "__lsub__": {
180 "id": "Unique identifier",
181 "name": "Full name",
182 "theme": "UI theme preference"
183 }
184 }
185 self.frun(pformat_json, data, comments=comments)
186
187 # Test __llist__ and __ldict__ for typed elements
188 data = [
189 {"type": "book", "title": "Python Programming"},
190 [1, 2, 3],
191 {"type": "video", "title": "Advanced Python"}
192 ]
193 comments = {
194 "__llist__": {
195 "__lcompact__": 40
196 },
197 "__ldict__": {
198 "type": "Content type",
199 "title": "Content title"
200 }
201 }
202 self.frun(pformat_json, data, comments=comments)
203
204 def test_custom_indentation(self):
205 """ {"output_highlight": "python"}
206 """
207 # Test custom indentation
208 data = {
209 "outer": {
210 "middle": {
211 "inner": "value"
212 }
213 }
214 }
215 # Test with different indent values
216 self.frun(pformat_json, data, indent=2)
217 self.frun(pformat_json, data, indent=4)
218
219
220 def test_custom_comment_prefix(self):
221 """ {"output_highlight": "python"}
222 """
223 # Test custom comment prefix
224 data = {"name": "John", "age": 30}
225 comments = {
226 "__dtitle__": "Person Info",
227 "name": "Person's name",
228 "age": "Age in years"
229 }
230 self.frun(pformat_json, data, comments=comments, comment_prefix="// ")
231
232 def test_different_compact_values(self):
233 """ {"output_highlight": "python"}
234 """
235 # Same data with different __lcompact__ values
236 data = {"item1": 100, "item2": 200, "item3": 300, "item4": 400, "item5": 500}
237 comments = {
238 "item1": "First item comment",
239 "item3": "Third item comment",
240 "item5": "Fifth item comment"
241 }
242
243 # No compact mode
244 print("\nNo compact mode:")
245 self.frun(pformat_json, data, comments=comments)
246
247 # Very wide compact mode (essentially same as no compact)
248 print("\nCompact mode (width=100):")
249 comments_wide = comments.copy()
250 comments_wide["__lcompact__"] = 100
251 self.frun(pformat_json, data, comments=comments_wide)
252
253 # Medium compact mode
254 print("\nCompact mode (width=50):")
255 comments_medium = comments.copy()
256 comments_medium["__lcompact__"] = 50
257 self.frun(pformat_json, data, comments=comments_medium)
258
259 # Narrow compact mode
260 print("\nCompact mode (width=25):")
261 comments_narrow = comments.copy()
262 comments_narrow["__lcompact__"] = 25
263 self.frun(pformat_json, data, comments=comments_narrow)
264
265 def test_deeply_nested_structure(self):
266 """ {"output_highlight": "python"}
267 """
268 # Create a deeply nested structure to test indentation handling
269 data = {
270 "level1": {
271 "level2": {
272 "level3": {
273 "level4": {
274 "level5": {
275 "value": "deeply nested value"
276 },
277 "array": [1, 2, [3, 4, [5, 6]]]
278 }
279 }
280 }
281 }
282 }
283
284 comments = {
285 "__dtitle__": "Deep Nesting Test",
286 "__lsub__": {
287 "level1": "First level",
288 "level2": "Second level",
289 "level3": "Third level",
290 "level4": "Fourth level",
291 "level5": "Fifth level",
292 "value": "The final value",
293 "array": "Array of values"
294 }
295 }
296
297 self.frun(pformat_json, data, comments=comments, debug=True)
298
299 def test_comprehensive_example(self):
300 """ {"output_highlight": "python"}
301 """
302 # Test case combining multiple features
303 data = {
304 "metadata": {
305 "title": "Comprehensive Example",
306 "version": 1.5,
307 "tags": ["test", "example", "comprehensive"]
308 },
309 "configuration": {
310 "enabled": True,
311 "options": {
312 "debug": False,
313 "verbose": True,
314 "timeout": 30
315 }
316 },
317 "data_points": [
318 {"id": 1, "value": 10.5, "label": "Point A"},
319 {"id": 2, "value": 20.7, "label": "Point B"},
320 {"id": 3, "value": 15.3, "label": "Point C"}
321 ],
322 "statistics": {
323 "count": 3,
324 "average": 15.5,
325 "max": 20.7,
326 "min": 10.5
327 }
328 }
329
330 # Define comprehensive comments
331 def format_stat(key, value):
332 if key == "average":
333 return f"Average value: {value:.1f}"
334 elif key == "max":
335 return f"Maximum value: {value:.1f}"
336 elif key == "min":
337 return f"Minimum value: {value:.1f}"
338 return str(value)
339
340 comments = {
341 "__dtitle__": "Complete Feature Demonstration",
342 "__lcompact__": 60,
343 "metadata": {
344 "__dtitle__": "Document Metadata",
345 "title": "The title of this example",
346 "tags": "Keywords for categorization"
347 },
348 "configuration": {
349 "__dtitle__": "System Configuration",
350 "__lcompact__": 40,
351 "options": {
352 "__dtitle__": "Available Options",
353 "debug": "Enable debug mode",
354 "verbose": "Show detailed output",
355 "timeout": "Operation timeout in seconds"
356 }
357 },
358 "data_points": {
359 "__ltitle__": "Measurement Data",
360 "__lprefix__": ["Array of data point objects", "Each with id, value and label"],
361 "__ldict__": {
362 "id": "Unique identifier",
363 "value": "Measurement value",
364 "label": "Display name"
365 }
366 },
367 "statistics": {
368 "__dtitle__": "Statistical Analysis",
369 "count": "Number of data points",
370 "average": format_stat,
371 "max": format_stat,
372 "min": format_stat
373 }
374 }
375
376 result = pformat_json(data, comments=comments)
377 print(result)
378
379if __name__ == "__main__":
380 t = unittest2doc.Unittest2Doc(
381 testcase=TestJsonFormatter(),
382 name='unittest2doc.formatter.pformat_json',
383 ref=':func:`unittest2doc.formatter.pformat_json`',
384 doc_root=Path(__file__).absolute().parent.parent / 'sphinx-docs/source/unittests',
385 )
386 t.generate_docs()
tests/test_exec_tool.py
¶
Generated documentation: unittest2doc.utils.exec_tool
1from pathlib import Path
2import unittest2doc
3from unittest2doc import Unittest2Doc
4from unittest2doc.utils.exec_tool import filter_after_comment_by, load_module, collect_module_attr, load_main
5
6""" Note
7This is a special way to run test cases in the ``if __name__ == "__main__"`` block of a test file (src/unittest2doc/utils/exec_tool.py),
8Using the helper functions from that file.
9"""
10
11if __name__ == "__main__":
12 test_module = "unittest2doc.utils.exec_tool"
13 module = load_module(test_module)
14 module_globals = collect_module_attr(module, all=True)
15
16 main = load_main(
17 module,
18 code_filter=filter_after_comment_by("Run unittests"),
19 globals=module_globals,
20 add_code_object=True,
21 )
22
23 t = unittest2doc.Unittest2Doc(
24 testcase=main['TestExecTool'](),
25 name='unittest2doc.utils.exec_tool',
26 ref=':mod:`unittest2doc.utils.exec_tool`',
27 doc_root=Path(__file__).absolute().parent.parent / 'sphinx-docs/source/unittests',
28 open_input=False,
29 )
30 t.generate_docs()
Finally, let’s look at the generated documentation
API documentation comes from our project source code, generated by sphinx’s autosummary feature, which is not the focus of this article
Unit test documentation comes from our test code, please explore the results yourself and compare with the test code