Compare commits
1681 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e56544cb58 | ||
|
|
f9cc704b1a | ||
|
|
bd57307a39 | ||
|
|
667c6463ab | ||
|
|
4e594552eb | ||
|
|
955dc6d18a | ||
|
|
3ddbc7fb2a | ||
|
|
bb60736a6f | ||
|
|
35b3b1097f | ||
|
|
01082fea12 | ||
|
|
a511b98da9 | ||
|
|
487659d8b1 | ||
|
|
955e542210 | ||
|
|
29bb0eda27 | ||
|
|
a98270eac0 | ||
|
|
1aac64573f | ||
|
|
505c3340bf | ||
|
|
7a69365486 | ||
|
|
3c82b1cb97 | ||
|
|
0215bcd84e | ||
|
|
01b9774e3b | ||
|
|
9859d37cf6 | ||
|
|
1c7aeb670a | ||
|
|
691c706fcc | ||
|
|
a4adf511fc | ||
|
|
4265ab3a41 | ||
|
|
b135f5af8d | ||
|
|
daff9066c0 | ||
|
|
43eab917a1 | ||
|
|
0e569faca2 | ||
|
|
a7c235732a | ||
|
|
cec2183aeb | ||
|
|
c049fd85ab | ||
|
|
62381125e7 | ||
|
|
a7ede64f42 | ||
|
|
307652202c | ||
|
|
a287aea00e | ||
|
|
6bf6265c59 | ||
|
|
7c26a65865 | ||
|
|
1e3205e7cf | ||
|
|
32dac18f38 | ||
|
|
e5f4c47cd5 | ||
|
|
05bfe73cf9 | ||
|
|
9f3bfe82cf | ||
|
|
c3a8e609f9 | ||
|
|
d9c4e646c4 | ||
|
|
8ccc0177c8 | ||
|
|
eaf7ce9a99 | ||
|
|
ac3056c5a2 | ||
|
|
409d61b972 | ||
|
|
6ead01aacd | ||
|
|
4c37dca011 | ||
|
|
44c10dbd5f | ||
|
|
0f11a7a73d | ||
|
|
d50198a3ff | ||
|
|
ac052a98ad | ||
|
|
f4a84a8dfd | ||
|
|
1049a38cee | ||
|
|
d7f082519a | ||
|
|
2d613a03b3 | ||
|
|
28c6c5bb71 | ||
|
|
6b9d729ed3 | ||
|
|
0ba774a7c3 | ||
|
|
0a62c4ac04 | ||
|
|
137255816e | ||
|
|
2f1b192fe6 | ||
|
|
7183335e62 | ||
|
|
0822a1e53a | ||
|
|
cb94fd31c8 | ||
|
|
fa75d818cf | ||
|
|
1434b66c35 | ||
|
|
a24132ddc5 | ||
|
|
ed2425119f | ||
|
|
a295a3ddaf | ||
|
|
3b3ce0e799 | ||
|
|
1a61265b1e | ||
|
|
67ac878ccf | ||
|
|
3eb4973065 | ||
|
|
39ba996133 | ||
|
|
cb481a354a | ||
|
|
300f78556f | ||
|
|
0db9dade65 | ||
|
|
a77c83a4c3 | ||
|
|
0767f080a4 | ||
|
|
2498aeaaa5 | ||
|
|
345df99db7 | ||
|
|
8665f5652a | ||
|
|
beb457c75e | ||
|
|
f7e81dab9a | ||
|
|
ee936b27a8 | ||
|
|
e0ce8b79d5 | ||
|
|
8ffa3aa65d | ||
|
|
b095e0de47 | ||
|
|
2f065a555f | ||
|
|
ec76f70d71 | ||
|
|
65e6038111 | ||
|
|
9f6da8cbeb | ||
|
|
ba72b480b9 | ||
|
|
ba76080b59 | ||
|
|
5ecac3c861 | ||
|
|
0a19439677 | ||
|
|
e772bb6480 | ||
|
|
7560a7b808 | ||
|
|
6c8d8a99f4 | ||
|
|
78de9d4677 | ||
|
|
23a0b532db | ||
|
|
9fce0bdd88 | ||
|
|
28fabc52bd | ||
|
|
5f95dce956 | ||
|
|
75d0b899bb | ||
|
|
7f90e74e02 | ||
|
|
cf7761f91f | ||
|
|
ef0915e1db | ||
|
|
68c486a25f | ||
|
|
f5fab2bfa1 | ||
|
|
f43b54aaeb | ||
|
|
aa06e6c8f3 | ||
|
|
bf3aa72a29 | ||
|
|
198fcd8a6f | ||
|
|
f7e925dcc1 | ||
|
|
cf6632a57a | ||
|
|
1a66a503b6 | ||
|
|
82763a293a | ||
|
|
73e0bf92f7 | ||
|
|
0d5ed57b40 | ||
|
|
1ce45a6f67 | ||
|
|
8db6642515 | ||
|
|
29e336bd9b | ||
|
|
d3e1907899 | ||
|
|
b5b710b3ae | ||
|
|
0d3958e8de | ||
|
|
9064eea216 | ||
|
|
dc6e7b9fcf | ||
|
|
b43ebb7d65 | ||
|
|
8a5e72c936 | ||
|
|
cd924b66ca | ||
|
|
1076a7e61d | ||
|
|
cff58457dd | ||
|
|
b3f4398d64 | ||
|
|
2ca47cb3f5 | ||
|
|
d19fe3c410 | ||
|
|
2de145f372 | ||
|
|
942fd91995 | ||
|
|
cb15e7c700 | ||
|
|
2959fb3198 | ||
|
|
aa13c625da | ||
|
|
958374addb | ||
|
|
6c2f673daf | ||
|
|
fd2fb36eac | ||
|
|
ab39502c98 | ||
|
|
50c7b5d2b5 | ||
|
|
3bdcd6b4f3 | ||
|
|
d89c88478d | ||
|
|
635916052c | ||
|
|
bd34bd872a | ||
|
|
1d8f668e10 | ||
|
|
28343bdcbd | ||
|
|
ebfe8eabf5 | ||
|
|
829cc5a242 | ||
|
|
7fa27485df | ||
|
|
b2839c4084 | ||
|
|
880e368607 | ||
|
|
b9111fe677 | ||
|
|
401c3d1109 | ||
|
|
2a724a1c81 | ||
|
|
52ad5a1591 | ||
|
|
4abf95ba4f | ||
|
|
f163b37f6a | ||
|
|
a82dd2f064 | ||
|
|
1873dc6a8a | ||
|
|
8c47db724c | ||
|
|
693e9d0733 | ||
|
|
5c09cc16f2 | ||
|
|
0824789459 | ||
|
|
ec4ca59bf0 | ||
|
|
3c94f32e77 | ||
|
|
d66b6c8371 | ||
|
|
6e687c4354 | ||
|
|
13d8577451 | ||
|
|
3c7438969a | ||
|
|
66cfc66d63 | ||
|
|
13c4b7d212 | ||
|
|
8c7d9124ba | ||
|
|
240d314f36 | ||
|
|
27c9d80a7e | ||
|
|
cb828ebe70 | ||
|
|
d35d09f82d | ||
|
|
f0feb6c83a | ||
|
|
dcbb9c1f5a | ||
|
|
c05fcc8641 | ||
|
|
c98e7aed94 | ||
|
|
c0231ae780 | ||
|
|
675e9507d8 | ||
|
|
0b532fda76 | ||
|
|
7d1c697c30 | ||
|
|
65aee1e0c8 | ||
|
|
8d413c1926 | ||
|
|
104f8fc836 | ||
|
|
157515f3c5 | ||
|
|
183750fa86 | ||
|
|
fd8f92d0e7 | ||
|
|
1e34734f8f | ||
|
|
7c2e843358 | ||
|
|
79414164c2 | ||
|
|
0cca7f831a | ||
|
|
1f4ae789b8 | ||
|
|
3f46315a9d | ||
|
|
e03b8b9e95 | ||
|
|
eb7f950e20 | ||
|
|
848b735a06 | ||
|
|
7337cce332 | ||
|
|
7a5c0a01bc | ||
|
|
faf222f8fb | ||
|
|
9258fd1296 | ||
|
|
7440cece59 | ||
|
|
f1332872a6 | ||
|
|
ac12245f5f | ||
|
|
d9eab12ee0 | ||
|
|
2e756d698b | ||
|
|
15e235c63e | ||
|
|
65c23017c7 | ||
|
|
bb7608c56f | ||
|
|
8a3f40996a | ||
|
|
6f1d358a0c | ||
|
|
3ad315bcee | ||
|
|
129600d698 | ||
|
|
a96710dd8a | ||
|
|
54e08b729f | ||
|
|
01606315aa | ||
|
|
8f2f3bb1fa | ||
|
|
499fda2349 | ||
|
|
c7aacc96bb | ||
|
|
0394ebffee | ||
|
|
0225be53a2 | ||
|
|
da5add1294 | ||
|
|
11f1f79222 | ||
|
|
3f1fb62584 | ||
|
|
14bf4cdf44 | ||
|
|
56dcc9e1f8 | ||
|
|
55a570e513 | ||
|
|
2dca68b863 | ||
|
|
d7ee3dac2c | ||
|
|
866904ab80 | ||
|
|
35a57a0dfb | ||
|
|
6afbac29a3 | ||
|
|
d23fbab188 | ||
|
|
374c4325a8 | ||
|
|
2c071a060e | ||
|
|
0f8b462677 | ||
|
|
898028cb22 | ||
|
|
4480d3e518 | ||
|
|
4868d0d97a | ||
|
|
8052c7c5c6 | ||
|
|
13d750db20 | ||
|
|
e98627223f | ||
|
|
c89e379f49 | ||
|
|
24a66db8d3 | ||
|
|
4027254a4b | ||
|
|
37c37963c4 | ||
|
|
666acc9b7a | ||
|
|
a740ef2036 | ||
|
|
4c590e002f | ||
|
|
26b06bdb45 | ||
|
|
d5f1d7aebe | ||
|
|
f7747f5dd6 | ||
|
|
c224c4f1d6 | ||
|
|
2c402f4bd9 | ||
|
|
602cd5e21f | ||
|
|
57e5bd0664 | ||
|
|
31738155b5 | ||
|
|
9db1823707 | ||
|
|
89dfde9535 | ||
|
|
39a43dbae1 | ||
|
|
c1167ac552 | ||
|
|
628ff4d619 | ||
|
|
add912ff68 | ||
|
|
b77c876481 | ||
|
|
9b78a216a2 | ||
|
|
7aff51ee83 | ||
|
|
e33736c791 | ||
|
|
d5cc0f2a62 | ||
|
|
2a23fdab9f | ||
|
|
aaa7e837cc | ||
|
|
71c8ca7d3d | ||
|
|
56a0dd7658 | ||
|
|
7a82285b03 | ||
|
|
4cda7093f6 | ||
|
|
6a9bf2852a | ||
|
|
3e669a262a | ||
|
|
f1b8431d99 | ||
|
|
a43ba78d3b | ||
|
|
2180d9ef6d | ||
|
|
0371a3023a | ||
|
|
d7588b8d40 | ||
|
|
b62b549f5f | ||
|
|
60a358fa2d | ||
|
|
bb29f31d22 | ||
|
|
b1928f878d | ||
|
|
8651d880a0 | ||
|
|
95824c588a | ||
|
|
c54cbd63c8 | ||
|
|
1db132290f | ||
|
|
caa08ebd45 | ||
|
|
b08ae4449b | ||
|
|
776a632170 | ||
|
|
d74a975f82 | ||
|
|
dfe54cd82f | ||
|
|
2b9522e9da | ||
|
|
528ee3e1c5 | ||
|
|
9677099acf | ||
|
|
1b0e8d73d5 | ||
|
|
50b846e9d3 | ||
|
|
fa9791127a | ||
|
|
2d9b432613 | ||
|
|
5d8da88e9e | ||
|
|
0da690c8e9 | ||
|
|
e32c903fb6 | ||
|
|
0ed7aa2db6 | ||
|
|
85cc12e328 | ||
|
|
c470ade0a5 | ||
|
|
9972c78cfa | ||
|
|
3a17c1b30b | ||
|
|
7b35405033 | ||
|
|
aa1955de72 | ||
|
|
7e58defc15 | ||
|
|
f2b7809d5d | ||
|
|
647d89c444 | ||
|
|
683b2632b4 | ||
|
|
13f7f27fd2 | ||
|
|
279733a30b | ||
|
|
be91c4d932 | ||
|
|
497cd87fdd | ||
|
|
94a05e513e | ||
|
|
9021194efd | ||
|
|
d4a76a0b99 | ||
|
|
789a3662ce | ||
|
|
93a68cdfb4 | ||
|
|
4f9bf028f5 | ||
|
|
4bc0415720 | ||
|
|
844d660d5c | ||
|
|
2c6453c72d | ||
|
|
e6ffa78e59 | ||
|
|
58bfc7736f | ||
|
|
97f0a20ca9 | ||
|
|
55d2fe076f | ||
|
|
5e39eb91bb | ||
|
|
fd2f320485 | ||
|
|
73d918db55 | ||
|
|
7ee244476a | ||
|
|
bf39e89946 | ||
|
|
3c9b46f781 | ||
|
|
f7bfbb557e | ||
|
|
45af361a67 | ||
|
|
4e723d6750 | ||
|
|
65fbdf2568 | ||
|
|
5ef507982c | ||
|
|
2479a91e92 | ||
|
|
3e0e31a364 | ||
|
|
c9923a3a5c | ||
|
|
1be49e713a | ||
|
|
37fb50a3ed | ||
|
|
790806e865 | ||
|
|
2ea22218ff | ||
|
|
629eb3ec6a | ||
|
|
8edf68f3c0 | ||
|
|
3afee36ebb | ||
|
|
fcbe66feba | ||
|
|
eb90f3d1c8 | ||
|
|
6854ff2acc | ||
|
|
53234bf613 | ||
|
|
6f851e6cbb | ||
|
|
9a34d88c8d | ||
|
|
d91a5d3cd7 | ||
|
|
f755ff6af1 | ||
|
|
d638da5821 | ||
|
|
0fb52416b1 | ||
|
|
6a2d844c5d | ||
|
|
a48feb3261 | ||
|
|
6cf8e71dca | ||
|
|
994c32235c | ||
|
|
d81f758285 | ||
|
|
fdb6e35b1b | ||
|
|
7efdd5063b | ||
|
|
bfba33ec9e | ||
|
|
3f3f3e7a29 | ||
|
|
aaf40314d1 | ||
|
|
3f2344e8f7 | ||
|
|
8c7eb82363 | ||
|
|
1aefb24b37 | ||
|
|
ed85c83154 | ||
|
|
bd647fdd8b | ||
|
|
80ac910a24 | ||
|
|
4db5488ed8 | ||
|
|
cfbfa53f2b | ||
|
|
db50a975fd | ||
|
|
019455298b | ||
|
|
64a6365227 | ||
|
|
b991810f32 | ||
|
|
4d5780facf | ||
|
|
81e3f3cf95 | ||
|
|
2280f28596 | ||
|
|
f8c9a7b86d | ||
|
|
98b212cbfb | ||
|
|
d72fb73fa0 | ||
|
|
a54e2e19f5 | ||
|
|
37fce6c6e5 | ||
|
|
23aa3bb0ae | ||
|
|
95714436a1 | ||
|
|
f43fb13179 | ||
|
|
3f5b078462 | ||
|
|
63c4802be0 | ||
|
|
a24933b0a6 | ||
|
|
380ca8f880 | ||
|
|
61dcb84f0d | ||
|
|
4cd08f9b52 | ||
|
|
52e695b329 | ||
|
|
9a89783fbb | ||
|
|
01a094cc43 | ||
|
|
3ce31b6370 | ||
|
|
3d01dd3adf | ||
|
|
a37b902afe | ||
|
|
12b76b6261 | ||
|
|
b713460cc7 | ||
|
|
0627d92df2 | ||
|
|
b38a4e8e11 | ||
|
|
689ce112e7 | ||
|
|
87fc5a5455 | ||
|
|
bbfc8d1501 | ||
|
|
ab6ed381ac | ||
|
|
1cfea5f1b3 | ||
|
|
cf27af734e | ||
|
|
8b3b10b14b | ||
|
|
103d6146b0 | ||
|
|
065fa17124 | ||
|
|
43e8576ca3 | ||
|
|
d8fa434d39 | ||
|
|
177af032d2 | ||
|
|
355eb5adfb | ||
|
|
2bd619ecb0 | ||
|
|
2b92fee1c3 | ||
|
|
240828d912 | ||
|
|
c94e9b6145 | ||
|
|
7513d87b15 | ||
|
|
bc345ac980 | ||
|
|
4cc05a657d | ||
|
|
ad15efc7ea | ||
|
|
e3dcf1f39d | ||
|
|
701d0351a6 | ||
|
|
bad7ab721a | ||
|
|
ede8b87560 | ||
|
|
52780f39ce | ||
|
|
d637d1b2ac | ||
|
|
47447963b7 | ||
|
|
f586d627b3 | ||
|
|
108fad1ac0 | ||
|
|
18c2ff6625 | ||
|
|
c5a549b5bb | ||
|
|
75cda6de53 | ||
|
|
e868bb647d | ||
|
|
40c5a9d9f2 | ||
|
|
83d18096d4 | ||
|
|
28aa38ece6 | ||
|
|
f0cee593f2 | ||
|
|
4f597f011d | ||
|
|
9f5c356784 | ||
|
|
47022b36cb | ||
|
|
042a10f2e2 | ||
|
|
0405697a78 | ||
|
|
918268774b | ||
|
|
ccd87f9e80 | ||
|
|
65c2a81924 | ||
|
|
450d264623 | ||
|
|
0fd1f3038c | ||
|
|
9f8b566ea9 | ||
|
|
3656885d0d | ||
|
|
577b0dffe7 | ||
|
|
8f5cb461a8 | ||
|
|
be84ba8a33 | ||
|
|
d9eafbdee3 | ||
|
|
190456aeda | ||
|
|
aab5687093 | ||
|
|
5fdc2d7744 | ||
|
|
25cef55e4a | ||
|
|
be2be040f9 | ||
|
|
0f4992c223 | ||
|
|
23cd68b667 | ||
|
|
79ef04888e | ||
|
|
1f8fd421c4 | ||
|
|
76d50801fd | ||
|
|
9349c72cac | ||
|
|
883db6a216 | ||
|
|
898e869bcd | ||
|
|
0a91e181af | ||
|
|
6a6b6d8195 | ||
|
|
b95bb29fc2 | ||
|
|
2a3d643bdf | ||
|
|
2cfd9c259f | ||
|
|
927e95c65a | ||
|
|
9657166a22 | ||
|
|
f223d26111 | ||
|
|
1d6bbab2b0 | ||
|
|
606d728697 | ||
|
|
3a8b08275d | ||
|
|
e770db4c91 | ||
|
|
63099bc282 | ||
|
|
4f57d40a43 | ||
|
|
230f736fcd | ||
|
|
1b381d5277 | ||
|
|
2125d04501 | ||
|
|
5976f36240 | ||
|
|
ce78c9adef | ||
|
|
6d393c5dc8 | ||
|
|
8292644015 | ||
|
|
96fd44e040 | ||
|
|
5dcf85c17e | ||
|
|
ca1efd57bd | ||
|
|
c63320cc31 | ||
|
|
4df529e5b9 | ||
|
|
a91fe1fedd | ||
|
|
3f1ec520fc | ||
|
|
5034399d7a | ||
|
|
99057555e1 | ||
|
|
b8781ff868 | ||
|
|
ef053193b5 | ||
|
|
28ac469eaa | ||
|
|
5999782768 | ||
|
|
6e68532bb3 | ||
|
|
602a290fcc | ||
|
|
876df87672 | ||
|
|
b7f8f53127 | ||
|
|
5c016d1021 | ||
|
|
25e728c78b | ||
|
|
642ef6dc1b | ||
|
|
a6dc283133 | ||
|
|
4d49ba6529 | ||
|
|
733f43b02e | ||
|
|
acdf30a523 | ||
|
|
e4fe41ebb7 | ||
|
|
cafb13c95f | ||
|
|
a8f4e56d8c | ||
|
|
e2ac4b782a | ||
|
|
917195ea8e | ||
|
|
e7cd00ac92 | ||
|
|
693c3b7f61 | ||
|
|
fb3ae5eaa9 | ||
|
|
c8d23c206b | ||
|
|
c5de8e8c50 | ||
|
|
f360147758 | ||
|
|
65bd1b8a93 | ||
|
|
882f3a4cd7 | ||
|
|
56e430f74e | ||
|
|
654d8da9f7 | ||
|
|
ace3a02cd4 | ||
|
|
f013a5e8c1 | ||
|
|
fbd8ff9502 | ||
|
|
737a1bf947 | ||
|
|
843ca03770 | ||
|
|
5cbc06a453 | ||
|
|
da23aa3419 | ||
|
|
28bf3816e7 | ||
|
|
d6ce2e5858 | ||
|
|
5ac498ea96 | ||
|
|
6765aca0d1 | ||
|
|
72fc43952b | ||
|
|
6896dbc5ca | ||
|
|
49c6aebbc7 | ||
|
|
fb12d2a612 | ||
|
|
8e51563384 | ||
|
|
61dfd0a94f | ||
|
|
f9cafd1c94 | ||
|
|
b10f28949d | ||
|
|
b0f090890c | ||
|
|
f0a4a13e48 | ||
|
|
ff80464b47 | ||
|
|
480dd9e6d6 | ||
|
|
ceb4f3f701 | ||
|
|
ea3ebec117 | ||
|
|
bf3b26b3f7 | ||
|
|
84569ca4da | ||
|
|
af21e6b45c | ||
|
|
5c5966f62d | ||
|
|
31b1c4ca0c | ||
|
|
61b76c7f5f | ||
|
|
c35544a0f7 | ||
|
|
5bc3ad8e27 | ||
|
|
a930ca0b45 | ||
|
|
d4b85da8c7 | ||
|
|
0025e4408f | ||
|
|
81cc73103a | ||
|
|
97d8e9fbec | ||
|
|
220a2a1bc9 | ||
|
|
2e2e895b4b | ||
|
|
bce45052a6 | ||
|
|
2f48ae4e66 | ||
|
|
2dfbed11b4 | ||
|
|
24c95c78e7 | ||
|
|
6061ecf95a | ||
|
|
e032904413 | ||
|
|
ece774f0eb | ||
|
|
bc49d6ff99 | ||
|
|
2b9ca34280 | ||
|
|
e38561037d | ||
|
|
0a57124063 | ||
|
|
13f02af97d | ||
|
|
f2ed796c41 | ||
|
|
b3f8fabac8 | ||
|
|
22d91a3c3a | ||
|
|
322a0f0a33 | ||
|
|
58149459a5 | ||
|
|
852fb6a4ae | ||
|
|
437d6452c1 | ||
|
|
e37ff3042e | ||
|
|
ecd2de25a1 | ||
|
|
c607697400 | ||
|
|
0996f3dbc5 | ||
|
|
a0dbf2ab99 | ||
|
|
ddbe733666 | ||
|
|
470e686a70 | ||
|
|
765f75a8f1 | ||
|
|
6b5152ae13 | ||
|
|
10ca84ffc5 | ||
|
|
e393a73890 | ||
|
|
bed3918cbc | ||
|
|
31dfbb4668 | ||
|
|
b4d75ad31d | ||
|
|
ec6d0fa4d7 | ||
|
|
fa8a658458 | ||
|
|
66f20b6f5e | ||
|
|
6ba5e3c071 | ||
|
|
b900b4155f | ||
|
|
364ae5d723 | ||
|
|
84c7fef836 | ||
|
|
aaea4e52ef | ||
|
|
0cacdadc97 | ||
|
|
6c56070df1 | ||
|
|
f9f41e69a8 | ||
|
|
b2ce6f3200 | ||
|
|
e0b584d048 | ||
|
|
a0ff5deabf | ||
|
|
97b85a17ae | ||
|
|
de7ba5958b | ||
|
|
8a498700da | ||
|
|
45c894b73f | ||
|
|
65342db7a4 | ||
|
|
d391274f39 | ||
|
|
c5fa1d1c3e | ||
|
|
a304dbb519 | ||
|
|
d19df5efa2 | ||
|
|
6663cb054c | ||
|
|
b27e40cbf1 | ||
|
|
ee52a8a5f8 | ||
|
|
8f04bd003c | ||
|
|
93fd9debe3 | ||
|
|
ff428bfee1 | ||
|
|
0f7c7a99bf | ||
|
|
041ea3704b | ||
|
|
d94b4b031f | ||
|
|
e253029ad0 | ||
|
|
43617a8c47 | ||
|
|
56bf7446f6 | ||
|
|
8e42c5b7db | ||
|
|
0571e1ee8e | ||
|
|
b0a6161d41 | ||
|
|
06fa2bc0b8 | ||
|
|
8abd4aec6e | ||
|
|
858010e214 | ||
|
|
96424272a1 | ||
|
|
e44a2ef653 | ||
|
|
c6e3ff3ce5 | ||
|
|
a31098a74e | ||
|
|
7e8044f9b8 | ||
|
|
b81173ea0c | ||
|
|
c8f7e50c47 | ||
|
|
c0e53a61e6 | ||
|
|
494ac28a32 | ||
|
|
dc75b6af47 | ||
|
|
7573747cda | ||
|
|
2db05b6582 | ||
|
|
4318698bae | ||
|
|
e668aaf885 | ||
|
|
58e6a09db4 | ||
|
|
6718a2f028 | ||
|
|
c081c01eb1 | ||
|
|
f8e1d58e8f | ||
|
|
18024467ff | ||
|
|
2ad36b1402 | ||
|
|
6ca3e1e425 | ||
|
|
ecd072ea94 | ||
|
|
dda21935a7 | ||
|
|
cc464f6b96 | ||
|
|
6a43c8cd94 | ||
|
|
63fe547d9f | ||
|
|
b709e61892 | ||
|
|
465b2d998a | ||
|
|
184ef92f0b | ||
|
|
73bbff2b74 | ||
|
|
4ccaa987d4 | ||
|
|
3a4a815c41 | ||
|
|
dae455e8a3 | ||
|
|
0594dba5ce | ||
|
|
f1183c2422 | ||
|
|
685ca96c71 | ||
|
|
ccf6c3cb46 | ||
|
|
ceca35b94a | ||
|
|
803d68847b | ||
|
|
0bd02cd1bc | ||
|
|
0b8b006db4 | ||
|
|
73b74c74c9 | ||
|
|
4d782dc13f | ||
|
|
e1756fc631 | ||
|
|
5d7686951c | ||
|
|
80c5f6e609 | ||
|
|
0b47e51d08 | ||
|
|
5eeb5ee960 | ||
|
|
ed2b715f4c | ||
|
|
7e08e09473 | ||
|
|
3ac43314ee | ||
|
|
972410f8b6 | ||
|
|
0cf267f187 | ||
|
|
7161f5b372 | ||
|
|
2051e30b9b | ||
|
|
f339147d12 | ||
|
|
c04767f946 | ||
|
|
6d040370ed | ||
|
|
1d466d0aa7 | ||
|
|
ef4dec0bcf | ||
|
|
d76735f9e5 | ||
|
|
d1a48ad68f | ||
|
|
6d259c400e | ||
|
|
d3686361ba | ||
|
|
8605ed2a15 | ||
|
|
8b34d981fc | ||
|
|
2795689435 | ||
|
|
dcf65a9643 | ||
|
|
784e1e3b7e | ||
|
|
4a2fdce62b | ||
|
|
32a5e80a6d | ||
|
|
0e8a8f94f6 | ||
|
|
8f23e19bcb | ||
|
|
f5c1f3df71 | ||
|
|
b25802eca7 | ||
|
|
7f7f84757d | ||
|
|
143499d041 | ||
|
|
26b41a5914 | ||
|
|
9f66102869 | ||
|
|
e1a426c067 | ||
|
|
2bd97ebaf7 | ||
|
|
45eeb53c98 | ||
|
|
254b195f50 | ||
|
|
865e84d206 | ||
|
|
1c13418a8b | ||
|
|
299e6479c1 | ||
|
|
6e81c3df92 | ||
|
|
1bd7d287a7 | ||
|
|
b82e1b87cc | ||
|
|
af40473c9a | ||
|
|
f17b734989 | ||
|
|
f050203f5d | ||
|
|
3f5622c577 | ||
|
|
1fb3f63f35 | ||
|
|
fc2ad1dbed | ||
|
|
bc6450773a | ||
|
|
204004c8b8 | ||
|
|
772a4a5cf3 | ||
|
|
fb6dad60a0 | ||
|
|
d9cad1e759 | ||
|
|
02053bf556 | ||
|
|
6fb3baf071 | ||
|
|
ff5317a7f3 | ||
|
|
766fc23151 | ||
|
|
08734bdd18 | ||
|
|
e943aff995 | ||
|
|
9c5da9c0d1 | ||
|
|
f6ab6d71ad | ||
|
|
67755d67fb | ||
|
|
a3c2ec3c5b | ||
|
|
9742f11d37 | ||
|
|
915ecb0dac | ||
|
|
53cd7fd2ea | ||
|
|
8532e991a5 | ||
|
|
50937fe622 | ||
|
|
ebc0cea226 | ||
|
|
65133018f3 | ||
|
|
ac7ee40aaf | ||
|
|
308b733b9d | ||
|
|
76c6ed8b05 | ||
|
|
7f519f8ab7 | ||
|
|
9c700d1fd5 | ||
|
|
19cd4d0af7 | ||
|
|
bf0fe1a1fa | ||
|
|
3df32e2732 | ||
|
|
0bf363472e | ||
|
|
34bc594beb | ||
|
|
bc00d0f7db | ||
|
|
e3e57a755b | ||
|
|
79d5fc3a0b | ||
|
|
322d686ab4 | ||
|
|
f75f7c1925 | ||
|
|
698c4e75fd | ||
|
|
4749dca764 | ||
|
|
649d23c8a8 | ||
|
|
e87d3d70e2 | ||
|
|
1460ad6027 | ||
|
|
78cd1a07d0 | ||
|
|
d67d68f6d3 | ||
|
|
008d04398a | ||
|
|
c3178a176d | ||
|
|
df1d1105b0 | ||
|
|
2b11b2c093 | ||
|
|
adb8edbae1 | ||
|
|
0e651d7297 | ||
|
|
3cff5e252d | ||
|
|
7412df0920 | ||
|
|
990133f804 | ||
|
|
df68808d29 | ||
|
|
b64d9402ca | ||
|
|
8d3a5dcd1b | ||
|
|
9eac4733c5 | ||
|
|
24bd51bda0 | ||
|
|
9374114370 | ||
|
|
eb13530560 | ||
|
|
5530d3e15d | ||
|
|
ea79eb5c3f | ||
|
|
c8b904a406 | ||
|
|
d45fa7b212 | ||
|
|
852ca7ad59 | ||
|
|
006dc30476 | ||
|
|
45ba736c81 | ||
|
|
c453fe7053 | ||
|
|
cc005af47e | ||
|
|
20c624efcf | ||
|
|
b2be6c1a30 | ||
|
|
c43a9c83ee | ||
|
|
e804e419bc | ||
|
|
992e7f7771 | ||
|
|
bd1a2e6435 | ||
|
|
1d137fd2fe | ||
|
|
f1f1862b19 | ||
|
|
6f0a5789fb | ||
|
|
cc78a533ae | ||
|
|
fd0b3e2e8b | ||
|
|
f3dbe5a308 | ||
|
|
1da8ce65a6 | ||
|
|
1dd5f088fa | ||
|
|
6a73714b00 | ||
|
|
6371243c10 | ||
|
|
533e610a35 | ||
|
|
8fd5a658eb | ||
|
|
c3b7efc818 | ||
|
|
43e7401c91 | ||
|
|
a9e850f749 | ||
|
|
da2e092163 | ||
|
|
8449294e5d | ||
|
|
19035f4b55 | ||
|
|
48ed437e70 | ||
|
|
14d3d9187f | ||
|
|
a37d1df089 | ||
|
|
97cd5f0deb | ||
|
|
1dafe969d1 | ||
|
|
8b2fcf517c | ||
|
|
42e60d935a | ||
|
|
1f66e3b0d0 | ||
|
|
b3759372ad | ||
|
|
f5d2b199e2 | ||
|
|
bc157417e1 | ||
|
|
12133d4eb7 | ||
|
|
148f2fc72c | ||
|
|
5d9d12a6be | ||
|
|
dde27a2305 | ||
|
|
10fa66e5b5 | ||
|
|
5e26304d81 | ||
|
|
d0860a339b | ||
|
|
fcbfdef11b | ||
|
|
b84f826fc8 | ||
|
|
ec46864922 | ||
|
|
c36a90531a | ||
|
|
3fa329c9e9 | ||
|
|
a70e5f119e | ||
|
|
b6b7185b7b | ||
|
|
4fb7a91a5e | ||
|
|
ff5e98c654 | ||
|
|
2662c400ba | ||
|
|
d8d835c1f5 | ||
|
|
06029d11d3 | ||
|
|
4c0ba6017d | ||
|
|
c70ecd49ca | ||
|
|
50edab8004 | ||
|
|
b4b9f788af | ||
|
|
a7e49e6c07 | ||
|
|
2b1ae8a66d | ||
|
|
2ebb69b50a | ||
|
|
4fca86e2af | ||
|
|
9ad00714ba | ||
|
|
b549438423 | ||
|
|
377888140f | ||
|
|
df377b589f | ||
|
|
87ce586d29 | ||
|
|
f599172add | ||
|
|
e20b39d928 | ||
|
|
4f33f46a02 | ||
|
|
159704421e | ||
|
|
0f965e57a2 | ||
|
|
8011ff5bda | ||
|
|
899e74aa14 | ||
|
|
66f743c45a | ||
|
|
8ad99c5cab | ||
|
|
da3f836ee3 | ||
|
|
37ecca3ba9 | ||
|
|
3d0ecd03ed | ||
|
|
eb5b2e0db5 | ||
|
|
47d92a0d96 | ||
|
|
7f1bf44aa8 | ||
|
|
5fec793bc7 | ||
|
|
e04936fc29 | ||
|
|
13a9d876f7 | ||
|
|
77526f412c | ||
|
|
0d4636b056 | ||
|
|
a65edf6711 | ||
|
|
973301b675 | ||
|
|
757ada2fd2 | ||
|
|
e88aa957ae | ||
|
|
8907fedc79 | ||
|
|
befc8a3f10 | ||
|
|
5935fbaa7a | ||
|
|
e8eaebe595 | ||
|
|
cec2dd6a7c | ||
|
|
266bf2c007 | ||
|
|
1f5a61e4ef | ||
|
|
46df1d5fcf | ||
|
|
d91527599a | ||
|
|
1410d3dc9a | ||
|
|
7538aa7bb9 | ||
|
|
db34bf01b6 | ||
|
|
9434541090 | ||
|
|
cc90bcce4c | ||
|
|
15ea5cef46 | ||
|
|
32a8d503a2 | ||
|
|
8c734dfc2f | ||
|
|
08ded2927a | ||
|
|
1c9dcf1f39 | ||
|
|
cab4069f42 | ||
|
|
4f6c67658c | ||
|
|
cda9ce198a | ||
|
|
9121138a1b | ||
|
|
407d74be27 | ||
|
|
49d690d137 | ||
|
|
deade370b9 | ||
|
|
00810b9b2a | ||
|
|
bcc08ffe4d | ||
|
|
ba1fc02a9b | ||
|
|
38d687f7c7 | ||
|
|
4621638f07 | ||
|
|
8881b201aa | ||
|
|
278b289f37 | ||
|
|
e7ade066b6 | ||
|
|
dee520e310 | ||
|
|
adebfd0a84 | ||
|
|
4e931b258d | ||
|
|
4011021823 | ||
|
|
bfda2a0050 | ||
|
|
2812c087ec | ||
|
|
6b5cddc48a | ||
|
|
403f556928 | ||
|
|
d8ef86aadf | ||
|
|
a9fe1e159a | ||
|
|
65c8e8a09e | ||
|
|
46d9243eb0 | ||
|
|
63a01bdb33 | ||
|
|
d53209956b | ||
|
|
951213ee09 | ||
|
|
ae067df941 | ||
|
|
40718efacc | ||
|
|
d406786a8d | ||
|
|
0ac069da13 | ||
|
|
d17ea7a9c0 | ||
|
|
c92021fc4f | ||
|
|
50a5cebba8 | ||
|
|
6c602c2282 | ||
|
|
76c70cbf4c | ||
|
|
3d9e68ecfd | ||
|
|
8b0b7156d9 | ||
|
|
cf6e2ceafd | ||
|
|
69a55d334a | ||
|
|
241b7433cd | ||
|
|
057c97812b | ||
|
|
02188e399d | ||
|
|
aae02863db | ||
|
|
49f36bb028 | ||
|
|
52730f6330 | ||
|
|
538efef1ba | ||
|
|
9311d822c7 | ||
|
|
351529cb50 | ||
|
|
94a2e3dddc | ||
|
|
ee96214a8d | ||
|
|
e1ae469504 | ||
|
|
0d00be4f4f | ||
|
|
23146e7527 | ||
|
|
b18df936ea | ||
|
|
4148663706 | ||
|
|
3e1971eb16 | ||
|
|
bcdb86ee7e | ||
|
|
2d77018d1b | ||
|
|
ceef0af1ae | ||
|
|
e4eec3416a | ||
|
|
645774295f | ||
|
|
f2e0c740d3 | ||
|
|
d856f4e51f | ||
|
|
7b9a414524 | ||
|
|
0c63f99016 | ||
|
|
3bc9cbea63 | ||
|
|
6eff3069da | ||
|
|
58a14b6b99 | ||
|
|
b53bf44139 | ||
|
|
d8758443bd | ||
|
|
51f64c2920 | ||
|
|
cea42ff9e4 | ||
|
|
2df9d05981 | ||
|
|
4142c41ffc | ||
|
|
de44293d59 | ||
|
|
5efe6ab93c | ||
|
|
ce59f42ce1 | ||
|
|
7da7b9610c | ||
|
|
d44e42ec15 | ||
|
|
0ea1889265 | ||
|
|
6352cf2374 | ||
|
|
3127ec737b | ||
|
|
aa0b657e58 | ||
|
|
d0f3f26fff | ||
|
|
08f3b02dfc | ||
|
|
2d690b83bf | ||
|
|
0642da0145 | ||
|
|
afa985c135 | ||
|
|
fd64fa1863 | ||
|
|
56dc01ffe0 | ||
|
|
5df45f5b27 | ||
|
|
05d55b86df | ||
|
|
475119988c | ||
|
|
7a6bcc3639 | ||
|
|
8e125c9759 | ||
|
|
ade773390a | ||
|
|
5c26ba9cb1 | ||
|
|
5a544d4fac | ||
|
|
2e7d6a6202 | ||
|
|
ea7357bc58 | ||
|
|
b3319a6074 | ||
|
|
c9628c52d6 | ||
|
|
dcbdcc729b | ||
|
|
15d608867d | ||
|
|
ea2c6b8a88 | ||
|
|
0e6cf0ff28 | ||
|
|
553951c443 | ||
|
|
15ef168821 | ||
|
|
cc6e5ec345 | ||
|
|
77643122a8 | ||
|
|
832cef953b | ||
|
|
bcdbb6b677 | ||
|
|
543779fc43 | ||
|
|
2ade3d5c89 | ||
|
|
7939e5327c | ||
|
|
f7171034f9 | ||
|
|
c7c120fba6 | ||
|
|
415899d428 | ||
|
|
8dda5613ef | ||
|
|
714f2113bb | ||
|
|
a50b92ea67 | ||
|
|
da81c1e49a | ||
|
|
23ab43233e | ||
|
|
1a119a22d1 | ||
|
|
77c5191ad7 | ||
|
|
7395501d1d | ||
|
|
920bffbfbb | ||
|
|
751c061d9a | ||
|
|
a624b84097 | ||
|
|
c75dd10671 | ||
|
|
b696666f5a | ||
|
|
f4f6cb7532 | ||
|
|
1e3d5a0412 | ||
|
|
98981276a0 | ||
|
|
8c96b65082 | ||
|
|
c926999cfb | ||
|
|
519157cfcf | ||
|
|
5d14362a75 | ||
|
|
15fe8c6e90 | ||
|
|
c1e01c2992 | ||
|
|
5e27ea5528 | ||
|
|
33d4c96aa2 | ||
|
|
b3eb5d1eb7 | ||
|
|
2af0a023c9 | ||
|
|
5f52d5ee17 | ||
|
|
95701566f3 | ||
|
|
57be1d60dd | ||
|
|
62f96eea6b | ||
|
|
fa3cca51e1 | ||
|
|
d441fa66fe | ||
|
|
43aee15ba3 | ||
|
|
5c57d92978 | ||
|
|
7afe17740f | ||
|
|
158432217c | ||
|
|
437ff1c01a | ||
|
|
40072b9511 | ||
|
|
520af9d767 | ||
|
|
bdac9d3dd0 | ||
|
|
37158f5303 | ||
|
|
612c3784e5 | ||
|
|
951e07d71d | ||
|
|
36f774a8fb | ||
|
|
a2b921f890 | ||
|
|
bd70f5c148 | ||
|
|
134b957bf4 | ||
|
|
4d21dc4f2d | ||
|
|
74416525d2 | ||
|
|
44cb51010c | ||
|
|
90597226eb | ||
|
|
7fb5ad82d9 | ||
|
|
f4bcb44025 | ||
|
|
b7ae7a654b | ||
|
|
148e6a30c8 | ||
|
|
47bd1688ed | ||
|
|
6630d96253 | ||
|
|
d32ab6029f | ||
|
|
76c00d1c09 | ||
|
|
492cc4219c | ||
|
|
51bf7c3aef | ||
|
|
489c61a22d | ||
|
|
0f3d630634 | ||
|
|
a0f652c559 | ||
|
|
877b57ae9b | ||
|
|
dc7ae41f33 | ||
|
|
2d43f42769 | ||
|
|
03ef546706 | ||
|
|
de5aa3847e | ||
|
|
0f4905a259 | ||
|
|
936f725b81 | ||
|
|
c86d2daf81 | ||
|
|
a70c1ca100 | ||
|
|
4668ee03f6 | ||
|
|
236bada755 | ||
|
|
76687030f0 | ||
|
|
2b3d69da2b | ||
|
|
8481e438bd | ||
|
|
bd2c9bedcf | ||
|
|
2fe922608f | ||
|
|
07fa69335c | ||
|
|
ddb16a1ab1 | ||
|
|
2e871f35f3 | ||
|
|
fa94e3c1b2 | ||
|
|
c6eb3413f3 | ||
|
|
54c70bc02c | ||
|
|
83558a0ba3 | ||
|
|
23ea04f910 | ||
|
|
c334adc78f | ||
|
|
f3f6cb2093 | ||
|
|
c4aa57bc4c | ||
|
|
2970c1df24 | ||
|
|
35c85f0db9 | ||
|
|
0deb7b1696 | ||
|
|
53b8aa065c | ||
|
|
6a2d122a50 | ||
|
|
d97473e551 | ||
|
|
525639eaa0 | ||
|
|
7dceabfcb2 | ||
|
|
e1f97e41e3 | ||
|
|
2d2f6cd4fd | ||
|
|
44c940765b | ||
|
|
ed68fcf665 | ||
|
|
907e9495a2 | ||
|
|
dac164cc99 | ||
|
|
4290cacb86 | ||
|
|
db5cc35b44 | ||
|
|
90031edde8 | ||
|
|
a96907a9db | ||
|
|
f8160f7bc5 | ||
|
|
f0d7773ffa | ||
|
|
84555c89de | ||
|
|
f7a3e001f7 | ||
|
|
42561db1ae | ||
|
|
0d31e852b1 | ||
|
|
75e1fde668 | ||
|
|
9cb71af9e5 | ||
|
|
0dd4cb0f8f | ||
|
|
33db5e081d | ||
|
|
a51dc0c7ce | ||
|
|
276ffa81f6 | ||
|
|
50610311a7 | ||
|
|
c30ab1014e | ||
|
|
df8869cf1a | ||
|
|
8b447878dc | ||
|
|
9c590fa474 | ||
|
|
a868a9ac13 | ||
|
|
55b78ff780 | ||
|
|
ccab469a0c | ||
|
|
e711a6c275 | ||
|
|
c1e3128b3f | ||
|
|
05bb5ffb65 | ||
|
|
ee95d666f8 | ||
|
|
1e2810e07d | ||
|
|
fec656b3b1 | ||
|
|
31174f3f83 | ||
|
|
5a0f379289 | ||
|
|
0138e9cbb0 | ||
|
|
b5cf61312b | ||
|
|
16cbb3196c | ||
|
|
f1254c4461 | ||
|
|
cd9415baf2 | ||
|
|
6bd77c0abd | ||
|
|
fb7ee7f42c | ||
|
|
9dcd6f2a87 | ||
|
|
e3eb26f91a | ||
|
|
86070f0b7d | ||
|
|
3fbe100a02 | ||
|
|
ee62674322 | ||
|
|
a4192160ce | ||
|
|
4d9296c71f | ||
|
|
d5d190335c | ||
|
|
7428064f79 | ||
|
|
af706edd59 | ||
|
|
4eb40ef283 | ||
|
|
f85f36ed03 | ||
|
|
904f1ca1ce | ||
|
|
32b85e4ccc | ||
|
|
c7bbb2a788 | ||
|
|
29112d7e0b | ||
|
|
3fd2f43fb6 | ||
|
|
2cf1de3f2d | ||
|
|
d9bdf5cfca | ||
|
|
f494eefcae | ||
|
|
c9e69438b1 | ||
|
|
2e89812fad | ||
|
|
a0207274f4 | ||
|
|
759d7fde5d | ||
|
|
a0f5c4c8f5 | ||
|
|
2e210acd00 | ||
|
|
ede6387caa | ||
|
|
ff25b52110 | ||
|
|
dc8c27037a | ||
|
|
3e11bd0d6e | ||
|
|
6a4c7063fd | ||
|
|
15fe60aa25 | ||
|
|
1ec7f60484 | ||
|
|
63e7f8e340 | ||
|
|
1cf9c2e76f | ||
|
|
0ca1f6e0f4 | ||
|
|
a68f4fd2b9 | ||
|
|
5b35241470 | ||
|
|
b26b731498 | ||
|
|
da305966d2 | ||
|
|
4ee10d2266 | ||
|
|
e1aeb6915e | ||
|
|
e75915bb73 | ||
|
|
ba2a43266a | ||
|
|
cfaa8bbee8 | ||
|
|
6b661795cf | ||
|
|
fa65b71c98 | ||
|
|
da5dec83f6 | ||
|
|
2ef3cb2510 | ||
|
|
c8a87e48ab | ||
|
|
b9561e29ff | ||
|
|
bf6dcd64dc | ||
|
|
214c331236 | ||
|
|
9cb504ca9a | ||
|
|
f0a9f9042f | ||
|
|
ff015f6308 | ||
|
|
eeac28f4ab | ||
|
|
5505826db9 | ||
|
|
31c869b4c4 | ||
|
|
0395996756 | ||
|
|
986dd84375 | ||
|
|
a36e986920 | ||
|
|
68dc433bf5 | ||
|
|
e59fc730f8 | ||
|
|
4f9e835472 | ||
|
|
498b994eb4 | ||
|
|
40eef6da0c | ||
|
|
71373b04b0 | ||
|
|
6fb7269979 | ||
|
|
3460105def | ||
|
|
e3824d23bc | ||
|
|
80ad448590 | ||
|
|
59f69dd9e7 | ||
|
|
6e1ee0802f | ||
|
|
5cf58a9ae9 | ||
|
|
2106515f6d | ||
|
|
66b4709830 | ||
|
|
0b1f813c38 | ||
|
|
0b6960446e | ||
|
|
4bc2f96c93 | ||
|
|
0e4750d837 | ||
|
|
40cec637d7 | ||
|
|
407d4a0cf0 | ||
|
|
c84ae0bb7a | ||
|
|
1dbf440194 | ||
|
|
afaaa7e411 | ||
|
|
7b91952645 | ||
|
|
799bcccd1b | ||
|
|
8726be27a6 | ||
|
|
e26c5bda6e | ||
|
|
f672b7e39e | ||
|
|
f0e6bf7604 | ||
|
|
f729d5d4ee | ||
|
|
04a941c818 | ||
|
|
215d537624 | ||
|
|
f63fbf8114 | ||
|
|
b595587031 | ||
|
|
82cc3d8cc2 | ||
|
|
e20e376881 | ||
|
|
8052d01a37 | ||
|
|
d03444db4a | ||
|
|
f9c1329dab | ||
|
|
a8003286b5 | ||
|
|
747a8ae3a6 | ||
|
|
b759ebdb93 | ||
|
|
b41632e9a8 | ||
|
|
b4be228330 | ||
|
|
821b6ef2a6 | ||
|
|
31c948184a | ||
|
|
67dd10de26 | ||
|
|
f13935da53 | ||
|
|
dc8af18a0e | ||
|
|
7bee359459 | ||
|
|
61b9246afe | ||
|
|
9feb4941f4 | ||
|
|
237f690f8b | ||
|
|
386e801a5a | ||
|
|
5cf05ce149 | ||
|
|
aee67bb1a7 | ||
|
|
5e2d740829 | ||
|
|
82b8ec37fc | ||
|
|
f73fa47b1f | ||
|
|
fd1684e70b | ||
|
|
19501028ca | ||
|
|
ed01dc6567 | ||
|
|
3a366f451a | ||
|
|
7f6108beb1 | ||
|
|
76b0660f47 | ||
|
|
fc8800c71f | ||
|
|
75a12b9d2b | ||
|
|
9bcbf552d6 | ||
|
|
32c6d4f603 | ||
|
|
b4b2f58eab | ||
|
|
a131cd6c3b | ||
|
|
9c03196e79 | ||
|
|
8b92d10fb3 | ||
|
|
8e220f0e6f | ||
|
|
e191a65ebb | ||
|
|
5ca81596bb | ||
|
|
64e8185ff7 | ||
|
|
7bb504b807 | ||
|
|
9be069f899 | ||
|
|
913a2da6e5 | ||
|
|
ea732464aa | ||
|
|
ddbea29c12 | ||
|
|
4c7ddb8d9b | ||
|
|
a1fcd6e445 | ||
|
|
7b8fd0cc12 | ||
|
|
391dc549c0 | ||
|
|
526f4a95cc | ||
|
|
4cd268dc5d | ||
|
|
59e6fb94b5 | ||
|
|
2f083504ee | ||
|
|
3384ffc6eb | ||
|
|
e276bd3332 | ||
|
|
7445b5345f | ||
|
|
429485e621 | ||
|
|
52d497570b | ||
|
|
1876a928d3 | ||
|
|
2dc2a19db5 | ||
|
|
0c5e717f43 | ||
|
|
54af0f4c65 | ||
|
|
678dfaa6eb | ||
|
|
fc5d4654e5 | ||
|
|
19c93d16d1 | ||
|
|
0ce8b910ca | ||
|
|
c780d1fa7c | ||
|
|
584c052da4 | ||
|
|
315374008b | ||
|
|
a9457345ee | ||
|
|
726e165932 | ||
|
|
2264db7f4a | ||
|
|
4e93dc2c97 | ||
|
|
8003d8d279 | ||
|
|
f0ecb25acd | ||
|
|
7ec1a1407a | ||
|
|
7dbe40092d | ||
|
|
e53563ebbe | ||
|
|
c2c9b27771 | ||
|
|
c3d7340542 | ||
|
|
2461a43e00 | ||
|
|
7dcd9bf5ad | ||
|
|
fa979a4290 | ||
|
|
e2a15c79e7 | ||
|
|
02962fabda | ||
|
|
b77d168d58 | ||
|
|
c0e6543b5a | ||
|
|
b96e162131 | ||
|
|
1dc16ad77b | ||
|
|
acece23697 | ||
|
|
e5f823a3a7 | ||
|
|
b41dc03930 | ||
|
|
ade5f2c8c5 | ||
|
|
3e0e819158 | ||
|
|
eb92e57509 | ||
|
|
7ad499ad76 | ||
|
|
2d7582bd92 | ||
|
|
7e8e593a45 | ||
|
|
6c3b86369f | ||
|
|
6aba60ab08 | ||
|
|
d720312df0 | ||
|
|
5b09eb1d74 | ||
|
|
5119abe498 | ||
|
|
1c5009c3fb | ||
|
|
8a1afe4213 | ||
|
|
fd4289dae0 | ||
|
|
977adf1354 | ||
|
|
c1fe07276c | ||
|
|
c166b80a8c | ||
|
|
afe9fd5ffd | ||
|
|
5567c772cd | ||
|
|
c75bd08807 | ||
|
|
f7d7555521 | ||
|
|
16f8cdac95 | ||
|
|
9905a73ae0 | ||
|
|
51dd738b1a | ||
|
|
067f2c6148 | ||
|
|
e2cd2cd409 | ||
|
|
37aab5dd6b | ||
|
|
7ddfc04793 | ||
|
|
7b10474fed | ||
|
|
8cf097635e | ||
|
|
2d18546870 | ||
|
|
0f546c4670 | ||
|
|
6d38868950 | ||
|
|
8723eb16ea | ||
|
|
daf39112e7 | ||
|
|
9543d1901f | ||
|
|
ba452dbcf0 | ||
|
|
a2954578aa | ||
|
|
1bcb2f91cc | ||
|
|
92a2c1a9c4 | ||
|
|
9f86e83478 | ||
|
|
f01f434311 | ||
|
|
0c6ca0da62 | ||
|
|
095ce2ca7f | ||
|
|
dbb6c18c44 | ||
|
|
653c685667 | ||
|
|
ec5e279f93 | ||
|
|
e69b1255d7 | ||
|
|
57bf9d6740 | ||
|
|
677a7d06da | ||
|
|
f28b834426 | ||
|
|
04bd147d46 | ||
|
|
6154a5ac02 | ||
|
|
1a04e8903a | ||
|
|
85c5fa9f64 | ||
|
|
8967976443 | ||
|
|
bcacc40775 | ||
|
|
4ecf29380a | ||
|
|
aaa7d36bc9 | ||
|
|
8937e39afd | ||
|
|
343430c537 | ||
|
|
335cc5d651 | ||
|
|
2e551c32b6 | ||
|
|
af2ee1e80a | ||
|
|
e2a9aaf24b | ||
|
|
4947eb85c0 | ||
|
|
1a358df998 | ||
|
|
5903f4596a | ||
|
|
5bb0be1e24 | ||
|
|
6504746652 | ||
|
|
42bb0b3904 | ||
|
|
1bb463a980 | ||
|
|
16546b7342 | ||
|
|
f2174c16cc | ||
|
|
e48f68953d | ||
|
|
25081d8e30 | ||
|
|
34eeda1c09 | ||
|
|
3efb26ae7f | ||
|
|
77da4f118c | ||
|
|
3241fc3103 | ||
|
|
acb3e8e8a7 | ||
|
|
5f16ff3acc | ||
|
|
71a745270a | ||
|
|
5dcb370f78 | ||
|
|
0f918b1a9d | ||
|
|
a6988aa0b9 | ||
|
|
2359663437 | ||
|
|
bace28517e | ||
|
|
9f6d9efc1d | ||
|
|
a0ab5a7cd8 | ||
|
|
ba8b3be61a | ||
|
|
2467831913 | ||
|
|
e4a21b11d5 | ||
|
|
f3b6425324 | ||
|
|
7ee03e0996 | ||
|
|
081accb62c | ||
|
|
fe4835c15e | ||
|
|
df3b5557d1 | ||
|
|
948a5d5ac6 | ||
|
|
85055a9efe | ||
|
|
149620f858 | ||
|
|
6ee5d431a0 | ||
|
|
a4c426b1a8 | ||
|
|
4f38c610c3 | ||
|
|
38adb23bd2 | ||
|
|
e24031fb36 | ||
|
|
99ef8c6d16 | ||
|
|
e8152207c4 | ||
|
|
d7465895d0 | ||
|
|
5d0bcb4419 | ||
|
|
01151ff566 | ||
|
|
d0e9b4812f | ||
|
|
5a8e674e92 | ||
|
|
e380d4306b | ||
|
|
9d297c06e8 | ||
|
|
e24fdb138d | ||
|
|
0da5531c7c | ||
|
|
0c4898670c | ||
|
|
be7eb22e88 | ||
|
|
8b48621687 | ||
|
|
56aecfc081 | ||
|
|
82a0308bc6 | ||
|
|
7f671586b0 | ||
|
|
9e62f9d64e | ||
|
|
81c2780d2b | ||
|
|
b39b69a730 | ||
|
|
30c7a7bd69 | ||
|
|
cf5a9aebb2 | ||
|
|
1a9979a803 | ||
|
|
1eef53b6fe | ||
|
|
388aff16c8 | ||
|
|
83ec0228d1 | ||
|
|
2dc8cc1e48 | ||
|
|
658fa35642 | ||
|
|
b2c4ed9a2b | ||
|
|
b5cd43bc95 | ||
|
|
134ace98d9 | ||
|
|
134641fcb5 | ||
|
|
8f8d3114dd | ||
|
|
102ffc69e8 | ||
|
|
64a353f2b6 | ||
|
|
b258764ffe | ||
|
|
3947b859dc | ||
|
|
9f9f6ee48b | ||
|
|
58fc918d0a | ||
|
|
ece01b0f56 | ||
|
|
c378cb4793 | ||
|
|
d888d5c933 | ||
|
|
b2d3ae257a | ||
|
|
a93f41233a | ||
|
|
9138419379 | ||
|
|
197fd69ddc | ||
|
|
c400d8b2d8 | ||
|
|
0115766df3 | ||
|
|
8563364d8b | ||
|
|
0a40ae4c6a | ||
|
|
e63c7a13ff | ||
|
|
b7e8171cf8 | ||
|
|
75e93e5168 | ||
|
|
843d00c219 | ||
|
|
c6d27d8224 | ||
|
|
84390acccc | ||
|
|
f04d3c8b7d | ||
|
|
60773e0a97 | ||
|
|
3cf44b3037 | ||
|
|
1499778d5e | ||
|
|
a7e401656e | ||
|
|
6e1b1abfa7 | ||
|
|
8e287c5c77 | ||
|
|
231863b133 | ||
|
|
ae5d5b8f59 | ||
|
|
fd48cd57f9 | ||
|
|
98987177a0 | ||
|
|
437f44a1f4 | ||
|
|
b76104e722 | ||
|
|
1e80a9cb34 | ||
|
|
26d202a7bd | ||
|
|
b390c66dc4 | ||
|
|
f96e1b6f3e | ||
|
|
15b0a89fb1 | ||
|
|
5b83417afc | ||
|
|
9b3be870dc | ||
|
|
7a600ea3eb | ||
|
|
110fe2473f | ||
|
|
f8d31d2400 | ||
|
|
e3d30f8ebf | ||
|
|
5d79baf3f8 | ||
|
|
ec4507d12a | ||
|
|
b1e766c30e | ||
|
|
316cca204f | ||
|
|
0bccfc44a7 | ||
|
|
3cd11617ea | ||
|
|
9839ceffe0 | ||
|
|
a44776ed48 | ||
|
|
cfbd387a5d | ||
|
|
bb363c8ff2 | ||
|
|
ebe0a88226 | ||
|
|
3445eae737 | ||
|
|
8152b6837e | ||
|
|
0e4e8e00a9 | ||
|
|
7b1cb885c7 | ||
|
|
fc4aa27cae | ||
|
|
038f1f94c2 | ||
|
|
539d3dc34d | ||
|
|
0db5ccb0dd | ||
|
|
76884c73bf | ||
|
|
5ebacc49c6 | ||
|
|
8a0ed7e2b3 | ||
|
|
62b8f2f731 | ||
|
|
8fd60483ef | ||
|
|
7a7ad0c120 | ||
|
|
41031fce2f | ||
|
|
e1e4b226c6 | ||
|
|
26d27df6fc | ||
|
|
3e6f1fa2db | ||
|
|
aaf7f7fcca | ||
|
|
e0c2ab1901 | ||
|
|
59a11b6a5d | ||
|
|
9fc9b2926f | ||
|
|
1654b77ca0 | ||
|
|
d237197de3 | ||
|
|
5db46d2087 | ||
|
|
b88c3f8f82 | ||
|
|
4a3c8e22d7 | ||
|
|
a131f0acf6 | ||
|
|
4ffa13728d | ||
|
|
44b74c8c25 | ||
|
|
40b85d7ee8 | ||
|
|
090f7ff449 | ||
|
|
b05061dcd2 | ||
|
|
06dc6e3490 | ||
|
|
63f38de38e | ||
|
|
e0ba1cbf8d | ||
|
|
847eacea19 | ||
|
|
b531f7d585 | ||
|
|
7eb28f9eb7 | ||
|
|
1d86247b2c | ||
|
|
1bba0a9714 | ||
|
|
5cf69fae7d | ||
|
|
b73e083d9d | ||
|
|
7d3ca68be6 | ||
|
|
e9b2475e29 | ||
|
|
59f65230b5 | ||
|
|
5bee396e4b | ||
|
|
33b877cc01 | ||
|
|
63f90a2bcd | ||
|
|
f987b368e8 | ||
|
|
5d2e2377ff | ||
|
|
2eaf3db6ae | ||
|
|
88bf01a31e | ||
|
|
d894bf4535 | ||
|
|
9ed63c607e | ||
|
|
d52ea4b6cf | ||
|
|
0ffb8ddd7f | ||
|
|
95c6d591f7 | ||
|
|
9a1e518cc3 | ||
|
|
9ca0ab6e2b | ||
|
|
8395b9e25d | ||
|
|
3d92d5a659 | ||
|
|
50e3783f07 | ||
|
|
6e85febf20 | ||
|
|
ba17363d75 | ||
|
|
92a2884b09 | ||
|
|
2754a13f86 | ||
|
|
4eddf634e7 | ||
|
|
1a8d9bf254 | ||
|
|
62967b3110 | ||
|
|
5872e1c35a | ||
|
|
ba457f5feb | ||
|
|
ed91d5f086 | ||
|
|
b83e97802e | ||
|
|
2bffd6829e | ||
|
|
4e99c80425 | ||
|
|
a663f60b05 | ||
|
|
e1e81e315e | ||
|
|
025d160dfc | ||
|
|
a3e388a73a | ||
|
|
1847cc7420 | ||
|
|
87b019d5f9 | ||
|
|
1184db8273 | ||
|
|
a0ba881c22 | ||
|
|
d42f1e87c3 | ||
|
|
9769bc05c6 |
23
.coveragerc
23
.coveragerc
@@ -1,9 +1,26 @@
|
||||
[run]
|
||||
source = pytest,_pytest,testing/
|
||||
include =
|
||||
src/*
|
||||
testing/*
|
||||
*/lib/python*/site-packages/_pytest/*
|
||||
*/lib/python*/site-packages/pytest.py
|
||||
*/pypy*/site-packages/_pytest/*
|
||||
*/pypy*/site-packages/pytest.py
|
||||
*\Lib\site-packages\_pytest\*
|
||||
*\Lib\site-packages\pytest.py
|
||||
parallel = 1
|
||||
branch = 1
|
||||
|
||||
[paths]
|
||||
source = src/
|
||||
.tox/*/lib/python*/site-packages/
|
||||
.tox\*\Lib\site-packages\
|
||||
*/lib/python*/site-packages/
|
||||
*/pypy*/site-packages/
|
||||
*\Lib\site-packages\
|
||||
|
||||
[report]
|
||||
skip_covered = True
|
||||
show_missing = True
|
||||
exclude_lines =
|
||||
\#\s*pragma: no cover
|
||||
^\s*raise NotImplementedError\b
|
||||
^\s*return NotImplemented\b
|
||||
|
||||
5
.github/FUNDING.yml
vendored
Normal file
5
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# info:
|
||||
# * https://help.github.com/en/articles/displaying-a-sponsor-button-in-your-repository
|
||||
# * https://tidelift.com/subscription/how-to-connect-tidelift-with-github
|
||||
tidelift: pypi/pytest
|
||||
open_collective: pytest
|
||||
10
.github/ISSUE_TEMPLATE.md
vendored
10
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,8 +1,10 @@
|
||||
<!--
|
||||
Thanks for submitting an issue!
|
||||
|
||||
Here's a quick checklist in what to include:
|
||||
Here's a quick checklist for what to provide:
|
||||
-->
|
||||
|
||||
- [ ] Include a detailed description of the bug or suggestion
|
||||
- [ ] `pip list` of the virtual environment you are using
|
||||
- [ ] a detailed description of the bug or suggestion
|
||||
- [ ] output of `pip list` from the virtual environment you are using
|
||||
- [ ] pytest and operating system versions
|
||||
- [ ] Minimal example if possible
|
||||
- [ ] minimal example if possible
|
||||
|
||||
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,14 +1,16 @@
|
||||
<!--
|
||||
Thanks for submitting a PR, your contribution is really appreciated!
|
||||
|
||||
Here's a quick checklist that should be present in PRs (you can delete this text from the final description, this is
|
||||
just a guideline):
|
||||
Here is a quick checklist that should be present in PRs.
|
||||
(please delete this text from the final description, this is just a guideline)
|
||||
-->
|
||||
|
||||
- [ ] Create a new changelog file in the `changelog` folder, with a name like `<ISSUE NUMBER>.<TYPE>.rst`. See [changelog/README.rst](https://github.com/pytest-dev/pytest/blob/master/changelog/README.rst) for details.
|
||||
- [ ] Target the `master` branch for bug fixes, documentation updates and trivial changes.
|
||||
- [ ] Target the `features` branch for new features and removals/deprecations.
|
||||
- [ ] Target the `features` branch for new features, improvements, and removals/deprecations.
|
||||
- [ ] Include documentation when adding new features.
|
||||
- [ ] Include new tests or update existing tests when applicable.
|
||||
|
||||
Unless your change is trivial or a small documentation fix (e.g., a typo or reword of a small section) please:
|
||||
|
||||
- [ ] Create a new changelog file in the `changelog` folder, with a name like `<ISSUE NUMBER>.<TYPE>.rst`. See [changelog/README.rst](https://github.com/pytest-dev/pytest/blob/master/changelog/README.rst) for details.
|
||||
- [ ] Add yourself to `AUTHORS` in alphabetical order;
|
||||
|
||||
2
.github/config.yml
vendored
Normal file
2
.github/config.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
rtd:
|
||||
project: pytest
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -35,6 +35,7 @@ env/
|
||||
.tox
|
||||
.cache
|
||||
.pytest_cache
|
||||
.mypy_cache
|
||||
.coverage
|
||||
.coverage.*
|
||||
coverage.xml
|
||||
@@ -44,3 +45,7 @@ coverage.xml
|
||||
.pydevproject
|
||||
.project
|
||||
.settings
|
||||
.vscode
|
||||
|
||||
# generated by pip
|
||||
pip-wheel-metadata/
|
||||
|
||||
@@ -1,53 +1,77 @@
|
||||
exclude: doc/en/example/py2py3/test_py2.py
|
||||
repos:
|
||||
- repo: https://github.com/ambv/black
|
||||
rev: 18.6b4
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 19.3b0
|
||||
hooks:
|
||||
- id: black
|
||||
args: [--safe, --quiet]
|
||||
language_version: python3
|
||||
- repo: https://github.com/asottile/blacken-docs
|
||||
rev: v0.3.0
|
||||
rev: v1.0.0
|
||||
hooks:
|
||||
- id: blacken-docs
|
||||
additional_dependencies: [black==18.9b0]
|
||||
language_version: python3
|
||||
additional_dependencies: [black==19.3b0]
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.0.0
|
||||
rev: v2.2.3
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: fix-encoding-pragma
|
||||
args: [--remove]
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
exclude: _pytest/debugging.py
|
||||
language_version: python3
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.7.7
|
||||
hooks:
|
||||
- id: flake8
|
||||
language_version: python3
|
||||
additional_dependencies: [flake8-typing-imports==1.3.0]
|
||||
- repo: https://github.com/asottile/reorder_python_imports
|
||||
rev: v1.3.3
|
||||
rev: v1.4.0
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
args: ['--application-directories=.:src']
|
||||
args: ['--application-directories=.:src', --py3-plus]
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v1.10.1
|
||||
rev: v1.18.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--keep-percent-format]
|
||||
args: [--py3-plus]
|
||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||
rev: v1.1.0
|
||||
rev: v1.4.0
|
||||
hooks:
|
||||
- id: rst-backticks
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.720
|
||||
hooks:
|
||||
- id: mypy
|
||||
files: ^(src/|testing/)
|
||||
args: []
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: rst
|
||||
name: rst
|
||||
entry: rst-lint --encoding utf-8
|
||||
files: ^(CHANGELOG.rst|HOWTORELEASE.rst|README.rst|changelog/.*)$
|
||||
files: ^(CHANGELOG.rst|HOWTORELEASE.rst|README.rst|TIDELIFT.rst|changelog/.*)$
|
||||
language: python
|
||||
additional_dependencies: [pygments, restructuredtext_lint]
|
||||
- id: changelogs-rst
|
||||
name: changelog filenames
|
||||
language: fail
|
||||
entry: 'changelog files must be named ####.(feature|bugfix|doc|deprecation|removal|vendor|trivial).rst'
|
||||
exclude: changelog/(\d+\.(feature|bugfix|doc|deprecation|removal|vendor|trivial).rst|README.rst|_template.rst)
|
||||
exclude: changelog/(\d+\.(feature|improvement|bugfix|doc|deprecation|removal|vendor|trivial).rst|README.rst|_template.rst)
|
||||
files: ^changelog/
|
||||
- id: py-deprecated
|
||||
name: py library is deprecated
|
||||
language: pygrep
|
||||
entry: >
|
||||
(?x)\bpy\.(
|
||||
_code\.|
|
||||
builtin\.|
|
||||
code\.|
|
||||
io\.(BytesIO|saferepr)|
|
||||
path\.local\.sysfind|
|
||||
process\.|
|
||||
std\.
|
||||
)
|
||||
types: [python]
|
||||
|
||||
139
.travis.yml
139
.travis.yml
@@ -1,4 +1,3 @@
|
||||
sudo: false
|
||||
language: python
|
||||
dist: xenial
|
||||
stages:
|
||||
@@ -7,62 +6,84 @@ stages:
|
||||
if: repo = pytest-dev/pytest AND tag IS NOT present
|
||||
- name: deploy
|
||||
if: repo = pytest-dev/pytest AND tag IS present
|
||||
python:
|
||||
- '3.7'
|
||||
install:
|
||||
- pip install --upgrade --pre tox
|
||||
python: '3.7'
|
||||
cache: false
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- TOXENV=py27
|
||||
# Specialized factors for py27.
|
||||
- TOXENV=py27-nobyte
|
||||
- TOXENV=py27-xdist
|
||||
- TOXENV=py27-pluggymaster PYTEST_NO_COVERAGE=1
|
||||
# Specialized factors for py37.
|
||||
- TOXENV=py37-pexpect,py37-trial,py37-numpy
|
||||
- TOXENV=py37-pluggymaster PYTEST_NO_COVERAGE=1
|
||||
- TOXENV=py37-freeze PYTEST_NO_COVERAGE=1
|
||||
global:
|
||||
- PYTEST_ADDOPTS=-vv
|
||||
|
||||
# setuptools-scm needs all tags in order to obtain a proper version
|
||||
git:
|
||||
depth: false
|
||||
|
||||
install:
|
||||
- python -m pip install --upgrade --pre tox
|
||||
|
||||
jobs:
|
||||
include:
|
||||
# Coverage tracking is slow with pypy, skip it.
|
||||
- env: TOXENV=pypy PYTEST_NO_COVERAGE=1
|
||||
python: 'pypy-5.4'
|
||||
dist: trusty
|
||||
- env: TOXENV=py34
|
||||
python: '3.4'
|
||||
- env: TOXENV=py35
|
||||
python: '3.5'
|
||||
- env: TOXENV=py36
|
||||
python: '3.6'
|
||||
- env: TOXENV=py37
|
||||
- &test-macos
|
||||
# OSX tests - first (in test stage), since they are the slower ones.
|
||||
- os: osx
|
||||
osx_image: xcode10.1
|
||||
language: generic
|
||||
os: osx
|
||||
osx_image: xcode9.4
|
||||
sudo: required
|
||||
install:
|
||||
- python -m pip install --pre tox
|
||||
env: TOXENV=py27
|
||||
- <<: *test-macos
|
||||
env: TOXENV=py37
|
||||
env: TOXENV=py37-xdist PYTEST_COVERAGE=1
|
||||
before_install:
|
||||
- brew update
|
||||
- brew upgrade python
|
||||
- brew unlink python
|
||||
- brew link python
|
||||
- which python3
|
||||
- python3 -V
|
||||
- ln -sfn "$(which python3)" /usr/local/bin/python
|
||||
- python -V
|
||||
- test $(python -c 'import sys; print("%d%d" % sys.version_info[0:2])') = 37
|
||||
|
||||
# Full run of latest supported version, without xdist.
|
||||
- env: TOXENV=py37
|
||||
python: '3.7'
|
||||
|
||||
# Coverage tracking is slow with pypy, skip it.
|
||||
- env: TOXENV=pypy3-xdist
|
||||
python: 'pypy3'
|
||||
|
||||
- env: TOXENV=py35
|
||||
dist: trusty
|
||||
python: '3.5.0'
|
||||
|
||||
# Coverage for:
|
||||
# - pytester's LsofFdLeakChecker
|
||||
# - TestArgComplete (linux only)
|
||||
# - numpy
|
||||
# Empty PYTEST_ADDOPTS to run this non-verbose.
|
||||
- env: TOXENV=py37-lsof-numpy-twisted-xdist PYTEST_COVERAGE=1 PYTEST_ADDOPTS=
|
||||
|
||||
# Specialized factors for py37.
|
||||
# Coverage for:
|
||||
# - test_sys_breakpoint_interception (via pexpect).
|
||||
- env: TOXENV=py37-pexpect PYTEST_COVERAGE=1
|
||||
- env: TOXENV=py37-pluggymaster-xdist
|
||||
- env: TOXENV=py37-freeze
|
||||
|
||||
- env: TOXENV=py38-xdist
|
||||
python: '3.8-dev'
|
||||
|
||||
- stage: baseline
|
||||
env: TOXENV=py27-pexpect,py27-trial,py27-numpy
|
||||
- env: TOXENV=py37-xdist
|
||||
- env: TOXENV=linting,docs,doctesting
|
||||
python: '3.7'
|
||||
env: TOXENV=py36-xdist
|
||||
python: '3.6'
|
||||
- env: TOXENV=linting,docs,doctesting PYTEST_COVERAGE=1
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pre-commit
|
||||
|
||||
- stage: deploy
|
||||
python: '3.6'
|
||||
env: PYTEST_NO_COVERAGE=1
|
||||
install: pip install -U setuptools setuptools_scm
|
||||
install: pip install -U setuptools setuptools_scm tox
|
||||
script: skip
|
||||
# token to upload github release notes: GH_RELEASE_NOTES_TOKEN
|
||||
env:
|
||||
- secure: "OjOeL7/0JUDkV00SsTs732e8vQjHynpbG9FKTNtZZJ+1Zn4Cib+hAlwmlBnvVukML0X60YpcfjnC4quDOIGLPsh5zeXnvJmYtAIIUNQXjWz8NhcGYrhyzuP1rqV22U68RTCdmOq3lMYU/W2acwHP7T49PwJtOiUM5kF120UAQ0Zi5EmkqkIvH8oM5mO9Dlver+/U7Htpz9rhKrHBXQNCMZI6yj2aUyukqB2PN2fjAlDbCF//+FmvYw9NjT4GeFOSkTCf4ER9yfqs7yglRfwiLtOCZ2qKQhWZNsSJDB89rxIRXWavJUjJKeY2EW2/NkomYJDpqJLIF4JeFRw/HhA47CYPeo6BJqyyNV+0CovL1frpWfi9UQw2cMbgFUkUIUk3F6DD59PHNIOX2R/HX56dQsw7WKl3QuHlCOkICXYg8F7Ta684IoKjeTX03/6QNOkURfDBwfGszY0FpbxrjCSWKom6RyZdyidnESaxv9RzjcIRZVh1rp8KMrwS1OrwRSdG0zjlsPr49hWMenN/8fKgcHTV4/r1Tj6mip0dorSRCrgUNIeRBKgmui6FS8642ab5JNKOxMteVPVR2sFuhjOQ0Jy+PmvceYY9ZMWc3+/B/KVh0dZ3hwvLGZep/vxDS2PwCA5/xw31714vT5LxidKo8yECjBynMU/wUTTS695D3NY="
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
# required by publish_gh_release_notes
|
||||
- pandoc
|
||||
after_deploy: tox -e publish_gh_release_notes
|
||||
deploy:
|
||||
provider: pypi
|
||||
user: nicoddemus
|
||||
@@ -74,27 +95,31 @@ jobs:
|
||||
tags: true
|
||||
repo: pytest-dev/pytest
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- python: '3.8-dev'
|
||||
env: TOXENV=py38-xdist
|
||||
|
||||
before_script:
|
||||
- |
|
||||
if [[ "$PYTEST_NO_COVERAGE" != 1 ]]; then
|
||||
# Do not (re-)upload coverage with cron runs.
|
||||
if [[ "$TRAVIS_EVENT_TYPE" = cron ]]; then
|
||||
PYTEST_COVERAGE=0
|
||||
fi
|
||||
- |
|
||||
if [[ "$PYTEST_COVERAGE" = 1 ]]; then
|
||||
export COVERAGE_FILE="$PWD/.coverage"
|
||||
export COVERAGE_PROCESS_START="$PWD/.coveragerc"
|
||||
export _PYTEST_TOX_COVERAGE_RUN="coverage run -m"
|
||||
export _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess
|
||||
fi
|
||||
|
||||
script: tox --recreate
|
||||
script: tox
|
||||
|
||||
after_success:
|
||||
- |
|
||||
if [[ "$PYTEST_NO_COVERAGE" != 1 ]]; then
|
||||
set -e
|
||||
# Add last TOXENV to $PATH.
|
||||
PATH="$PWD/.tox/${TOXENV##*,}/bin:$PATH"
|
||||
coverage combine
|
||||
coverage xml --ignore-errors
|
||||
coverage report -m --ignore-errors
|
||||
bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml -F "${TOXENV//-/,},linux"
|
||||
if [[ "$PYTEST_COVERAGE" = 1 ]]; then
|
||||
env CODECOV_NAME="$TOXENV-$TRAVIS_OS_NAME" scripts/report-coverage.sh
|
||||
fi
|
||||
|
||||
notifications:
|
||||
@@ -106,7 +131,3 @@ notifications:
|
||||
skip_join: true
|
||||
email:
|
||||
- pytest-commit@python.org
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pip
|
||||
- $HOME/.cache/pre-commit
|
||||
|
||||
32
AUTHORS
32
AUTHORS
@@ -6,22 +6,32 @@ Contributors include::
|
||||
Aaron Coleman
|
||||
Abdeali JK
|
||||
Abhijeet Kasurde
|
||||
Adam Johnson
|
||||
Adam Uhlir
|
||||
Ahn Ki-Wook
|
||||
Akiomi Kamakura
|
||||
Alan Velasco
|
||||
Alexander Johnson
|
||||
Alexei Kozlenok
|
||||
Allan Feldman
|
||||
Aly Sivji
|
||||
Amir Elkess
|
||||
Anatoly Bubenkoff
|
||||
Anders Hovmöller
|
||||
Andras Mitzki
|
||||
Andras Tim
|
||||
Andrea Cimatoribus
|
||||
Andreas Zeidler
|
||||
Andrey Paramonov
|
||||
Andrzej Klajnert
|
||||
Andrzej Ostrowski
|
||||
Andy Freeland
|
||||
Anthon van der Neut
|
||||
Anthony Shaw
|
||||
Anthony Sottile
|
||||
Anton Lodder
|
||||
Antony Lee
|
||||
Arel Cordero
|
||||
Armin Rigo
|
||||
Aron Coyle
|
||||
Aron Curzon
|
||||
@@ -45,11 +55,15 @@ Charles Cloud
|
||||
Charnjit SiNGH (CCSJ)
|
||||
Chris Lamb
|
||||
Christian Boelsen
|
||||
Christian Fetzer
|
||||
Christian Neumüller
|
||||
Christian Theunert
|
||||
Christian Tismer
|
||||
Christopher Gilling
|
||||
Christopher Dignam
|
||||
CrazyMerlyn
|
||||
Cyrus Maden
|
||||
Damian Skrzypczak
|
||||
Dhiren Serai
|
||||
Daniel Grana
|
||||
Daniel Hahler
|
||||
@@ -59,6 +73,7 @@ Danielle Jenkins
|
||||
Dave Hunt
|
||||
David Díaz-Barquero
|
||||
David Mohr
|
||||
David Paul Röthlisberger
|
||||
David Szotten
|
||||
David Vierra
|
||||
Daw-Ran Liou
|
||||
@@ -76,6 +91,7 @@ Endre Galaczi
|
||||
Eric Hunsberger
|
||||
Eric Siegerman
|
||||
Erik M. Bray
|
||||
Evan Kepner
|
||||
Fabien Zarifian
|
||||
Fabio Zadrozny
|
||||
Feng Ma
|
||||
@@ -96,6 +112,7 @@ Hugo van Kemenade
|
||||
Hui Wang (coldnight)
|
||||
Ian Bicking
|
||||
Ian Lesperance
|
||||
Ilya Konstantinov
|
||||
Ionuț Turturică
|
||||
Iwan Briquemont
|
||||
Jaap Broekhuizen
|
||||
@@ -114,6 +131,7 @@ Jonas Obrist
|
||||
Jordan Guymon
|
||||
Jordan Moldow
|
||||
Jordan Speicher
|
||||
Joseph Hunkeler
|
||||
Joshua Bronson
|
||||
Jurko Gospodnetić
|
||||
Justyna Janczyszyn
|
||||
@@ -121,8 +139,10 @@ Kale Kundert
|
||||
Katarzyna Jachim
|
||||
Katerina Koukiou
|
||||
Kevin Cox
|
||||
Kevin J. Foley
|
||||
Kodi B. Arfer
|
||||
Kostis Anagnostopoulos
|
||||
Kristoffer Nordström
|
||||
Kyle Altendorf
|
||||
Lawrence Mitchell
|
||||
Lee Kamentsky
|
||||
@@ -164,27 +184,34 @@ Miro Hrončok
|
||||
Nathaniel Waisbrot
|
||||
Ned Batchelder
|
||||
Neven Mundar
|
||||
Nicholas Devenish
|
||||
Nicholas Murphy
|
||||
Niclas Olofsson
|
||||
Nicolas Delaby
|
||||
Nikolay Kondratyev
|
||||
Oleg Pidsadnyi
|
||||
Oleg Sushchenko
|
||||
Oliver Bestwalter
|
||||
Omar Kohl
|
||||
Omer Hadari
|
||||
Ondřej Súkup
|
||||
Oscar Benjamin
|
||||
Patrick Hayes
|
||||
Paweł Adamczak
|
||||
Pedro Algarvio
|
||||
Pieter Mulder
|
||||
Piotr Banaszkiewicz
|
||||
Pulkit Goyal
|
||||
Punyashloka Biswal
|
||||
Quentin Pradet
|
||||
Ralf Schmitt
|
||||
Ralph Giles
|
||||
Ran Benita
|
||||
Raphael Castaneda
|
||||
Raphael Pierzina
|
||||
Raquel Alegre
|
||||
Ravi Chandra
|
||||
Robert Holt
|
||||
Roberto Polli
|
||||
Roland Puntaier
|
||||
Romain Dorgueil
|
||||
@@ -194,6 +221,7 @@ Ross Lawley
|
||||
Russel Winder
|
||||
Ryan Wooden
|
||||
Samuel Dion-Girardeau
|
||||
Samuel Searles-Bryant
|
||||
Samuele Pedroni
|
||||
Sankt Petersbug
|
||||
Segev Finer
|
||||
@@ -208,11 +236,13 @@ Steffen Allner
|
||||
Stephan Obermann
|
||||
Sven-Hendrik Haase
|
||||
Tadek Teleżyński
|
||||
Takafumi Arakaki
|
||||
Tarcisio Fischer
|
||||
Tareq Alayan
|
||||
Ted Xiao
|
||||
Thomas Grainger
|
||||
Thomas Hisch
|
||||
Tim Hoffmann
|
||||
Tim Strazny
|
||||
Tom Dalton
|
||||
Tom Viner
|
||||
@@ -227,10 +257,12 @@ Vidar T. Fauske
|
||||
Virgil Dupras
|
||||
Vitaly Lashmanov
|
||||
Vlad Dragos
|
||||
Volodymyr Piskun
|
||||
Wil Cooley
|
||||
William Lee
|
||||
Wim Glenn
|
||||
Wouter van Ackooy
|
||||
Xixi Zhao
|
||||
Xuan Luong
|
||||
Xuecong Liao
|
||||
Zac Hatfield-Dodds
|
||||
|
||||
1424
CHANGELOG.rst
1424
CHANGELOG.rst
File diff suppressed because it is too large
Load Diff
84
CODE_OF_CONDUCT.md
Normal file
84
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at coc@pytest.org. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
The coc@pytest.org address is routed to the following people who can also be
|
||||
contacted individually:
|
||||
|
||||
- Brianna Laugher ([@pfctdayelise](https://github.com/pfctdayelise)): brianna@laugher.id.au
|
||||
- Bruno Oliveira ([@nicoddemus](https://github.com/nicoddemus)): nicoddemus@gmail.com
|
||||
- Florian Bruhin ([@the-compiler](https://github.com/the-compiler)): pytest@the-compiler.org
|
||||
- Ronny Pfannschmidt ([@RonnyPfannschmidt](https://github.com/RonnyPfannschmidt)): ich@ronnypfannschmidt.de
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
@@ -5,8 +5,9 @@ Contribution getting started
|
||||
Contributions are highly welcomed and appreciated. Every little help counts,
|
||||
so do not hesitate!
|
||||
|
||||
.. contents:: Contribution links
|
||||
.. contents::
|
||||
:depth: 2
|
||||
:backlinks: none
|
||||
|
||||
|
||||
.. _submitfeedback:
|
||||
@@ -166,14 +167,14 @@ Short version
|
||||
#. Enable and install `pre-commit <https://pre-commit.com>`_ to ensure style-guides and code checks are followed.
|
||||
#. Target ``master`` for bugfixes and doc changes.
|
||||
#. Target ``features`` for new features or functionality changes.
|
||||
#. Follow **PEP-8** for naming and `black <https://github.com/ambv/black>`_ for formatting.
|
||||
#. Follow **PEP-8** for naming and `black <https://github.com/psf/black>`_ for formatting.
|
||||
#. Tests are run using ``tox``::
|
||||
|
||||
tox -e linting,py27,py37
|
||||
tox -e linting,py37
|
||||
|
||||
The test environments above are usually enough to cover most cases locally.
|
||||
|
||||
#. Write a ``changelog`` entry: ``changelog/2574.bugfix``, use issue id number
|
||||
#. Write a ``changelog`` entry: ``changelog/2574.bugfix.rst``, use issue id number
|
||||
and one of ``bugfix``, ``removal``, ``feature``, ``vendor``, ``doc`` or
|
||||
``trivial`` for the issue type.
|
||||
#. Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please
|
||||
@@ -217,7 +218,9 @@ Here is a simple overview, with pytest-specific bits:
|
||||
If you need some help with Git, follow this quick start
|
||||
guide: https://git.wiki.kernel.org/index.php/QuickStart
|
||||
|
||||
#. Install `pre-commit <https://pre-commit.com>`_ and its hook on the pytest repo::
|
||||
#. Install `pre-commit <https://pre-commit.com>`_ and its hook on the pytest repo:
|
||||
|
||||
**Note: pre-commit must be installed as admin, as it will not function otherwise**::
|
||||
|
||||
$ pip install --user pre-commit
|
||||
$ pre-commit install
|
||||
@@ -237,20 +240,20 @@ Here is a simple overview, with pytest-specific bits:
|
||||
|
||||
#. Run all the tests
|
||||
|
||||
You need to have Python 2.7 and 3.7 available in your system. Now
|
||||
You need to have Python 3.7 available in your system. Now
|
||||
running tests is as simple as issuing this command::
|
||||
|
||||
$ tox -e linting,py27,py37
|
||||
$ tox -e linting,py37
|
||||
|
||||
This command will run tests via the "tox" tool against Python 2.7 and 3.7
|
||||
This command will run tests via the "tox" tool against Python 3.7
|
||||
and also perform "lint" coding-style checks.
|
||||
|
||||
#. You can now edit your local working copy and run the tests again as necessary. Please follow PEP-8 for naming.
|
||||
|
||||
You can pass different options to ``tox``. For example, to run tests on Python 2.7 and pass options to pytest
|
||||
You can pass different options to ``tox``. For example, to run tests on Python 3.7 and pass options to pytest
|
||||
(e.g. enter pdb on failure) to pytest you can do::
|
||||
|
||||
$ tox -e py27 -- --pdb
|
||||
$ tox -e py37 -- --pdb
|
||||
|
||||
Or to only run tests in a particular test module on Python 3.7::
|
||||
|
||||
@@ -264,9 +267,10 @@ Here is a simple overview, with pytest-specific bits:
|
||||
$ git commit -a -m "<commit message>"
|
||||
$ git push -u
|
||||
|
||||
#. Create a new changelog entry in ``changelog``. The file should be named ``<issueid>.<type>``,
|
||||
#. Create a new changelog entry in ``changelog``. The file should be named ``<issueid>.<type>.rst``,
|
||||
where *issueid* is the number of the issue related to the change and *type* is one of
|
||||
``bugfix``, ``removal``, ``feature``, ``vendor``, ``doc`` or ``trivial``.
|
||||
``bugfix``, ``removal``, ``feature``, ``vendor``, ``doc`` or ``trivial``. You may not create a
|
||||
changelog entry if the change doesn't affect the documented behaviour of Pytest.
|
||||
|
||||
#. Add yourself to ``AUTHORS`` file if not there yet, in alphabetical order.
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ taking a lot of time to make a new one.
|
||||
|
||||
#. Create a branch ``release-X.Y.Z`` with the version for the release.
|
||||
|
||||
* **maintenance releases**: from ``4.6-maintenance``;
|
||||
|
||||
* **patch releases**: from the latest ``master``;
|
||||
|
||||
* **minor releases**: from the latest ``features``; then merge with the latest ``master``;
|
||||
@@ -24,7 +26,8 @@ taking a lot of time to make a new one.
|
||||
|
||||
This will generate a commit with all the changes ready for pushing.
|
||||
|
||||
#. Open a PR for this branch targeting ``master``.
|
||||
#. Open a PR for this branch targeting ``master`` (or ``4.6-maintenance`` for
|
||||
maintenance releases).
|
||||
|
||||
#. After all tests pass and the PR has been approved, publish to PyPI by pushing the tag::
|
||||
|
||||
@@ -33,7 +36,16 @@ taking a lot of time to make a new one.
|
||||
|
||||
Wait for the deploy to complete, then make sure it is `available on PyPI <https://pypi.org/project/pytest>`_.
|
||||
|
||||
#. Merge the PR into ``master``.
|
||||
#. Merge the PR.
|
||||
|
||||
#. If this is a maintenance release, cherry-pick the CHANGELOG / announce
|
||||
files to the ``master`` branch::
|
||||
|
||||
git fetch --all --prune
|
||||
git checkout origin/master -b cherry-pick-maintenance-release
|
||||
git cherry-pick --no-commit -m1 origin/4.6-maintenance
|
||||
git checkout origin/master -- changelog
|
||||
git commit # no arguments
|
||||
|
||||
#. Send an email announcement with the contents from::
|
||||
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2004-2017 Holger Krekel and others
|
||||
Copyright (c) 2004-2019 Holger Krekel and others
|
||||
|
||||
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
|
||||
|
||||
44
OPENCOLLECTIVE.rst
Normal file
44
OPENCOLLECTIVE.rst
Normal file
@@ -0,0 +1,44 @@
|
||||
==============
|
||||
OpenCollective
|
||||
==============
|
||||
|
||||
pytest has a collective setup at `OpenCollective`_. This document describes how the core team manages
|
||||
OpenCollective-related activities.
|
||||
|
||||
What is it
|
||||
==========
|
||||
|
||||
Open Collective is an online funding platform for open and transparent communities.
|
||||
It provides tools to raise money and share your finances in full transparency.
|
||||
|
||||
It is the platform of choice for individuals and companies that want to make one-time or
|
||||
monthly donations directly to the project.
|
||||
|
||||
Funds
|
||||
=====
|
||||
|
||||
The OpenCollective funds donated to pytest will be used to fund overall maintenance,
|
||||
local sprints, merchandising (stickers to distribute in conferences for example), and future
|
||||
gatherings of pytest developers (sprints).
|
||||
|
||||
`Core contributors`_ which are contributing on a continuous basis are free to submit invoices
|
||||
to bill maintenance hours using the platform. How much each contributor should request is still an
|
||||
open question, but we should use common sense and trust in the contributors, most of which know
|
||||
themselves in-person. A good rule of thumb is to bill the same amount as monthly payments
|
||||
contributors which participate in the `Tidelift`_ subscription. If in doubt, just ask.
|
||||
|
||||
Admins
|
||||
======
|
||||
|
||||
A few people have admin access to the OpenCollective dashboard to make changes. Those people
|
||||
are part of the `@pytest-dev/opencollective-admins`_ team.
|
||||
|
||||
`Core contributors`_ interested in helping out with OpenCollective maintenance are welcome! We don't
|
||||
expect much work here other than the occasional approval of expenses from other core contributors.
|
||||
Just drop a line to one of the `@pytest-dev/opencollective-admins`_ or use the mailing list.
|
||||
|
||||
|
||||
.. _`OpenCollective`: https://opencollective.com/pytest
|
||||
.. _`Tidelift`: https://tidelift.com
|
||||
.. _`core contributors`: https://github.com/orgs/pytest-dev/teams/core/members
|
||||
.. _`@pytest-dev/opencollective-admins`: https://github.com/orgs/pytest-dev/teams/opencollective-admins/members
|
||||
30
README.rst
30
README.rst
@@ -22,11 +22,11 @@
|
||||
.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master
|
||||
:target: https://travis-ci.org/pytest-dev/pytest
|
||||
|
||||
.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true
|
||||
:target: https://ci.appveyor.com/project/pytestbot/pytest
|
||||
.. image:: https://dev.azure.com/pytest-dev/pytest/_apis/build/status/pytest-CI?branchName=master
|
||||
:target: https://dev.azure.com/pytest-dev/pytest
|
||||
|
||||
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
||||
:target: https://github.com/ambv/black
|
||||
:target: https://github.com/psf/black
|
||||
|
||||
.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg
|
||||
:target: https://www.codetriage.com/pytest-dev/pytest
|
||||
@@ -85,7 +85,7 @@ Features
|
||||
- Can run `unittest <https://docs.pytest.org/en/latest/unittest.html>`_ (or trial),
|
||||
`nose <https://docs.pytest.org/en/latest/nose.html>`_ test suites out of the box;
|
||||
|
||||
- Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (untested);
|
||||
- Python 3.5+ and PyPy3;
|
||||
|
||||
- Rich plugin architecture, with over 315+ `external plugins <http://plugincompat.herokuapp.com>`_ and thriving community;
|
||||
|
||||
@@ -108,10 +108,30 @@ Changelog
|
||||
Consult the `Changelog <https://docs.pytest.org/en/latest/changelog.html>`__ page for fixes and enhancements of each version.
|
||||
|
||||
|
||||
Support pytest
|
||||
--------------
|
||||
|
||||
You can support pytest by obtaining a `Tidelift subscription`_.
|
||||
|
||||
Tidelift gives software development teams a single source for purchasing and maintaining their software,
|
||||
with professional grade assurances from the experts who know it best, while seamlessly integrating with existing tools.
|
||||
|
||||
|
||||
.. _`Tidelift subscription`: https://tidelift.com/subscription/pkg/pypi-pytest?utm_source=pypi-pytest&utm_medium=referral&utm_campaign=readme
|
||||
|
||||
|
||||
Security
|
||||
^^^^^^^^
|
||||
|
||||
pytest has never been associated with a security vunerability, but in any case, to report a
|
||||
security vulnerability please use the `Tidelift security contact <https://tidelift.com/security>`_.
|
||||
Tidelift will coordinate the fix and disclosure.
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright Holger Krekel and others, 2004-2018.
|
||||
Copyright Holger Krekel and others, 2004-2019.
|
||||
|
||||
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
|
||||
|
||||
|
||||
60
TIDELIFT.rst
Normal file
60
TIDELIFT.rst
Normal file
@@ -0,0 +1,60 @@
|
||||
========
|
||||
Tidelift
|
||||
========
|
||||
|
||||
pytest is a member of `Tidelift`_. This document describes how the core team manages
|
||||
Tidelift-related activities.
|
||||
|
||||
What is it
|
||||
==========
|
||||
|
||||
Tidelift aims to make Open Source sustainable by offering subscriptions to companies which rely
|
||||
on Open Source packages. This subscription allows it to pay maintainers of those Open Source
|
||||
packages to aid sustainability of the work.
|
||||
|
||||
It is the perfect platform for companies that want to support Open Source packages and at the same
|
||||
time obtain assurances regarding maintenance, quality and security.
|
||||
|
||||
Funds
|
||||
=====
|
||||
|
||||
It was decided in the `mailing list`_ that the Tidelift contribution will be split evenly between
|
||||
members of the `contributors team`_ interested in receiving funding.
|
||||
|
||||
The current list of contributors receiving funding are:
|
||||
|
||||
* `@asottile`_
|
||||
* `@blueyed`_
|
||||
* `@nicoddemus`_
|
||||
|
||||
Contributors interested in receiving a part of the funds just need to submit a PR adding their
|
||||
name to the list. Contributors that want to stop receiving the funds should also submit a PR
|
||||
in the same way.
|
||||
|
||||
The PR should mention `@pytest-dev/tidelift-admins`_ so appropriate changes
|
||||
can be made in the Tidelift platform.
|
||||
|
||||
After the PR has been accepted and merged, the contributor should register in the `Tidelift`_
|
||||
platform and follow the instructions there, including signing an `agreement`_.
|
||||
|
||||
Admins
|
||||
======
|
||||
|
||||
A few people have admin access to the Tidelift dashboard to make changes. Those people
|
||||
are part of the `@pytest-dev/tidelift-admins`_ team.
|
||||
|
||||
`Core contributors`_ interested in helping out with Tidelift maintenance are welcome! We don't
|
||||
expect much work here other than the occasional adding/removal of a contributor from receiving
|
||||
funds. Just drop a line to one of the `@pytest-dev/tidelift-admins`_ or use the mailing list.
|
||||
|
||||
|
||||
.. _`Tidelift`: https://tidelift.com
|
||||
.. _`mailing list`: https://mail.python.org/pipermail/pytest-dev/2019-May/004716.html
|
||||
.. _`contributors team`: https://github.com/orgs/pytest-dev/teams/contributors
|
||||
.. _`core contributors`: https://github.com/orgs/pytest-dev/teams/core/members
|
||||
.. _`@pytest-dev/tidelift-admins`: https://github.com/orgs/pytest-dev/teams/tidelift-admins/members
|
||||
.. _`agreement`: https://tidelift.com/docs/lifting/agreement
|
||||
|
||||
.. _`@asottile`: https://github.com/asottile
|
||||
.. _`@blueyed`: https://github.com/blueyed
|
||||
.. _`@nicoddemus`: https://github.com/nicoddemus
|
||||
53
appveyor.yml
53
appveyor.yml
@@ -1,53 +0,0 @@
|
||||
environment:
|
||||
matrix:
|
||||
- TOXENV: "py37-xdist"
|
||||
- TOXENV: "py27-xdist"
|
||||
- TOXENV: "py27"
|
||||
- TOXENV: "py37"
|
||||
- TOXENV: "linting,docs,doctesting"
|
||||
- TOXENV: "py36"
|
||||
- TOXENV: "py35"
|
||||
- TOXENV: "py34"
|
||||
- TOXENV: "pypy"
|
||||
PYTEST_NO_COVERAGE: "1"
|
||||
# Specialized factors for py27.
|
||||
- TOXENV: "py27-trial,py27-numpy,py27-nobyte"
|
||||
- TOXENV: "py27-pluggymaster"
|
||||
PYTEST_NO_COVERAGE: "1"
|
||||
# Specialized factors for py37.
|
||||
- TOXENV: "py37-trial,py37-numpy"
|
||||
- TOXENV: "py37-pluggymaster"
|
||||
PYTEST_NO_COVERAGE: "1"
|
||||
- TOXENV: "py37-freeze"
|
||||
PYTEST_NO_COVERAGE: "1"
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
install:
|
||||
- echo Installed Pythons
|
||||
- dir c:\Python*
|
||||
|
||||
- if "%TOXENV%" == "pypy" call scripts\install-pypy.bat
|
||||
|
||||
- C:\Python36\python -m pip install --upgrade pip
|
||||
- C:\Python36\python -m pip install --upgrade --pre tox
|
||||
|
||||
build: false # Not a C# project, build stuff at the test step instead.
|
||||
|
||||
before_test:
|
||||
- call scripts\prepare-coverage.bat
|
||||
|
||||
test_script:
|
||||
- C:\Python36\python -m tox
|
||||
|
||||
on_success:
|
||||
- call scripts\upload-coverage.bat
|
||||
|
||||
cache:
|
||||
- '%LOCALAPPDATA%\pip\cache'
|
||||
- '%USERPROFILE%\.cache\pre-commit'
|
||||
|
||||
# We don't deploy anything on tags with AppVeyor, we use Travis instead, so we
|
||||
# might as well save resources
|
||||
skip_tags: true
|
||||
77
azure-pipelines.yml
Normal file
77
azure-pipelines.yml
Normal file
@@ -0,0 +1,77 @@
|
||||
trigger:
|
||||
- master
|
||||
- features
|
||||
|
||||
variables:
|
||||
PYTEST_ADDOPTS: "--junitxml=build/test-results/$(tox.env).xml -vv"
|
||||
PYTEST_COVERAGE: '0'
|
||||
|
||||
jobs:
|
||||
|
||||
- job: 'Test'
|
||||
pool:
|
||||
vmImage: "vs2017-win2016"
|
||||
strategy:
|
||||
matrix:
|
||||
# -- pypy3 disabled for now: #5279 --
|
||||
# pypy3:
|
||||
# python.version: 'pypy3'
|
||||
# tox.env: 'pypy3'
|
||||
py35-xdist:
|
||||
python.version: '3.5'
|
||||
tox.env: 'py35-xdist'
|
||||
# Coverage for:
|
||||
# - test_supports_breakpoint_module_global
|
||||
PYTEST_COVERAGE: '1'
|
||||
py36-xdist:
|
||||
python.version: '3.6'
|
||||
tox.env: 'py36-xdist'
|
||||
py37:
|
||||
python.version: '3.7'
|
||||
tox.env: 'py37-twisted-numpy'
|
||||
# Coverage for:
|
||||
# - _py36_windowsconsoleio_workaround (with py36+)
|
||||
# - test_request_garbage (no xdist)
|
||||
PYTEST_COVERAGE: '1'
|
||||
py37-linting/docs/doctesting:
|
||||
python.version: '3.7'
|
||||
tox.env: 'linting,docs,doctesting'
|
||||
py37-pluggymaster-xdist:
|
||||
python.version: '3.7'
|
||||
tox.env: 'py37-pluggymaster-xdist'
|
||||
maxParallel: 10
|
||||
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '$(python.version)'
|
||||
architecture: 'x64'
|
||||
|
||||
- script: python -m pip install --upgrade pip && python -m pip install tox
|
||||
displayName: 'Install tox'
|
||||
|
||||
- bash: |
|
||||
if [[ "$PYTEST_COVERAGE" == "1" ]]; then
|
||||
export _PYTEST_TOX_COVERAGE_RUN="coverage run -m"
|
||||
export _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess
|
||||
export COVERAGE_FILE="$PWD/.coverage"
|
||||
export COVERAGE_PROCESS_START="$PWD/.coveragerc"
|
||||
fi
|
||||
python -m tox -e $(tox.env)
|
||||
displayName: 'Run tests'
|
||||
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testResultsFiles: 'build/test-results/$(tox.env).xml'
|
||||
testRunTitle: '$(tox.env)'
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- bash: |
|
||||
if [[ "$PYTEST_COVERAGE" == 1 ]]; then
|
||||
scripts/report-coverage.sh
|
||||
fi
|
||||
env:
|
||||
CODECOV_NAME: $(tox.env)
|
||||
CODECOV_TOKEN: $(CODECOV_TOKEN)
|
||||
displayName: Report and upload coverage
|
||||
condition: eq(variables['PYTEST_COVERAGE'], '1')
|
||||
@@ -5,8 +5,8 @@ if __name__ == "__main__":
|
||||
import pytest # NOQA
|
||||
import pstats
|
||||
|
||||
script = sys.argv[1:] if len(sys.argv) > 1 else "empty.py"
|
||||
stats = cProfile.run("pytest.cmdline.main(%r)" % script, "prof")
|
||||
script = sys.argv[1:] if len(sys.argv) > 1 else ["empty.py"]
|
||||
cProfile.run("pytest.cmdline.main(%r)" % script, "prof")
|
||||
p = pstats.Stats("prof")
|
||||
p.strip_dirs()
|
||||
p.sort_stats("cumulative")
|
||||
|
||||
@@ -16,4 +16,4 @@ run = 'fc("/d")'
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(timeit.timeit(run, setup=setup % imports[0], number=count))
|
||||
print((timeit.timeit(run, setup=setup % imports[1], number=count)))
|
||||
print(timeit.timeit(run, setup=setup % imports[1], number=count))
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
import six
|
||||
|
||||
for i in range(1000):
|
||||
six.exec_("def test_func_%d(): pass" % i)
|
||||
exec("def test_func_%d(): pass" % i)
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
from six.moves import range
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
SKIP = True
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ Each file should be named like ``<ISSUE>.<TYPE>.rst``, where
|
||||
``<ISSUE>`` is an issue number, and ``<TYPE>`` is one of:
|
||||
|
||||
* ``feature``: new user facing features, like new command-line options and new behavior.
|
||||
* ``improvement``: improvement of existing functionality, usually without requiring user intervention (for example, new fields being written in ``--junitxml``, improved colors in terminal, etc).
|
||||
* ``bugfix``: fixes a reported bug.
|
||||
* ``doc``: documentation improvement, like rewording an entire session or adding missing docs.
|
||||
* ``deprecation``: feature deprecation.
|
||||
|
||||
7
codecov.yml
Normal file
7
codecov.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
coverage:
|
||||
status:
|
||||
project: true
|
||||
patch: true
|
||||
changes: true
|
||||
|
||||
comment: off
|
||||
@@ -13,7 +13,10 @@ PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
REGENDOC_ARGS := \
|
||||
--normalize "/in \d+.\d+ seconds/in 0.12 seconds/" \
|
||||
--normalize "/[ \t]+\n/\n/" \
|
||||
--normalize "~\$$REGENDOC_TMPDIR~/home/sweet/project~" \
|
||||
--normalize "~/path/to/example~/home/sweet/project~" \
|
||||
--normalize "/in \d+.\d+s ==/in 0.12s ==/" \
|
||||
--normalize "@/tmp/pytest-of-.*/pytest-\d+@PYTEST_TMPDIR@" \
|
||||
--normalize "@pytest-(\d+)\\.[^ ,]+@pytest-\1.x.y@" \
|
||||
--normalize "@(This is pytest version )(\d+)\\.[^ ,]+@\1\2.x.y@" \
|
||||
@@ -38,8 +41,9 @@ help:
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
regen: REGENDOC_FILES:=*.rst */*.rst
|
||||
regen:
|
||||
PYTHONDONTWRITEBYTECODE=1 PYTEST_ADDOPT=-pno:hypothesis COLUMNS=76 regendoc --update *.rst */*.rst ${REGENDOC_ARGS}
|
||||
PYTHONDONTWRITEBYTECODE=1 PYTEST_ADDOPTS="-pno:hypothesis -Wignore::pytest.PytestUnknownMarkWarning" COLUMNS=76 regendoc --update ${REGENDOC_FILES} ${REGENDOC_ARGS}
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
<li><a href="{{ pathto('index') }}">Home</a></li>
|
||||
<li><a href="{{ pathto('getting-started') }}">Install</a></li>
|
||||
<li><a href="{{ pathto('contents') }}">Contents</a></li>
|
||||
<li><a href="{{ pathto('reference') }}">Reference</a></li>
|
||||
<li><a href="{{ pathto('reference') }}">API Reference</a></li>
|
||||
<li><a href="{{ pathto('example/index') }}">Examples</a></li>
|
||||
<li><a href="{{ pathto('customize') }}">Customize</a></li>
|
||||
<li><a href="{{ pathto('changelog') }}">Changelog</a></li>
|
||||
<li><a href="{{ pathto('contributing') }}">Contributing</a></li>
|
||||
<li><a href="{{ pathto('backwards-compatibility') }}">Backwards Compatibility</a></li>
|
||||
<li><a href="{{ pathto('py27-py34-deprecation') }}">Python 2.7 and 3.4 Support</a></li>
|
||||
<li><a href="{{ pathto('sponsor') }}">Sponsor</a></li>
|
||||
<li><a href="{{ pathto('license') }}">License</a></li>
|
||||
<li><a href="{{ pathto('contact') }}">Contact Channels</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
{%- block footer %}
|
||||
<div class="footer">
|
||||
© Copyright {{ copyright }}.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> {{ sphinx_version }}.
|
||||
</div>
|
||||
{% if pagename == 'index' %}
|
||||
</div>
|
||||
|
||||
15
doc/en/_themes/flask/slim_searchbox.html
Normal file
15
doc/en/_themes/flask/slim_searchbox.html
Normal file
@@ -0,0 +1,15 @@
|
||||
{#
|
||||
basic/searchbox.html with heading removed.
|
||||
#}
|
||||
{%- if pagename != "search" and builder != "singlehtml" %}
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="{{ pathto('search') }}" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel"
|
||||
placeholder="Search"/>
|
||||
<input type="submit" value="{{ _('Go') }}" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
{%- endif %}
|
||||
@@ -8,11 +8,12 @@
|
||||
|
||||
{% set page_width = '1020px' %}
|
||||
{% set sidebar_width = '220px' %}
|
||||
/* orange of logo is #d67c29 but we use black for links for now */
|
||||
{% set link_color = '#000' %}
|
||||
{% set link_hover_color = '#000' %}
|
||||
/* muted version of green logo color #C9D22A */
|
||||
{% set link_color = '#606413' %}
|
||||
/* blue logo color */
|
||||
{% set link_hover_color = '#009de0' %}
|
||||
{% set base_font = 'sans-serif' %}
|
||||
{% set header_font = 'serif' %}
|
||||
{% set header_font = 'sans-serif' %}
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
@@ -20,7 +21,7 @@
|
||||
|
||||
body {
|
||||
font-family: {{ base_font }};
|
||||
font-size: 17px;
|
||||
font-size: 16px;
|
||||
background-color: white;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
@@ -78,13 +79,13 @@ div.related {
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted #999;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a:hover {
|
||||
border-bottom: 1px solid #999;
|
||||
color: {{ link_hover_color }};
|
||||
border-bottom: 1px solid {{ link_hover_color }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
@@ -106,14 +107,14 @@ div.sphinxsidebar h3,
|
||||
div.sphinxsidebar h4 {
|
||||
font-family: {{ header_font }};
|
||||
color: #444;
|
||||
font-size: 24px;
|
||||
font-size: 21px;
|
||||
font-weight: normal;
|
||||
margin: 0 0 5px 0;
|
||||
margin: 16px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h4 {
|
||||
font-size: 20px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 a {
|
||||
@@ -205,10 +206,22 @@ div.body p, div.body dd, div.body li {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
ul.simple li {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
div.topic ul.simple li {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.topic li > p:first-child {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.admonition {
|
||||
background: #fafafa;
|
||||
margin: 20px -30px;
|
||||
padding: 10px 30px;
|
||||
padding: 10px 20px;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
@@ -217,11 +230,6 @@ div.admonition tt.xref, div.admonition a tt {
|
||||
border-bottom: 1px solid #fafafa;
|
||||
}
|
||||
|
||||
dd div.admonition {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title {
|
||||
font-family: {{ header_font }};
|
||||
font-weight: normal;
|
||||
@@ -231,7 +239,7 @@ div.admonition p.admonition-title {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
div.admonition p.last {
|
||||
div.admonition :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -243,7 +251,7 @@ dt:target, .highlight {
|
||||
background: #FAF3E8;
|
||||
}
|
||||
|
||||
div.note {
|
||||
div.note, div.warning {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
@@ -257,6 +265,11 @@ div.topic {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
div.topic a {
|
||||
text-decoration: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
@@ -358,21 +371,10 @@ ul, ol {
|
||||
|
||||
pre {
|
||||
background: #eee;
|
||||
padding: 7px 30px;
|
||||
margin: 15px -30px;
|
||||
padding: 7px 12px;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
dl pre, blockquote pre, li pre {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
dl dl pre {
|
||||
margin-left: -90px;
|
||||
padding-left: 90px;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #ecf0f3;
|
||||
color: #222;
|
||||
@@ -393,6 +395,20 @@ a.reference:hover {
|
||||
border-bottom: 1px solid {{ link_hover_color }};
|
||||
}
|
||||
|
||||
li.toctree-l1 a.reference,
|
||||
li.toctree-l2 a.reference,
|
||||
li.toctree-l3 a.reference,
|
||||
li.toctree-l4 a.reference {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
li.toctree-l1 a.reference:hover,
|
||||
li.toctree-l2 a.reference:hover,
|
||||
li.toctree-l3 a.reference:hover,
|
||||
li.toctree-l4 a.reference:hover {
|
||||
border-bottom: 1px solid {{ link_hover_color }};
|
||||
}
|
||||
|
||||
a.footnote-reference {
|
||||
text-decoration: none;
|
||||
font-size: 0.7em;
|
||||
@@ -408,6 +424,56 @@ a:hover tt {
|
||||
background: #EEE;
|
||||
}
|
||||
|
||||
#reference div.section h2 {
|
||||
/* separate code elements in the reference section */
|
||||
border-top: 2px solid #ccc;
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
#reference div.section h3 {
|
||||
/* separate code elements in the reference section */
|
||||
border-top: 1px solid #ccc;
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
dl.class, dl.function {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
dl.class > dd {
|
||||
border-left: 3px solid #ccc;
|
||||
margin-left: 0px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
dl.field-list {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
dl.field-list dd {
|
||||
padding-left: 4em;
|
||||
border-left: 3px solid #ccc;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
dl.field-list dd > ul {
|
||||
list-style: none;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
dl.field-list dd > ul > li li :first-child {
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
dl.field-list dd > ul > li :first-child {
|
||||
text-indent: -2em;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
dl.field-list dd > p:first-child {
|
||||
text-indent: -2em;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 870px) {
|
||||
|
||||
|
||||
@@ -24,11 +24,9 @@ The ideal pytest helper
|
||||
- feels confident in using pytest (e.g. has explored command line options, knows how to write parametrized tests, has an idea about conftest contents)
|
||||
- does not need to be an expert in every aspect!
|
||||
|
||||
`Pytest helpers, sign up here`_! (preferably in February, hard deadline 22 March)
|
||||
Pytest helpers, sign up here! (preferably in February, hard deadline 22 March)
|
||||
|
||||
|
||||
.. _`Pytest helpers, sign up here`: http://goo.gl/forms/nxqAhqWt1P
|
||||
|
||||
|
||||
The ideal partner project
|
||||
-----------------------------------------
|
||||
@@ -40,11 +38,9 @@ The ideal partner project
|
||||
- has the support of the core development team, in trying out pytest adoption
|
||||
- has no tests... or 100% test coverage... or somewhere in between!
|
||||
|
||||
`Partner projects, sign up here`_! (by 22 March)
|
||||
Partner projects, sign up here! (by 22 March)
|
||||
|
||||
|
||||
.. _`Partner projects, sign up here`: http://goo.gl/forms/ZGyqlHiwk3
|
||||
|
||||
|
||||
What does it mean to "adopt pytest"?
|
||||
-----------------------------------------
|
||||
@@ -68,11 +64,11 @@ Progressive success might look like:
|
||||
It may be after the month is up, the partner project decides that pytest is not right for it. That's okay - hopefully the pytest team will also learn something about its weaknesses or deficiencies.
|
||||
|
||||
.. _`nose and unittest`: faq.html#how-does-pytest-relate-to-nose-and-unittest
|
||||
.. _assert: asserts.html
|
||||
.. _assert: assert.html
|
||||
.. _pycmd: https://bitbucket.org/hpk42/pycmd/overview
|
||||
.. _`setUp/tearDown methods`: xunit_setup.html
|
||||
.. _fixtures: fixture.html
|
||||
.. _markers: markers.html
|
||||
.. _markers: mark.html
|
||||
.. _distributed: xdist.html
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,27 @@ Release announcements
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
release-5.1.2
|
||||
release-5.1.1
|
||||
release-5.1.0
|
||||
release-5.0.1
|
||||
release-5.0.0
|
||||
release-4.6.5
|
||||
release-4.6.4
|
||||
release-4.6.3
|
||||
release-4.6.2
|
||||
release-4.6.1
|
||||
release-4.6.0
|
||||
release-4.5.0
|
||||
release-4.4.2
|
||||
release-4.4.1
|
||||
release-4.4.0
|
||||
release-4.3.1
|
||||
release-4.3.0
|
||||
release-4.2.1
|
||||
release-4.2.0
|
||||
release-4.1.1
|
||||
release-4.1.0
|
||||
release-4.0.2
|
||||
release-4.0.1
|
||||
release-4.0.0
|
||||
|
||||
@@ -12,7 +12,7 @@ courtesy of Benjamin Peterson. You can now safely use ``assert``
|
||||
statements in test modules without having to worry about side effects
|
||||
or python optimization ("-OO") options. This is achieved by rewriting
|
||||
assert statements in test modules upon import, using a PEP302 hook.
|
||||
See http://pytest.org/assert.html#advanced-assertion-introspection for
|
||||
See https://docs.pytest.org/en/latest/assert.html for
|
||||
detailed information. The work has been partly sponsored by my company,
|
||||
merlinux GmbH.
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ The py.test Development Team
|
||||
|
||||
**Changes**
|
||||
|
||||
* **Important**: `py.code <https://pylib.readthedocs.io/en/latest/code.html>`_ has been
|
||||
* **Important**: `py.code <https://pylib.readthedocs.io/en/stable/code.html>`_ has been
|
||||
merged into the ``pytest`` repository as ``pytest._code``. This decision
|
||||
was made because ``py.code`` had very few uses outside ``pytest`` and the
|
||||
fact that it was in a different repository made it difficult to fix bugs on
|
||||
@@ -88,7 +88,7 @@ The py.test Development Team
|
||||
**experimental**, so you definitely should not import it explicitly!
|
||||
|
||||
Please note that the original ``py.code`` is still available in
|
||||
`pylib <https://pylib.readthedocs.io>`_.
|
||||
`pylib <https://pylib.readthedocs.io/en/stable/>`_.
|
||||
|
||||
* ``pytest_enter_pdb`` now optionally receives the pytest config object.
|
||||
Thanks `@nicoddemus`_ for the PR.
|
||||
|
||||
@@ -66,8 +66,8 @@ The py.test Development Team
|
||||
|
||||
.. _#510: https://github.com/pytest-dev/pytest/issues/510
|
||||
.. _#1506: https://github.com/pytest-dev/pytest/pull/1506
|
||||
.. _#1496: https://github.com/pytest-dev/pytest/issue/1496
|
||||
.. _#1524: https://github.com/pytest-dev/pytest/issue/1524
|
||||
.. _#1496: https://github.com/pytest-dev/pytest/issues/1496
|
||||
.. _#1524: https://github.com/pytest-dev/pytest/pull/1524
|
||||
|
||||
.. _@astraw38: https://github.com/astraw38
|
||||
.. _@hackebrot: https://github.com/hackebrot
|
||||
|
||||
@@ -20,7 +20,7 @@ Thanks to all who contributed to this release, among them:
|
||||
* Jeffrey Rackauckas
|
||||
* Jose Carlos Menezes
|
||||
* Ronny Pfannschmidt
|
||||
* Zac-HD
|
||||
* Zac Hatfield-Dodds
|
||||
* iwanb
|
||||
|
||||
|
||||
|
||||
44
doc/en/announce/release-4.1.0.rst
Normal file
44
doc/en/announce/release-4.1.0.rst
Normal file
@@ -0,0 +1,44 @@
|
||||
pytest-4.1.0
|
||||
=======================================
|
||||
|
||||
The pytest team is proud to announce the 4.1.0 release!
|
||||
|
||||
pytest is a mature Python testing tool with more than a 2000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
This release contains a number of bugs fixes and improvements, so users are encouraged
|
||||
to take a look at the CHANGELOG:
|
||||
|
||||
https://docs.pytest.org/en/latest/changelog.html
|
||||
|
||||
For complete documentation, please visit:
|
||||
|
||||
https://docs.pytest.org/en/latest/
|
||||
|
||||
As usual, you can upgrade from pypi via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Adam Johnson
|
||||
* Aly Sivji
|
||||
* Andrey Paramonov
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* David Vo
|
||||
* Hyunchel Kim
|
||||
* Jeffrey Rackauckas
|
||||
* Kanguros
|
||||
* Nicholas Devenish
|
||||
* Pedro Algarvio
|
||||
* Randy Barlow
|
||||
* Ronny Pfannschmidt
|
||||
* Tomer Keren
|
||||
* feuillemorte
|
||||
* wim glenn
|
||||
|
||||
|
||||
Happy testing,
|
||||
The Pytest Development Team
|
||||
27
doc/en/announce/release-4.1.1.rst
Normal file
27
doc/en/announce/release-4.1.1.rst
Normal file
@@ -0,0 +1,27 @@
|
||||
pytest-4.1.1
|
||||
=======================================
|
||||
|
||||
pytest 4.1.1 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Anton Lodder
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* David Vo
|
||||
* Oscar Benjamin
|
||||
* Ronny Pfannschmidt
|
||||
* Victor Maryama
|
||||
* Yoav Caspi
|
||||
* dmitry.dygalo
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
37
doc/en/announce/release-4.2.0.rst
Normal file
37
doc/en/announce/release-4.2.0.rst
Normal file
@@ -0,0 +1,37 @@
|
||||
pytest-4.2.0
|
||||
=======================================
|
||||
|
||||
The pytest team is proud to announce the 4.2.0 release!
|
||||
|
||||
pytest is a mature Python testing tool with more than a 2000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
This release contains a number of bugs fixes and improvements, so users are encouraged
|
||||
to take a look at the CHANGELOG:
|
||||
|
||||
https://docs.pytest.org/en/latest/changelog.html
|
||||
|
||||
For complete documentation, please visit:
|
||||
|
||||
https://docs.pytest.org/en/latest/
|
||||
|
||||
As usual, you can upgrade from pypi via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Adam Uhlir
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Christopher Dignam
|
||||
* Daniel Hahler
|
||||
* Joseph Hunkeler
|
||||
* Kristoffer Nordstroem
|
||||
* Ronny Pfannschmidt
|
||||
* Thomas Hisch
|
||||
* wim glenn
|
||||
|
||||
|
||||
Happy testing,
|
||||
The Pytest Development Team
|
||||
30
doc/en/announce/release-4.2.1.rst
Normal file
30
doc/en/announce/release-4.2.1.rst
Normal file
@@ -0,0 +1,30 @@
|
||||
pytest-4.2.1
|
||||
=======================================
|
||||
|
||||
pytest 4.2.1 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Arel Cordero
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Holger Kohr
|
||||
* Kevin J. Foley
|
||||
* Nick Murphy
|
||||
* Paweł Stradomski
|
||||
* Raphael Pierzina
|
||||
* Ronny Pfannschmidt
|
||||
* Sam Brightman
|
||||
* Thomas Hisch
|
||||
* Zac Hatfield-Dodds
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
36
doc/en/announce/release-4.3.0.rst
Normal file
36
doc/en/announce/release-4.3.0.rst
Normal file
@@ -0,0 +1,36 @@
|
||||
pytest-4.3.0
|
||||
=======================================
|
||||
|
||||
The pytest team is proud to announce the 4.3.0 release!
|
||||
|
||||
pytest is a mature Python testing tool with more than a 2000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
This release contains a number of bugs fixes and improvements, so users are encouraged
|
||||
to take a look at the CHANGELOG:
|
||||
|
||||
https://docs.pytest.org/en/latest/changelog.html
|
||||
|
||||
For complete documentation, please visit:
|
||||
|
||||
https://docs.pytest.org/en/latest/
|
||||
|
||||
As usual, you can upgrade from pypi via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Andras Mitzki
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Christian Fetzer
|
||||
* Daniel Hahler
|
||||
* Grygorii Iermolenko
|
||||
* R. Alex Matevish
|
||||
* Ronny Pfannschmidt
|
||||
* cclauss
|
||||
|
||||
|
||||
Happy testing,
|
||||
The Pytest Development Team
|
||||
28
doc/en/announce/release-4.3.1.rst
Normal file
28
doc/en/announce/release-4.3.1.rst
Normal file
@@ -0,0 +1,28 @@
|
||||
pytest-4.3.1
|
||||
=======================================
|
||||
|
||||
pytest 4.3.1 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Andras Mitzki
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Danilo Horta
|
||||
* Grygorii Iermolenko
|
||||
* Jeff Hale
|
||||
* Kyle Altendorf
|
||||
* Stephan Hoyer
|
||||
* Zac Hatfield-Dodds
|
||||
* songbowen
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
39
doc/en/announce/release-4.4.0.rst
Normal file
39
doc/en/announce/release-4.4.0.rst
Normal file
@@ -0,0 +1,39 @@
|
||||
pytest-4.4.0
|
||||
=======================================
|
||||
|
||||
The pytest team is proud to announce the 4.4.0 release!
|
||||
|
||||
pytest is a mature Python testing tool with more than a 2000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
This release contains a number of bugs fixes and improvements, so users are encouraged
|
||||
to take a look at the CHANGELOG:
|
||||
|
||||
https://docs.pytest.org/en/latest/changelog.html
|
||||
|
||||
For complete documentation, please visit:
|
||||
|
||||
https://docs.pytest.org/en/latest/
|
||||
|
||||
As usual, you can upgrade from pypi via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* ApaDoctor
|
||||
* Bernhard M. Wiedemann
|
||||
* Brian Skinn
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Gary Tyler
|
||||
* Jeong YunWon
|
||||
* Miro Hrončok
|
||||
* Takafumi Arakaki
|
||||
* henrykironde
|
||||
* smheidrich
|
||||
|
||||
|
||||
Happy testing,
|
||||
The Pytest Development Team
|
||||
20
doc/en/announce/release-4.4.1.rst
Normal file
20
doc/en/announce/release-4.4.1.rst
Normal file
@@ -0,0 +1,20 @@
|
||||
pytest-4.4.1
|
||||
=======================================
|
||||
|
||||
pytest 4.4.1 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
33
doc/en/announce/release-4.4.2.rst
Normal file
33
doc/en/announce/release-4.4.2.rst
Normal file
@@ -0,0 +1,33 @@
|
||||
pytest-4.4.2
|
||||
=======================================
|
||||
|
||||
pytest 4.4.2 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Allan Lewis
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* DamianSkrzypczak
|
||||
* Daniel Hahler
|
||||
* Don Kirkby
|
||||
* Douglas Thor
|
||||
* Hugo
|
||||
* Ilya Konstantinov
|
||||
* Jon Dufresne
|
||||
* Matt Cooper
|
||||
* Nikolay Kondratyev
|
||||
* Ondřej Súkup
|
||||
* Peter Schutt
|
||||
* Romain Chossart
|
||||
* Sitaktif
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
34
doc/en/announce/release-4.5.0.rst
Normal file
34
doc/en/announce/release-4.5.0.rst
Normal file
@@ -0,0 +1,34 @@
|
||||
pytest-4.5.0
|
||||
=======================================
|
||||
|
||||
The pytest team is proud to announce the 4.5.0 release!
|
||||
|
||||
pytest is a mature Python testing tool with more than a 2000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
This release contains a number of bugs fixes and improvements, so users are encouraged
|
||||
to take a look at the CHANGELOG:
|
||||
|
||||
https://docs.pytest.org/en/latest/changelog.html
|
||||
|
||||
For complete documentation, please visit:
|
||||
|
||||
https://docs.pytest.org/en/latest/
|
||||
|
||||
As usual, you can upgrade from pypi via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Floris Bruynooghe
|
||||
* Pulkit Goyal
|
||||
* Samuel Searles-Bryant
|
||||
* Zac Hatfield-Dodds
|
||||
|
||||
|
||||
Happy testing,
|
||||
The Pytest Development Team
|
||||
43
doc/en/announce/release-4.6.0.rst
Normal file
43
doc/en/announce/release-4.6.0.rst
Normal file
@@ -0,0 +1,43 @@
|
||||
pytest-4.6.0
|
||||
=======================================
|
||||
|
||||
The pytest team is proud to announce the 4.6.0 release!
|
||||
|
||||
pytest is a mature Python testing tool with more than a 2000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
This release contains a number of bugs fixes and improvements, so users are encouraged
|
||||
to take a look at the CHANGELOG:
|
||||
|
||||
https://docs.pytest.org/en/latest/changelog.html
|
||||
|
||||
For complete documentation, please visit:
|
||||
|
||||
https://docs.pytest.org/en/latest/
|
||||
|
||||
As usual, you can upgrade from pypi via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Akiomi Kamakura
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* David Röthlisberger
|
||||
* Evan Kepner
|
||||
* Jeffrey Rackauckas
|
||||
* MyComputer
|
||||
* Nikita Krokosh
|
||||
* Raul Tambre
|
||||
* Thomas Hisch
|
||||
* Tim Hoffmann
|
||||
* Tomer Keren
|
||||
* Victor Maryama
|
||||
* danielx123
|
||||
* oleg-yegorov
|
||||
|
||||
|
||||
Happy testing,
|
||||
The Pytest Development Team
|
||||
19
doc/en/announce/release-4.6.1.rst
Normal file
19
doc/en/announce/release-4.6.1.rst
Normal file
@@ -0,0 +1,19 @@
|
||||
pytest-4.6.1
|
||||
=======================================
|
||||
|
||||
pytest 4.6.1 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
18
doc/en/announce/release-4.6.2.rst
Normal file
18
doc/en/announce/release-4.6.2.rst
Normal file
@@ -0,0 +1,18 @@
|
||||
pytest-4.6.2
|
||||
=======================================
|
||||
|
||||
pytest 4.6.2 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
21
doc/en/announce/release-4.6.3.rst
Normal file
21
doc/en/announce/release-4.6.3.rst
Normal file
@@ -0,0 +1,21 @@
|
||||
pytest-4.6.3
|
||||
=======================================
|
||||
|
||||
pytest 4.6.3 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Dirk Thomas
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
22
doc/en/announce/release-4.6.4.rst
Normal file
22
doc/en/announce/release-4.6.4.rst
Normal file
@@ -0,0 +1,22 @@
|
||||
pytest-4.6.4
|
||||
=======================================
|
||||
|
||||
pytest 4.6.4 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Thomas Grainger
|
||||
* Zac Hatfield-Dodds
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
21
doc/en/announce/release-4.6.5.rst
Normal file
21
doc/en/announce/release-4.6.5.rst
Normal file
@@ -0,0 +1,21 @@
|
||||
pytest-4.6.5
|
||||
=======================================
|
||||
|
||||
pytest 4.6.5 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Thomas Grainger
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
46
doc/en/announce/release-5.0.0.rst
Normal file
46
doc/en/announce/release-5.0.0.rst
Normal file
@@ -0,0 +1,46 @@
|
||||
pytest-5.0.0
|
||||
=======================================
|
||||
|
||||
The pytest team is proud to announce the 5.0.0 release!
|
||||
|
||||
pytest is a mature Python testing tool with more than a 2000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
This release contains a number of bugs fixes and improvements, so users are encouraged
|
||||
to take a look at the CHANGELOG:
|
||||
|
||||
https://docs.pytest.org/en/latest/changelog.html
|
||||
|
||||
For complete documentation, please visit:
|
||||
|
||||
https://docs.pytest.org/en/latest/
|
||||
|
||||
As usual, you can upgrade from pypi via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Dirk Thomas
|
||||
* Evan Kepner
|
||||
* Florian Bruhin
|
||||
* Hugo
|
||||
* Kevin J. Foley
|
||||
* Pulkit Goyal
|
||||
* Ralph Giles
|
||||
* Ronny Pfannschmidt
|
||||
* Thomas Grainger
|
||||
* Thomas Hisch
|
||||
* Tim Gates
|
||||
* Victor Maryama
|
||||
* Yuri Apollov
|
||||
* Zac Hatfield-Dodds
|
||||
* curiousjazz77
|
||||
* patriksevallius
|
||||
|
||||
|
||||
Happy testing,
|
||||
The Pytest Development Team
|
||||
25
doc/en/announce/release-5.0.1.rst
Normal file
25
doc/en/announce/release-5.0.1.rst
Normal file
@@ -0,0 +1,25 @@
|
||||
pytest-5.0.1
|
||||
=======================================
|
||||
|
||||
pytest 5.0.1 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* AmirElkess
|
||||
* Andreu Vallbona Plazas
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Florian Bruhin
|
||||
* Michael Moore
|
||||
* Niklas Meinzer
|
||||
* Thomas Grainger
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
56
doc/en/announce/release-5.1.0.rst
Normal file
56
doc/en/announce/release-5.1.0.rst
Normal file
@@ -0,0 +1,56 @@
|
||||
pytest-5.1.0
|
||||
=======================================
|
||||
|
||||
The pytest team is proud to announce the 5.1.0 release!
|
||||
|
||||
pytest is a mature Python testing tool with more than a 2000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
This release contains a number of bugs fixes and improvements, so users are encouraged
|
||||
to take a look at the CHANGELOG:
|
||||
|
||||
https://docs.pytest.org/en/latest/changelog.html
|
||||
|
||||
For complete documentation, please visit:
|
||||
|
||||
https://docs.pytest.org/en/latest/
|
||||
|
||||
As usual, you can upgrade from pypi via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Albert Tugushev
|
||||
* Alexey Zankevich
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* David Röthlisberger
|
||||
* Florian Bruhin
|
||||
* Ilya Stepin
|
||||
* Jon Dufresne
|
||||
* Kaiqi
|
||||
* Max R
|
||||
* Miro Hrončok
|
||||
* Oliver Bestwalter
|
||||
* Ran Benita
|
||||
* Ronny Pfannschmidt
|
||||
* Samuel Searles-Bryant
|
||||
* Semen Zhydenko
|
||||
* Steffen Schroeder
|
||||
* Thomas Grainger
|
||||
* Tim Hoffmann
|
||||
* William Woodall
|
||||
* Wojtek Erbetowski
|
||||
* Xixi Zhao
|
||||
* Yash Todi
|
||||
* boris
|
||||
* dmitry.dygalo
|
||||
* helloocc
|
||||
* martbln
|
||||
* mei-li
|
||||
|
||||
|
||||
Happy testing,
|
||||
The Pytest Development Team
|
||||
24
doc/en/announce/release-5.1.1.rst
Normal file
24
doc/en/announce/release-5.1.1.rst
Normal file
@@ -0,0 +1,24 @@
|
||||
pytest-5.1.1
|
||||
=======================================
|
||||
|
||||
pytest 5.1.1 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Florian Bruhin
|
||||
* Hugo van Kemenade
|
||||
* Ran Benita
|
||||
* Ronny Pfannschmidt
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
23
doc/en/announce/release-5.1.2.rst
Normal file
23
doc/en/announce/release-5.1.2.rst
Normal file
@@ -0,0 +1,23 @@
|
||||
pytest-5.1.2
|
||||
=======================================
|
||||
|
||||
pytest 5.1.2 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Andrzej Klajnert
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Christian Neumüller
|
||||
* Robert Holt
|
||||
* linchiwei123
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
@@ -12,12 +12,15 @@ Asserting with the ``assert`` statement
|
||||
|
||||
``pytest`` allows you to use the standard python ``assert`` for verifying
|
||||
expectations and values in Python tests. For example, you can write the
|
||||
following::
|
||||
following:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_assert1.py
|
||||
def f():
|
||||
return 3
|
||||
|
||||
|
||||
def test_function():
|
||||
assert f() == 4
|
||||
|
||||
@@ -28,8 +31,9 @@ you will see the return value of the function call:
|
||||
|
||||
$ pytest test_assert1.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 1 item
|
||||
|
||||
test_assert1.py F [100%]
|
||||
@@ -42,8 +46,8 @@ you will see the return value of the function call:
|
||||
E assert 3 == 4
|
||||
E + where 3 = f()
|
||||
|
||||
test_assert1.py:5: AssertionError
|
||||
========================= 1 failed in 0.12 seconds =========================
|
||||
test_assert1.py:6: AssertionError
|
||||
============================ 1 failed in 0.12s =============================
|
||||
|
||||
``pytest`` has support for showing the values of the most common subexpressions
|
||||
including calls, attributes, comparisons, and binary and unary
|
||||
@@ -51,7 +55,9 @@ operators. (See :ref:`tbreportdemo`). This allows you to use the
|
||||
idiomatic python constructs without boilerplate code while not losing
|
||||
introspection information.
|
||||
|
||||
However, if you specify a message with the assertion like this::
|
||||
However, if you specify a message with the assertion like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
assert a % 2 == 0, "value was odd, should be even"
|
||||
|
||||
@@ -66,50 +72,71 @@ Assertions about expected exceptions
|
||||
------------------------------------------
|
||||
|
||||
In order to write assertions about raised exceptions, you can use
|
||||
``pytest.raises`` as a context manager like this::
|
||||
``pytest.raises`` as a context manager like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def test_zero_division():
|
||||
with pytest.raises(ZeroDivisionError):
|
||||
1 / 0
|
||||
|
||||
and if you need to have access to the actual exception info you may use::
|
||||
and if you need to have access to the actual exception info you may use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_recursion_depth():
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
|
||||
def f():
|
||||
f()
|
||||
|
||||
f()
|
||||
assert 'maximum recursion' in str(excinfo.value)
|
||||
assert "maximum recursion" in str(excinfo.value)
|
||||
|
||||
``excinfo`` is a ``ExceptionInfo`` instance, which is a wrapper around
|
||||
the actual exception raised. The main attributes of interest are
|
||||
``.type``, ``.value`` and ``.traceback``.
|
||||
|
||||
.. versionchanged:: 3.0
|
||||
You can pass a ``match`` keyword parameter to the context-manager to test
|
||||
that a regular expression matches on the string representation of an exception
|
||||
(similar to the ``TestCase.assertRaisesRegexp`` method from ``unittest``):
|
||||
|
||||
In the context manager form you may use the keyword argument
|
||||
``message`` to specify a custom failure message::
|
||||
.. code-block:: python
|
||||
|
||||
>>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"):
|
||||
... pass
|
||||
... Failed: Expecting ZeroDivisionError
|
||||
import pytest
|
||||
|
||||
If you want to write test code that works on Python 2.4 as well,
|
||||
you may also use two other ways to test for an expected exception::
|
||||
|
||||
def myfunc():
|
||||
raise ValueError("Exception 123 raised")
|
||||
|
||||
|
||||
def test_match():
|
||||
with pytest.raises(ValueError, match=r".* 123 .*"):
|
||||
myfunc()
|
||||
|
||||
The regexp parameter of the ``match`` method is matched with the ``re.search``
|
||||
function, so in the above example ``match='123'`` would have worked as
|
||||
well.
|
||||
|
||||
There's an alternate form of the ``pytest.raises`` function where you pass
|
||||
a function that will be executed with the given ``*args`` and ``**kwargs`` and
|
||||
assert that the given exception is raised:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pytest.raises(ExpectedException, func, *args, **kwargs)
|
||||
pytest.raises(ExpectedException, "func(*args, **kwargs)")
|
||||
|
||||
both of which execute the specified function with args and kwargs and
|
||||
asserts that the given ``ExpectedException`` is raised. The reporter will
|
||||
provide you with helpful output in case of failures such as *no
|
||||
The reporter will provide you with helpful output in case of failures such as *no
|
||||
exception* or *wrong exception*.
|
||||
|
||||
Note that it is also possible to specify a "raises" argument to
|
||||
``pytest.mark.xfail``, which checks that the test is failing in a more
|
||||
specific way than just having any exception raised::
|
||||
specific way than just having any exception raised:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.xfail(raises=IndexError)
|
||||
def test_f():
|
||||
@@ -121,30 +148,13 @@ exceptions your own code is deliberately raising, whereas using
|
||||
like documenting unfixed bugs (where the test describes what "should" happen)
|
||||
or bugs in dependencies.
|
||||
|
||||
Also, the context manager form accepts a ``match`` keyword parameter to test
|
||||
that a regular expression matches on the string representation of an exception
|
||||
(like the ``TestCase.assertRaisesRegexp`` method from ``unittest``)::
|
||||
|
||||
import pytest
|
||||
|
||||
def myfunc():
|
||||
raise ValueError("Exception 123 raised")
|
||||
|
||||
def test_match():
|
||||
with pytest.raises(ValueError, match=r'.* 123 .*'):
|
||||
myfunc()
|
||||
|
||||
The regexp parameter of the ``match`` method is matched with the ``re.search``
|
||||
function. So in the above example ``match='123'`` would have worked as
|
||||
well.
|
||||
|
||||
|
||||
.. _`assertwarns`:
|
||||
|
||||
Assertions about expected warnings
|
||||
-----------------------------------------
|
||||
|
||||
.. versionadded:: 2.8
|
||||
|
||||
|
||||
You can check that code raises a particular warning using
|
||||
:ref:`pytest.warns <warns>`.
|
||||
@@ -155,13 +165,16 @@ You can check that code raises a particular warning using
|
||||
Making use of context-sensitive comparisons
|
||||
-------------------------------------------------
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
|
||||
``pytest`` has rich support for providing context-sensitive information
|
||||
when it encounters comparisons. For example::
|
||||
when it encounters comparisons. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_assert2.py
|
||||
|
||||
|
||||
def test_set_comparison():
|
||||
set1 = set("1308")
|
||||
set2 = set("8035")
|
||||
@@ -173,8 +186,9 @@ if you run this module:
|
||||
|
||||
$ pytest test_assert2.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 1 item
|
||||
|
||||
test_assert2.py F [100%]
|
||||
@@ -193,8 +207,8 @@ if you run this module:
|
||||
E '5'
|
||||
E Use -v to get the full diff
|
||||
|
||||
test_assert2.py:5: AssertionError
|
||||
========================= 1 failed in 0.12 seconds =========================
|
||||
test_assert2.py:6: AssertionError
|
||||
============================ 1 failed in 0.12s =============================
|
||||
|
||||
Special comparisons are done for a number of cases:
|
||||
|
||||
@@ -204,8 +218,8 @@ Special comparisons are done for a number of cases:
|
||||
|
||||
See the :ref:`reporting demo <tbreportdemo>` for many more examples.
|
||||
|
||||
Defining your own assertion comparison
|
||||
----------------------------------------------
|
||||
Defining your own explanation for failed assertions
|
||||
---------------------------------------------------
|
||||
|
||||
It is possible to add your own detailed explanations by implementing
|
||||
the ``pytest_assertrepr_compare`` hook.
|
||||
@@ -214,25 +228,34 @@ the ``pytest_assertrepr_compare`` hook.
|
||||
:noindex:
|
||||
|
||||
As an example consider adding the following hook in a :ref:`conftest.py <conftest.py>`
|
||||
file which provides an alternative explanation for ``Foo`` objects::
|
||||
file which provides an alternative explanation for ``Foo`` objects:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
from test_foocompare import Foo
|
||||
|
||||
|
||||
def pytest_assertrepr_compare(op, left, right):
|
||||
if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
|
||||
return ['Comparing Foo instances:',
|
||||
' vals: %s != %s' % (left.val, right.val)]
|
||||
return [
|
||||
"Comparing Foo instances:",
|
||||
" vals: {} != {}".format(left.val, right.val),
|
||||
]
|
||||
|
||||
now, given this test module::
|
||||
now, given this test module:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_foocompare.py
|
||||
class Foo(object):
|
||||
class Foo:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.val == other.val
|
||||
|
||||
|
||||
def test_compare():
|
||||
f1 = Foo(1)
|
||||
f2 = Foo(2)
|
||||
@@ -255,16 +278,16 @@ the conftest file:
|
||||
E assert Comparing Foo instances:
|
||||
E vals: 1 != 2
|
||||
|
||||
test_foocompare.py:11: AssertionError
|
||||
1 failed in 0.12 seconds
|
||||
test_foocompare.py:12: AssertionError
|
||||
1 failed in 0.02s
|
||||
|
||||
.. _assert-details:
|
||||
.. _`assert introspection`:
|
||||
|
||||
Advanced assertion introspection
|
||||
----------------------------------
|
||||
Assertion introspection details
|
||||
-------------------------------
|
||||
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
|
||||
Reporting details about a failing assertion is achieved by rewriting assert
|
||||
@@ -275,35 +298,53 @@ supporting modules which are not themselves test modules will not be rewritten**
|
||||
|
||||
You can manually enable assertion rewriting for an imported module by calling
|
||||
`register_assert_rewrite <https://docs.pytest.org/en/latest/writing_plugins.html#assertion-rewriting>`_
|
||||
before you import it (a good place to do that is in ``conftest.py``).
|
||||
|
||||
.. note::
|
||||
|
||||
``pytest`` rewrites test modules on import by using an import
|
||||
hook to write new ``pyc`` files. Most of the time this works transparently.
|
||||
However, if you are messing with import yourself, the import hook may
|
||||
interfere.
|
||||
|
||||
If this is the case you have two options:
|
||||
|
||||
* Disable rewriting for a specific module by adding the string
|
||||
``PYTEST_DONT_REWRITE`` to its docstring.
|
||||
|
||||
* Disable rewriting for all modules by using ``--assert=plain``.
|
||||
|
||||
Additionally, rewriting will fail silently if it cannot write new ``.pyc`` files,
|
||||
i.e. in a read-only filesystem or a zipfile.
|
||||
|
||||
before you import it (a good place to do that is in your root ``conftest.py``).
|
||||
|
||||
For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
Assertion rewriting caches files on disk
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``pytest`` will write back the rewritten modules to disk for caching. You can disable
|
||||
this behavior (for example to avoid leaving stale ``.pyc`` files around in projects that
|
||||
move files around a lot) by adding this to the top of your ``conftest.py`` file:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import sys
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
Note that you still get the benefits of assertion introspection, the only change is that
|
||||
the ``.pyc`` files won't be cached on disk.
|
||||
|
||||
Additionally, rewriting will silently skip caching if it cannot write new ``.pyc`` files,
|
||||
i.e. in a read-only filesystem or a zipfile.
|
||||
|
||||
|
||||
Disabling assert rewriting
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``pytest`` rewrites test modules on import by using an import
|
||||
hook to write new ``pyc`` files. Most of the time this works transparently.
|
||||
However, if you are working with the import machinery yourself, the import hook may
|
||||
interfere.
|
||||
|
||||
If this is the case you have two options:
|
||||
|
||||
* Disable rewriting for a specific module by adding the string
|
||||
``PYTEST_DONT_REWRITE`` to its docstring.
|
||||
|
||||
* Disable rewriting for all modules by using ``--assert=plain``.
|
||||
|
||||
|
||||
|
||||
Add assert rewriting as an alternate introspection technique.
|
||||
|
||||
.. versionchanged:: 2.1
|
||||
|
||||
Introduce the ``--assert`` option. Deprecate ``--no-assert`` and
|
||||
``--nomagic``.
|
||||
|
||||
.. versionchanged:: 3.0
|
||||
|
||||
Removes the ``--no-assert`` and ``--nomagic`` options.
|
||||
Removes the ``--assert=reinterp`` option.
|
||||
|
||||
@@ -8,18 +8,26 @@ When using bash as your shell, ``pytest`` can use argcomplete
|
||||
(https://argcomplete.readthedocs.io/) for auto-completion.
|
||||
For this ``argcomplete`` needs to be installed **and** enabled.
|
||||
|
||||
Install argcomplete using::
|
||||
Install argcomplete using:
|
||||
|
||||
sudo pip install 'argcomplete>=0.5.7'
|
||||
.. code-block:: bash
|
||||
|
||||
For global activation of all argcomplete enabled python applications run::
|
||||
sudo pip install 'argcomplete>=0.5.7'
|
||||
|
||||
For global activation of all argcomplete enabled python applications run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo activate-global-python-argcomplete
|
||||
|
||||
For permanent (but not global) ``pytest`` activation, use::
|
||||
For permanent (but not global) ``pytest`` activation, use:
|
||||
|
||||
register-python-argcomplete pytest >> ~/.bashrc
|
||||
.. code-block:: bash
|
||||
|
||||
For one-time activation of argcomplete for ``pytest`` only, use::
|
||||
register-python-argcomplete pytest >> ~/.bashrc
|
||||
|
||||
eval "$(register-python-argcomplete pytest)"
|
||||
For one-time activation of argcomplete for ``pytest`` only, use:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
eval "$(register-python-argcomplete pytest)"
|
||||
|
||||
@@ -27,36 +27,47 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||
name of your plugin or application to avoid clashes with other cache users.
|
||||
|
||||
Values can be any object handled by the json stdlib module.
|
||||
|
||||
capsys
|
||||
Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make
|
||||
captured output available via ``capsys.readouterr()`` method calls
|
||||
which return a ``(out, err)`` namedtuple. ``out`` and ``err`` will be ``text``
|
||||
objects.
|
||||
Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
|
||||
|
||||
The captured output is made available via ``capsys.readouterr()`` method
|
||||
calls, which return a ``(out, err)`` namedtuple.
|
||||
``out`` and ``err`` will be ``text`` objects.
|
||||
|
||||
capsysbinary
|
||||
Enable capturing of writes to ``sys.stdout`` and ``sys.stderr`` and make
|
||||
captured output available via ``capsys.readouterr()`` method calls
|
||||
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``bytes``
|
||||
objects.
|
||||
Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
|
||||
|
||||
The captured output is made available via ``capsysbinary.readouterr()``
|
||||
method calls, which return a ``(out, err)`` namedtuple.
|
||||
``out`` and ``err`` will be ``bytes`` objects.
|
||||
|
||||
capfd
|
||||
Enable capturing of writes to file descriptors ``1`` and ``2`` and make
|
||||
captured output available via ``capfd.readouterr()`` method calls
|
||||
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``text``
|
||||
objects.
|
||||
Enable text capturing of writes to file descriptors ``1`` and ``2``.
|
||||
|
||||
The captured output is made available via ``capfd.readouterr()`` method
|
||||
calls, which return a ``(out, err)`` namedtuple.
|
||||
``out`` and ``err`` will be ``text`` objects.
|
||||
|
||||
capfdbinary
|
||||
Enable capturing of write to file descriptors 1 and 2 and make
|
||||
captured output available via ``capfdbinary.readouterr`` method calls
|
||||
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be
|
||||
``bytes`` objects.
|
||||
doctest_namespace
|
||||
Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
|
||||
|
||||
The captured output is made available via ``capfd.readouterr()`` method
|
||||
calls, which return a ``(out, err)`` namedtuple.
|
||||
``out`` and ``err`` will be ``byte`` objects.
|
||||
|
||||
doctest_namespace [session scope]
|
||||
Fixture that returns a :py:class:`dict` that will be injected into the namespace of doctests.
|
||||
pytestconfig
|
||||
|
||||
pytestconfig [session scope]
|
||||
Session-scoped fixture that returns the :class:`_pytest.config.Config` object.
|
||||
|
||||
Example::
|
||||
|
||||
def test_foo(pytestconfig):
|
||||
if pytestconfig.getoption("verbose"):
|
||||
if pytestconfig.getoption("verbose") > 0:
|
||||
...
|
||||
|
||||
record_property
|
||||
Add an extra properties the calling test.
|
||||
User properties become part of the test report and are available to the
|
||||
@@ -68,12 +79,26 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||
|
||||
def test_function(record_property):
|
||||
record_property("example_key", 1)
|
||||
record_xml_property
|
||||
(Deprecated) use record_property.
|
||||
|
||||
record_xml_attribute
|
||||
Add extra xml attributes to the tag for the calling test.
|
||||
The fixture is callable with ``(name, value)``, with value being
|
||||
automatically xml-encoded
|
||||
|
||||
record_testsuite_property [session scope]
|
||||
Records a new ``<property>`` tag as child of the root ``<testsuite>``. This is suitable to
|
||||
writing global information regarding the entire test suite, and is compatible with ``xunit2`` JUnit family.
|
||||
|
||||
This is a ``session``-scoped fixture which is called with ``(name, value)``. Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_foo(record_testsuite_property):
|
||||
record_testsuite_property("ARCH", "PPC")
|
||||
record_testsuite_property("STORAGE_TYPE", "CEPH")
|
||||
|
||||
``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped.
|
||||
|
||||
caplog
|
||||
Access and control log capturing.
|
||||
|
||||
@@ -83,6 +108,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||
* caplog.records -> list of logging.LogRecord instances
|
||||
* caplog.record_tuples -> list of (logger_name, level, message) tuples
|
||||
* caplog.clear() -> clear captured records and formatted log output string
|
||||
|
||||
monkeypatch
|
||||
The returned ``monkeypatch`` fixture provides these
|
||||
helper methods to modify objects, dictionaries or os.environ::
|
||||
@@ -100,15 +126,19 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||
test function or fixture has finished. The ``raising``
|
||||
parameter determines if a KeyError or AttributeError
|
||||
will be raised if the set/deletion operation has no target.
|
||||
|
||||
recwarn
|
||||
Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
|
||||
|
||||
See http://docs.python.org/library/warnings.html for information
|
||||
on warning categories.
|
||||
tmpdir_factory
|
||||
|
||||
tmpdir_factory [session scope]
|
||||
Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session.
|
||||
tmp_path_factory
|
||||
|
||||
tmp_path_factory [session scope]
|
||||
Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session.
|
||||
|
||||
tmpdir
|
||||
Return a temporary directory path object
|
||||
which is unique to each test function invocation,
|
||||
@@ -117,6 +147,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||
path object.
|
||||
|
||||
.. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html
|
||||
|
||||
tmp_path
|
||||
Return a temporary directory path object
|
||||
which is unique to each test function invocation,
|
||||
@@ -128,9 +159,13 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||
|
||||
in python < 3.6 this is a pathlib2.Path
|
||||
|
||||
no tests ran in 0.12 seconds
|
||||
|
||||
You can also interactively ask for help, e.g. by typing on the Python interactive prompt something like::
|
||||
no tests ran in 0.00s
|
||||
|
||||
You can also interactively ask for help, e.g. by typing on the Python interactive prompt something like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import pytest
|
||||
|
||||
help(pytest)
|
||||
|
||||
143
doc/en/cache.rst
143
doc/en/cache.rst
@@ -5,7 +5,7 @@
|
||||
Cache: working with cross-testrun state
|
||||
=======================================
|
||||
|
||||
.. versionadded:: 2.8
|
||||
|
||||
|
||||
Usage
|
||||
---------
|
||||
@@ -33,15 +33,18 @@ Other plugins may access the `config.cache`_ object to set/get
|
||||
Rerunning only failures or failures first
|
||||
-----------------------------------------------
|
||||
|
||||
First, let's create 50 test invocation of which only 2 fail::
|
||||
First, let's create 50 test invocation of which only 2 fail:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_50.py
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize("i", range(50))
|
||||
def test_num(i):
|
||||
if i in (17, 25):
|
||||
pytest.fail("bad luck")
|
||||
pytest.fail("bad luck")
|
||||
|
||||
If you run this for the first time you will see two failures:
|
||||
|
||||
@@ -57,10 +60,10 @@ If you run this for the first time you will see two failures:
|
||||
@pytest.mark.parametrize("i", range(50))
|
||||
def test_num(i):
|
||||
if i in (17, 25):
|
||||
> pytest.fail("bad luck")
|
||||
E Failed: bad luck
|
||||
> pytest.fail("bad luck")
|
||||
E Failed: bad luck
|
||||
|
||||
test_50.py:6: Failed
|
||||
test_50.py:7: Failed
|
||||
_______________________________ test_num[25] _______________________________
|
||||
|
||||
i = 25
|
||||
@@ -68,11 +71,11 @@ If you run this for the first time you will see two failures:
|
||||
@pytest.mark.parametrize("i", range(50))
|
||||
def test_num(i):
|
||||
if i in (17, 25):
|
||||
> pytest.fail("bad luck")
|
||||
E Failed: bad luck
|
||||
> pytest.fail("bad luck")
|
||||
E Failed: bad luck
|
||||
|
||||
test_50.py:6: Failed
|
||||
2 failed, 48 passed in 0.12 seconds
|
||||
test_50.py:7: Failed
|
||||
2 failed, 48 passed in 0.07s
|
||||
|
||||
If you then run it with ``--lf``:
|
||||
|
||||
@@ -80,9 +83,10 @@ If you then run it with ``--lf``:
|
||||
|
||||
$ pytest --lf
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 50 items / 48 deselected
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 50 items / 48 deselected / 2 selected
|
||||
run-last-failure: rerun previous 2 failures
|
||||
|
||||
test_50.py FF [100%]
|
||||
@@ -95,10 +99,10 @@ If you then run it with ``--lf``:
|
||||
@pytest.mark.parametrize("i", range(50))
|
||||
def test_num(i):
|
||||
if i in (17, 25):
|
||||
> pytest.fail("bad luck")
|
||||
E Failed: bad luck
|
||||
> pytest.fail("bad luck")
|
||||
E Failed: bad luck
|
||||
|
||||
test_50.py:6: Failed
|
||||
test_50.py:7: Failed
|
||||
_______________________________ test_num[25] _______________________________
|
||||
|
||||
i = 25
|
||||
@@ -106,14 +110,14 @@ If you then run it with ``--lf``:
|
||||
@pytest.mark.parametrize("i", range(50))
|
||||
def test_num(i):
|
||||
if i in (17, 25):
|
||||
> pytest.fail("bad luck")
|
||||
E Failed: bad luck
|
||||
> pytest.fail("bad luck")
|
||||
E Failed: bad luck
|
||||
|
||||
test_50.py:6: Failed
|
||||
================= 2 failed, 48 deselected in 0.12 seconds ==================
|
||||
test_50.py:7: Failed
|
||||
===================== 2 failed, 48 deselected in 0.12s =====================
|
||||
|
||||
You have run only the two failing test from the last run, while 48 tests have
|
||||
not been run ("deselected").
|
||||
You have run only the two failing tests from the last run, while the 48 passing
|
||||
tests have not been run ("deselected").
|
||||
|
||||
Now, if you run with the ``--ff`` option, all tests will be run but the first
|
||||
previous failures will be executed first (as can be seen from the series
|
||||
@@ -123,8 +127,9 @@ of ``FF`` and dots):
|
||||
|
||||
$ pytest --ff
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 50 items
|
||||
run-last-failure: rerun previous 2 failures first
|
||||
|
||||
@@ -138,10 +143,10 @@ of ``FF`` and dots):
|
||||
@pytest.mark.parametrize("i", range(50))
|
||||
def test_num(i):
|
||||
if i in (17, 25):
|
||||
> pytest.fail("bad luck")
|
||||
E Failed: bad luck
|
||||
> pytest.fail("bad luck")
|
||||
E Failed: bad luck
|
||||
|
||||
test_50.py:6: Failed
|
||||
test_50.py:7: Failed
|
||||
_______________________________ test_num[25] _______________________________
|
||||
|
||||
i = 25
|
||||
@@ -149,11 +154,11 @@ of ``FF`` and dots):
|
||||
@pytest.mark.parametrize("i", range(50))
|
||||
def test_num(i):
|
||||
if i in (17, 25):
|
||||
> pytest.fail("bad luck")
|
||||
E Failed: bad luck
|
||||
> pytest.fail("bad luck")
|
||||
E Failed: bad luck
|
||||
|
||||
test_50.py:6: Failed
|
||||
=================== 2 failed, 48 passed in 0.12 seconds ====================
|
||||
test_50.py:7: Failed
|
||||
======================= 2 failed, 48 passed in 0.12s =======================
|
||||
|
||||
.. _`config.cache`:
|
||||
|
||||
@@ -166,7 +171,9 @@ Behavior when no tests failed in the last run
|
||||
|
||||
When no tests failed in the last run, or when no cached ``lastfailed`` data was
|
||||
found, ``pytest`` can be configured either to run all of the tests or no tests,
|
||||
using the ``--last-failed-no-failures`` option, which takes one of the following values::
|
||||
using the ``--last-failed-no-failures`` option, which takes one of the following values:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --last-failed --last-failed-no-failures all # run all tests (default behavior)
|
||||
pytest --last-failed --last-failed-no-failures none # run no tests and exit
|
||||
@@ -179,26 +186,33 @@ The new config.cache object
|
||||
Plugins or conftest.py support code can get a cached value using the
|
||||
pytest ``config`` object. Here is a basic example plugin which
|
||||
implements a :ref:`fixture` which re-uses previously created state
|
||||
across pytest invocations::
|
||||
across pytest invocations:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_caching.py
|
||||
import pytest
|
||||
import time
|
||||
|
||||
|
||||
def expensive_computation():
|
||||
print("running expensive computation...")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mydata(request):
|
||||
val = request.config.cache.get("example/value", None)
|
||||
if val is None:
|
||||
time.sleep(9*0.6) # expensive computation :)
|
||||
expensive_computation()
|
||||
val = 42
|
||||
request.config.cache.set("example/value", val)
|
||||
return val
|
||||
|
||||
|
||||
def test_function(mydata):
|
||||
assert mydata == 23
|
||||
|
||||
If you run this command once, it will take a while because
|
||||
of the sleep:
|
||||
If you run this command for the first time, you can see the print statement:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
@@ -213,11 +227,13 @@ of the sleep:
|
||||
> assert mydata == 23
|
||||
E assert 42 == 23
|
||||
|
||||
test_caching.py:14: AssertionError
|
||||
1 failed in 0.12 seconds
|
||||
test_caching.py:20: AssertionError
|
||||
-------------------------- Captured stdout setup ---------------------------
|
||||
running expensive computation...
|
||||
1 failed in 0.02s
|
||||
|
||||
If you run it a second time the value will be retrieved from
|
||||
the cache and this will be quick:
|
||||
If you run it a second time, the value will be retrieved from
|
||||
the cache and nothing will be printed:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
@@ -232,14 +248,14 @@ the cache and this will be quick:
|
||||
> assert mydata == 23
|
||||
E assert 42 == 23
|
||||
|
||||
test_caching.py:14: AssertionError
|
||||
1 failed in 0.12 seconds
|
||||
test_caching.py:20: AssertionError
|
||||
1 failed in 0.02s
|
||||
|
||||
See the :ref:`cache-api` for more details.
|
||||
|
||||
|
||||
Inspecting Cache content
|
||||
-------------------------------
|
||||
------------------------
|
||||
|
||||
You can always peek at the content of the cache using the
|
||||
``--cache-show`` command line option:
|
||||
@@ -248,12 +264,18 @@ You can always peek at the content of the cache using the
|
||||
|
||||
$ pytest --cache-show
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
cachedir: $REGENDOC_TMPDIR/.pytest_cache
|
||||
------------------------------- cache values -------------------------------
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
--------------------------- cache values for '*' ---------------------------
|
||||
cache/lastfailed contains:
|
||||
{'test_caching.py::test_function': True}
|
||||
{'test_50.py::test_num[17]': True,
|
||||
'test_50.py::test_num[25]': True,
|
||||
'test_assert1.py::test_function': True,
|
||||
'test_assert2.py::test_set_comparison': True,
|
||||
'test_caching.py::test_function': True,
|
||||
'test_foocompare.py::test_compare': True}
|
||||
cache/nodeids contains:
|
||||
['test_caching.py::test_function']
|
||||
cache/stepwise contains:
|
||||
@@ -261,13 +283,32 @@ You can always peek at the content of the cache using the
|
||||
example/value contains:
|
||||
42
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
``--cache-show`` takes an optional argument to specify a glob pattern for
|
||||
filtering:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
$ pytest --cache-show example/*
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
----------------------- cache values for 'example/*' -----------------------
|
||||
example/value contains:
|
||||
42
|
||||
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
Clearing Cache content
|
||||
-------------------------------
|
||||
----------------------
|
||||
|
||||
You can instruct pytest to clear all cache files and values
|
||||
by adding the ``--cache-clear`` option like this::
|
||||
by adding the ``--cache-clear`` option like this:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --cache-clear
|
||||
|
||||
|
||||
@@ -35,7 +35,9 @@ There are two ways in which ``pytest`` can perform capturing:
|
||||
|
||||
.. _`disable capturing`:
|
||||
|
||||
You can influence output capturing mechanisms from the command line::
|
||||
You can influence output capturing mechanisms from the command line:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest -s # disable all capturing
|
||||
pytest --capture=sys # replace sys.stdout/stderr with in-mem files
|
||||
@@ -47,16 +49,21 @@ Using print statements for debugging
|
||||
---------------------------------------------------
|
||||
|
||||
One primary benefit of the default capturing of stdout/stderr output
|
||||
is that you can use print statements for debugging::
|
||||
is that you can use print statements for debugging:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_module.py
|
||||
|
||||
|
||||
def setup_function(function):
|
||||
print("setting up %s" % function)
|
||||
print("setting up", function)
|
||||
|
||||
|
||||
def test_func1():
|
||||
assert True
|
||||
|
||||
|
||||
def test_func2():
|
||||
assert False
|
||||
|
||||
@@ -67,8 +74,9 @@ of the failing function and hide the other one:
|
||||
|
||||
$ pytest
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 2 items
|
||||
|
||||
test_module.py .F [100%]
|
||||
@@ -80,10 +88,10 @@ of the failing function and hide the other one:
|
||||
> assert False
|
||||
E assert False
|
||||
|
||||
test_module.py:9: AssertionError
|
||||
test_module.py:12: AssertionError
|
||||
-------------------------- Captured stdout setup ---------------------------
|
||||
setting up <function test_func2 at 0xdeadbeef>
|
||||
==================== 1 failed, 1 passed in 0.12 seconds ====================
|
||||
======================= 1 failed, 1 passed in 0.12s ========================
|
||||
|
||||
Accessing captured output from a test function
|
||||
---------------------------------------------------
|
||||
@@ -118,11 +126,11 @@ same interface but allows to also capture output from
|
||||
libraries or subprocesses that directly write to operating
|
||||
system level output streams (FD1 and FD2).
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
|
||||
The return value from ``readouterr`` changed to a ``namedtuple`` with two attributes, ``out`` and ``err``.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
|
||||
If the code under test writes non-textual data, you can capture this using
|
||||
the ``capsysbinary`` fixture which instead returns ``bytes`` from
|
||||
@@ -130,7 +138,7 @@ the ``readouterr`` method. The ``capfsysbinary`` fixture is currently only
|
||||
available in python 3.
|
||||
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
|
||||
If the code under test writes non-textual data, you can capture this using
|
||||
the ``capfdbinary`` fixture which instead returns ``bytes`` from
|
||||
@@ -138,7 +146,7 @@ the ``readouterr`` method. The ``capfdbinary`` fixture operates on the
|
||||
filedescriptor level.
|
||||
|
||||
|
||||
.. versionadded:: 3.0
|
||||
|
||||
|
||||
To temporarily disable capture within a test, both ``capsys``
|
||||
and ``capfd`` have a ``disabled()`` method that can be used
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# pytest documentation build configuration file, created by
|
||||
# sphinx-quickstart on Fri Oct 8 17:54:28 2010.
|
||||
@@ -16,7 +15,6 @@
|
||||
#
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
# The short X.Y version.
|
||||
import datetime
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -42,10 +40,11 @@ todo_include_todos = 1
|
||||
extensions = [
|
||||
"pygments_pytest",
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.todo",
|
||||
"sphinx.ext.autosummary",
|
||||
"sphinx.ext.intersphinx",
|
||||
"sphinx.ext.todo",
|
||||
"sphinx.ext.viewcode",
|
||||
"sphinx_removed_in",
|
||||
"sphinxcontrib_trio",
|
||||
]
|
||||
|
||||
@@ -62,9 +61,8 @@ source_suffix = ".rst"
|
||||
master_doc = "contents"
|
||||
|
||||
# General information about the project.
|
||||
project = u"pytest"
|
||||
year = datetime.datetime.utcnow().year
|
||||
copyright = u"2015–2018 , holger krekel and pytest-dev team"
|
||||
project = "pytest"
|
||||
copyright = "2015–2019, holger krekel and pytest-dev team"
|
||||
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
@@ -167,18 +165,18 @@ html_favicon = "img/pytest1favi.ico"
|
||||
|
||||
html_sidebars = {
|
||||
"index": [
|
||||
"slim_searchbox.html",
|
||||
"sidebarintro.html",
|
||||
"globaltoc.html",
|
||||
"links.html",
|
||||
"sourcelink.html",
|
||||
"searchbox.html",
|
||||
],
|
||||
"**": [
|
||||
"slim_searchbox.html",
|
||||
"globaltoc.html",
|
||||
"relations.html",
|
||||
"links.html",
|
||||
"sourcelink.html",
|
||||
"searchbox.html",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -232,8 +230,8 @@ latex_documents = [
|
||||
(
|
||||
"contents",
|
||||
"pytest.tex",
|
||||
u"pytest Documentation",
|
||||
u"holger krekel, trainer and consultant, http://merlinux.eu",
|
||||
"pytest Documentation",
|
||||
"holger krekel, trainer and consultant, http://merlinux.eu",
|
||||
"manual",
|
||||
)
|
||||
]
|
||||
@@ -265,16 +263,16 @@ latex_domain_indices = False
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [("usage", "pytest", u"pytest usage", [u"holger krekel at merlinux eu"], 1)]
|
||||
man_pages = [("usage", "pytest", "pytest usage", ["holger krekel at merlinux eu"], 1)]
|
||||
|
||||
|
||||
# -- Options for Epub output ---------------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
epub_title = u"pytest"
|
||||
epub_author = u"holger krekel at merlinux eu"
|
||||
epub_publisher = u"holger krekel at merlinux eu"
|
||||
epub_copyright = u"2013, holger krekel et alii"
|
||||
epub_title = "pytest"
|
||||
epub_author = "holger krekel at merlinux eu"
|
||||
epub_publisher = "holger krekel at merlinux eu"
|
||||
epub_copyright = "2013, holger krekel et alii"
|
||||
|
||||
# The language of the text. It defaults to the language option
|
||||
# or en if the language is not set.
|
||||
@@ -334,7 +332,7 @@ intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
|
||||
def setup(app):
|
||||
# from sphinx.ext.autodoc import cut_lines
|
||||
# app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
|
||||
app.add_description_unit(
|
||||
app.add_object_type(
|
||||
"confval",
|
||||
"confval",
|
||||
objname="configuration value",
|
||||
|
||||
@@ -41,6 +41,7 @@ Full pytest documentation
|
||||
|
||||
backwards-compatibility
|
||||
deprecations
|
||||
py27-py34-deprecation
|
||||
historical-notes
|
||||
license
|
||||
contributing
|
||||
@@ -49,6 +50,7 @@ Full pytest documentation
|
||||
projects
|
||||
faq
|
||||
contact
|
||||
sponsor
|
||||
|
||||
.. only:: html
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ Command line options and configuration file settings
|
||||
-----------------------------------------------------------------
|
||||
|
||||
You can get help on command line options and values in INI-style
|
||||
configurations files by using the general help option::
|
||||
configurations files by using the general help option:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest -h # prints options _and_ config file settings
|
||||
|
||||
@@ -18,7 +20,7 @@ which were registered by installed plugins.
|
||||
Initialization: determining rootdir and inifile
|
||||
-----------------------------------------------
|
||||
|
||||
.. versionadded:: 2.7
|
||||
|
||||
|
||||
pytest determines a ``rootdir`` for each test run which depends on
|
||||
the command line arguments (specified test files, paths) and on
|
||||
@@ -88,16 +90,20 @@ The ``config`` object will subsequently carry these attributes:
|
||||
|
||||
- ``config.inifile``: the determined ini-file, may be ``None``.
|
||||
|
||||
The rootdir is used a reference directory for constructing test
|
||||
The rootdir is used as a reference directory for constructing test
|
||||
addresses ("nodeids") and can be used also by plugins for storing
|
||||
per-testrun information.
|
||||
|
||||
Example::
|
||||
Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest path/to/testdir path/other/
|
||||
|
||||
will determine the common ancestor as ``path`` and then
|
||||
check for ini-files as follows::
|
||||
check for ini-files as follows:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
# first look for pytest.ini files
|
||||
path/pytest.ini
|
||||
@@ -127,25 +133,33 @@ progress output, you can write it into a configuration file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# content of pytest.ini
|
||||
# (or tox.ini or setup.cfg)
|
||||
# content of pytest.ini or tox.ini
|
||||
# setup.cfg files should use [tool:pytest] section instead
|
||||
[pytest]
|
||||
addopts = -ra -q
|
||||
|
||||
Alternatively, you can set a ``PYTEST_ADDOPTS`` environment variable to add command
|
||||
line options while the environment is in use::
|
||||
line options while the environment is in use:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
export PYTEST_ADDOPTS="-v"
|
||||
|
||||
Here's how the command-line is built in the presence of ``addopts`` or the environment variable::
|
||||
Here's how the command-line is built in the presence of ``addopts`` or the environment variable:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
<pytest.ini:addopts> $PYTEST_ADDOPTS <extra command-line arguments>
|
||||
|
||||
So if the user executes in the command-line::
|
||||
So if the user executes in the command-line:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest -m slow
|
||||
|
||||
The actual command line executed is::
|
||||
The actual command line executed is:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest -ra -q -v -m slow
|
||||
|
||||
|
||||
@@ -7,6 +7,11 @@ This page lists all pytest features that are currently deprecated or have been r
|
||||
The objective is to give users a clear rationale why a certain feature has been removed, and what alternatives
|
||||
should be used instead.
|
||||
|
||||
.. contents::
|
||||
:depth: 3
|
||||
:local:
|
||||
|
||||
|
||||
Deprecated Features
|
||||
-------------------
|
||||
|
||||
@@ -14,24 +19,249 @@ Below is a complete list of all pytest features which are considered deprecated.
|
||||
:class:`_pytest.warning_types.PytestWarning` or subclasses, which can be filtered using
|
||||
:ref:`standard warning filters <warnings>`.
|
||||
|
||||
Internal classes accessed through ``Node``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.9
|
||||
``funcargnames`` alias for ``fixturenames``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Access of ``Module``, ``Function``, ``Class``, ``Instance``, ``File`` and ``Item`` through ``Node`` instances now issue
|
||||
this warning::
|
||||
.. deprecated:: 5.0
|
||||
|
||||
usage of Function.Module is deprecated, please use pytest.Module instead
|
||||
The ``FixtureRequest``, ``Metafunc``, and ``Function`` classes track the names of
|
||||
their associated fixtures, with the aptly-named ``fixturenames`` attribute.
|
||||
|
||||
Users should just ``import pytest`` and access those objects using the ``pytest`` module.
|
||||
Prior to pytest 2.3, this attribute was named ``funcargnames``, and we have kept
|
||||
that as an alias since. It is finally due for removal, as it is often confusing
|
||||
in places where we or plugin authors must distinguish between fixture names and
|
||||
names supplied by non-fixture things such as ``pytest.mark.parametrize``.
|
||||
|
||||
|
||||
Result log (``--result-log``)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 4.0
|
||||
|
||||
The ``--result-log`` option produces a stream of test reports which can be
|
||||
analysed at runtime. It uses a custom format which requires users to implement their own
|
||||
parser, but the team believes using a line-based format that can be parsed using standard
|
||||
tools would provide a suitable and better alternative.
|
||||
|
||||
The current plan is to provide an alternative in the pytest 5.0 series and remove the ``--result-log``
|
||||
option in pytest 6.0 after the new implementation proves satisfactory to all users and is deemed
|
||||
stable.
|
||||
|
||||
The actual alternative is still being discussed in issue `#4488 <https://github.com/pytest-dev/pytest/issues/4488>`__.
|
||||
|
||||
|
||||
Removed Features
|
||||
----------------
|
||||
|
||||
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
||||
an appropriate period of deprecation has passed.
|
||||
|
||||
|
||||
``pytest.config`` global
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 5.0
|
||||
|
||||
The ``pytest.config`` global object is deprecated. Instead use
|
||||
``request.config`` (via the ``request`` fixture) or if you are a plugin author
|
||||
use the ``pytest_configure(config)`` hook. Note that many hooks can also access
|
||||
the ``config`` object indirectly, through ``session.config`` or ``item.config`` for example.
|
||||
|
||||
|
||||
.. _`raises message deprecated`:
|
||||
|
||||
``"message"`` parameter of ``pytest.raises``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 5.0
|
||||
|
||||
It is a common mistake to think this parameter will match the exception message, while in fact
|
||||
it only serves to provide a custom message in case the ``pytest.raises`` check fails. To prevent
|
||||
users from making this mistake, and because it is believed to be little used, pytest is
|
||||
deprecating it without providing an alternative for the moment.
|
||||
|
||||
If you have a valid use case for this parameter, consider that to obtain the same results
|
||||
you can just call ``pytest.fail`` manually at the end of the ``with`` statement.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with pytest.raises(TimeoutError, message="Client got unexpected message"):
|
||||
wait_for(websocket.recv(), 0.5)
|
||||
|
||||
|
||||
Becomes:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with pytest.raises(TimeoutError):
|
||||
wait_for(websocket.recv(), 0.5)
|
||||
pytest.fail("Client got unexpected message")
|
||||
|
||||
|
||||
If you still have concerns about this deprecation and future removal, please comment on
|
||||
`issue #3974 <https://github.com/pytest-dev/pytest/issues/3974>`__.
|
||||
|
||||
|
||||
.. _raises-warns-exec:
|
||||
|
||||
``raises`` / ``warns`` with a string as the second argument
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 5.0
|
||||
|
||||
Use the context manager form of these instead. When necessary, invoke ``exec``
|
||||
directly.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pytest.raises(ZeroDivisionError, "1 / 0")
|
||||
pytest.raises(SyntaxError, "a $ b")
|
||||
|
||||
pytest.warns(DeprecationWarning, "my_function()")
|
||||
pytest.warns(SyntaxWarning, "assert(1, 2)")
|
||||
|
||||
Becomes:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with pytest.raises(ZeroDivisionError):
|
||||
1 / 0
|
||||
with pytest.raises(SyntaxError):
|
||||
exec("a $ b") # exec is required for invalid syntax
|
||||
|
||||
with pytest.warns(DeprecationWarning):
|
||||
my_function()
|
||||
with pytest.warns(SyntaxWarning):
|
||||
exec("assert(1, 2)") # exec is used to avoid a top-level warning
|
||||
|
||||
|
||||
|
||||
|
||||
Using ``Class`` in custom Collectors
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
Using objects named ``"Class"`` as a way to customize the type of nodes that are collected in ``Collector``
|
||||
subclasses has been deprecated. Users instead should use ``pytest_pycollect_makeitem`` to customize node types during
|
||||
collection.
|
||||
|
||||
This issue should affect only advanced plugins who create new collection types, so if you see this warning
|
||||
message please contact the authors so they can change the code.
|
||||
|
||||
|
||||
marks in ``pytest.mark.parametrize``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
Applying marks to values of a ``pytest.mark.parametrize`` call is now deprecated. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"a, b",
|
||||
[
|
||||
(3, 9),
|
||||
pytest.mark.xfail(reason="flaky")(6, 36),
|
||||
(10, 100),
|
||||
(20, 200),
|
||||
(40, 400),
|
||||
(50, 500),
|
||||
],
|
||||
)
|
||||
def test_foo(a, b):
|
||||
...
|
||||
|
||||
This code applies the ``pytest.mark.xfail(reason="flaky")`` mark to the ``(6, 36)`` value of the above parametrization
|
||||
call.
|
||||
|
||||
This was considered hard to read and understand, and also its implementation presented problems to the code preventing
|
||||
further internal improvements in the marks architecture.
|
||||
|
||||
To update the code, use ``pytest.param``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"a, b",
|
||||
[
|
||||
(3, 9),
|
||||
pytest.param(6, 36, marks=pytest.mark.xfail(reason="flaky")),
|
||||
(10, 100),
|
||||
(20, 200),
|
||||
(40, 400),
|
||||
(50, 500),
|
||||
],
|
||||
)
|
||||
def test_foo(a, b):
|
||||
...
|
||||
|
||||
|
||||
``pytest_funcarg__`` prefix
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
In very early pytest versions fixtures could be defined using the ``pytest_funcarg__`` prefix:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def pytest_funcarg__data():
|
||||
return SomeData()
|
||||
|
||||
Switch over to the ``@pytest.fixture`` decorator:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.fixture
|
||||
def data():
|
||||
return SomeData()
|
||||
|
||||
|
||||
|
||||
[pytest] section in setup.cfg files
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
``[pytest]`` sections in ``setup.cfg`` files should now be named ``[tool:pytest]``
|
||||
to avoid conflicts with other distutils commands.
|
||||
|
||||
|
||||
Metafunc.addcall
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
:meth:`_pytest.python.Metafunc.addcall` was a precursor to the current parametrized mechanism. Users should use
|
||||
:meth:`_pytest.python.Metafunc.parametrize` instead.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
metafunc.addcall({"i": 1}, id="1")
|
||||
metafunc.addcall({"i": 2}, id="2")
|
||||
|
||||
Becomes:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
metafunc.parametrize("i", [1, 2], ids=["1", "2"])
|
||||
|
||||
This has been documented as deprecated for years, but only now we are actually emitting deprecation warnings.
|
||||
|
||||
``cached_setup``
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.9
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
``request.cached_setup`` was the precursor of the setup/teardown mechanism available to fixtures.
|
||||
|
||||
@@ -59,26 +289,21 @@ This should be updated to make use of standard fixture mechanisms:
|
||||
You can consult `funcarg comparison section in the docs <https://docs.pytest.org/en/latest/funcarg_compare.html>`_ for
|
||||
more information.
|
||||
|
||||
This has been documented as deprecated for years, but only now we are actually emitting deprecation warnings.
|
||||
|
||||
pytest_plugins in non-top-level conftest files
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Using ``Class`` in custom Collectors
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
.. deprecated:: 3.9
|
||||
|
||||
Using objects named ``"Class"`` as a way to customize the type of nodes that are collected in ``Collector``
|
||||
subclasses has been deprecated. Users instead should use ``pytest_pycollect_makeitem`` to customize node types during
|
||||
collection.
|
||||
|
||||
This issue should affect only advanced plugins who create new collection types, so if you see this warning
|
||||
message please contact the authors so they can change the code.
|
||||
Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py
|
||||
files because they will activate referenced plugins *globally*, which is surprising because for all other pytest
|
||||
features ``conftest.py`` files are only *active* for tests at or below it.
|
||||
|
||||
|
||||
``Config.warn`` and ``Node.warn``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.8
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
Those methods were part of the internal pytest warnings system, but since ``3.8`` pytest is using the builtin warning
|
||||
system for its own warnings, so those two functions are now deprecated.
|
||||
@@ -100,47 +325,57 @@ Becomes:
|
||||
* ``node.warn(PytestWarning("some message"))``: is now the **recommended** way to call this function.
|
||||
The warning instance must be a PytestWarning or subclass.
|
||||
|
||||
* ``node.warn("CI", "some message")``: this code/message form is now **deprecated** and should be converted to the warning instance form above.
|
||||
* ``node.warn("CI", "some message")``: this code/message form has been **removed** and should be converted to the warning instance form above.
|
||||
|
||||
record_xml_property
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``pytest_namespace``
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
.. deprecated:: 3.7
|
||||
The ``record_xml_property`` fixture is now deprecated in favor of the more generic ``record_property``, which
|
||||
can be used by other consumers (for example ``pytest-html``) to obtain custom information about the test run.
|
||||
|
||||
This hook is deprecated because it greatly complicates the pytest internals regarding configuration and initialization, making some
|
||||
bug fixes and refactorings impossible.
|
||||
|
||||
Example of usage:
|
||||
This is just a matter of renaming the fixture as the API is the same:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MySymbol:
|
||||
def test_foo(record_xml_property):
|
||||
...
|
||||
|
||||
Change to:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_foo(record_property):
|
||||
...
|
||||
|
||||
|
||||
def pytest_namespace():
|
||||
return {"my_symbol": MySymbol()}
|
||||
Passing command-line string to ``pytest.main()``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
Plugin authors relying on this hook should instead require that users now import the plugin modules directly (with an appropriate public API).
|
||||
|
||||
As a stopgap measure, plugin authors may still inject their names into pytest's namespace, usually during ``pytest_configure``:
|
||||
Passing a command-line string to ``pytest.main()`` is deprecated:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import pytest
|
||||
pytest.main("-v -s")
|
||||
|
||||
Pass a list instead:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pytest.main(["-v", "-s"])
|
||||
|
||||
|
||||
def pytest_configure():
|
||||
pytest.my_symbol = MySymbol()
|
||||
|
||||
By passing a string, users expect that pytest will interpret that command-line using the shell rules they are working
|
||||
on (for example ``bash`` or ``Powershell``), but this is very hard/impossible to do in a portable way.
|
||||
|
||||
|
||||
Calling fixtures directly
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.7
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
Calling a fixture function directly, as opposed to request them in a test function, is deprecated.
|
||||
|
||||
@@ -175,116 +410,27 @@ In those cases just request the function directly in the dependent fixture:
|
||||
cell.make_full()
|
||||
return cell
|
||||
|
||||
``Node.get_marker``
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.6
|
||||
|
||||
As part of a large :ref:`marker-revamp`, :meth:`_pytest.nodes.Node.get_marker` is deprecated. See
|
||||
:ref:`the documentation <update marker code>` on tips on how to update your code.
|
||||
|
||||
|
||||
record_xml_property
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.5
|
||||
|
||||
The ``record_xml_property`` fixture is now deprecated in favor of the more generic ``record_property``, which
|
||||
can be used by other consumers (for example ``pytest-html``) to obtain custom information about the test run.
|
||||
|
||||
This is just a matter of renaming the fixture as the API is the same:
|
||||
Alternatively if the fixture function is called multiple times inside a test (making it hard to apply the above pattern) or
|
||||
if you would like to make minimal changes to the code, you can create a fixture which calls the original function together
|
||||
with the ``name`` parameter:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_foo(record_xml_property):
|
||||
...
|
||||
|
||||
Change to:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_foo(record_property):
|
||||
...
|
||||
|
||||
pytest_plugins in non-top-level conftest files
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.5
|
||||
|
||||
Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py
|
||||
files because they will activate referenced plugins *globally*, which is surprising because for all other pytest
|
||||
features ``conftest.py`` files are only *active* for tests at or below it.
|
||||
|
||||
Metafunc.addcall
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.3
|
||||
|
||||
:meth:`_pytest.python.Metafunc.addcall` was a precursor to the current parametrized mechanism. Users should use
|
||||
:meth:`_pytest.python.Metafunc.parametrize` instead.
|
||||
|
||||
marks in ``pytest.mark.parametrize``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.2
|
||||
|
||||
Applying marks to values of a ``pytest.mark.parametrize`` call is now deprecated. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"a, b", [(3, 9), pytest.mark.xfail(reason="flaky")(6, 36), (10, 100)]
|
||||
)
|
||||
def test_foo(a, b):
|
||||
...
|
||||
|
||||
This code applies the ``pytest.mark.xfail(reason="flaky")`` mark to the ``(6, 36)`` value of the above parametrization
|
||||
call.
|
||||
|
||||
This was considered hard to read and understand, and also its implementation presented problems to the code preventing
|
||||
further internal improvements in the marks architecture.
|
||||
|
||||
To update the code, use ``pytest.param``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"a, b",
|
||||
[(3, 9), pytest.param((6, 36), marks=pytest.mark.xfail(reason="flaky")), (10, 100)],
|
||||
)
|
||||
def test_foo(a, b):
|
||||
...
|
||||
def cell():
|
||||
return ...
|
||||
|
||||
|
||||
|
||||
Passing command-line string to ``pytest.main()``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.0
|
||||
|
||||
Passing a command-line string to ``pytest.main()`` is deprecated:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pytest.main("-v -s")
|
||||
|
||||
Pass a list instead:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pytest.main(["-v", "-s"])
|
||||
|
||||
|
||||
By passing a string, users expect that pytest will interpret that command-line using the shell rules they are working
|
||||
on (for example ``bash`` or ``Powershell``), but this is very hard/impossible to do in a portable way.
|
||||
@pytest.fixture(name="cell")
|
||||
def cell_fixture():
|
||||
return cell()
|
||||
|
||||
|
||||
``yield`` tests
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.0
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
pytest supports ``yield``-style tests, where a test function actually ``yield`` functions and values
|
||||
pytest supported ``yield``-style tests, where a test function actually ``yield`` functions and values
|
||||
that are then turned into proper test methods. Example:
|
||||
|
||||
.. code-block:: python
|
||||
@@ -307,54 +453,79 @@ This form of test function doesn't support fixtures properly, and users should s
|
||||
def test_squared(x, y):
|
||||
assert x ** x == y
|
||||
|
||||
Internal classes accessed through ``Node``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``pytest_funcarg__`` prefix
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
.. deprecated:: 3.0
|
||||
Access of ``Module``, ``Function``, ``Class``, ``Instance``, ``File`` and ``Item`` through ``Node`` instances now issue
|
||||
this warning:
|
||||
|
||||
In very early pytest versions fixtures could be defined using the ``pytest_funcarg__`` prefix:
|
||||
.. code-block:: text
|
||||
|
||||
usage of Function.Module is deprecated, please use pytest.Module instead
|
||||
|
||||
Users should just ``import pytest`` and access those objects using the ``pytest`` module.
|
||||
|
||||
This has been documented as deprecated for years, but only now we are actually emitting deprecation warnings.
|
||||
|
||||
``Node.get_marker``
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
As part of a large :ref:`marker-revamp`, :meth:`_pytest.nodes.Node.get_marker` is deprecated. See
|
||||
:ref:`the documentation <update marker code>` on tips on how to update your code.
|
||||
|
||||
|
||||
``somefunction.markname``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
As part of a large :ref:`marker-revamp` we already deprecated using ``MarkInfo``
|
||||
the only correct way to get markers of an element is via ``node.iter_markers(name)``.
|
||||
|
||||
|
||||
``pytest_namespace``
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 4.0
|
||||
|
||||
This hook is deprecated because it greatly complicates the pytest internals regarding configuration and initialization, making some
|
||||
bug fixes and refactorings impossible.
|
||||
|
||||
Example of usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def pytest_funcarg__data():
|
||||
return SomeData()
|
||||
class MySymbol:
|
||||
...
|
||||
|
||||
Switch over to the ``@pytest.fixture`` decorator:
|
||||
|
||||
def pytest_namespace():
|
||||
return {"my_symbol": MySymbol()}
|
||||
|
||||
|
||||
Plugin authors relying on this hook should instead require that users now import the plugin modules directly (with an appropriate public API).
|
||||
|
||||
As a stopgap measure, plugin authors may still inject their names into pytest's namespace, usually during ``pytest_configure``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.fixture
|
||||
def data():
|
||||
return SomeData()
|
||||
import pytest
|
||||
|
||||
[pytest] section in setup.cfg files
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.0
|
||||
def pytest_configure():
|
||||
pytest.my_symbol = MySymbol()
|
||||
|
||||
``[pytest]`` sections in ``setup.cfg`` files should now be named ``[tool:pytest]``
|
||||
to avoid conflicts with other distutils commands.
|
||||
|
||||
Result log (``--result-log``)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.0
|
||||
|
||||
The ``--resultlog`` command line option has been deprecated: it is little used
|
||||
and there are more modern and better alternatives, for example `pytest-tap <https://tappy.readthedocs.io/en/latest/>`_.
|
||||
|
||||
Removed Features
|
||||
----------------
|
||||
|
||||
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
||||
an appropriate period of deprecation has passed.
|
||||
|
||||
|
||||
Reinterpretation mode (``--assert=reinterp``)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Removed in version 3.0.*
|
||||
.. versionremoved:: 3.0
|
||||
|
||||
Reinterpretation mode has now been removed and only plain and rewrite
|
||||
mode are available, consequently the ``--assert=reinterp`` option is
|
||||
@@ -366,7 +537,7 @@ explicitly turn on assertion rewriting for those files.
|
||||
Removed command-line options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Removed in version 3.0.*
|
||||
.. versionremoved:: 3.0
|
||||
|
||||
The following deprecated commandline options were removed:
|
||||
|
||||
@@ -378,7 +549,7 @@ The following deprecated commandline options were removed:
|
||||
py.test-X* entry points
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Removed in version 3.0.*
|
||||
.. versionremoved:: 3.0
|
||||
|
||||
Removed all ``py.test-X*`` entry points. The versioned, suffixed entry points
|
||||
were never documented and a leftover from a pre-virtualenv era. These entry
|
||||
|
||||
@@ -4,31 +4,69 @@ Doctest integration for modules and test files
|
||||
|
||||
By default all files matching the ``test*.txt`` pattern will
|
||||
be run through the python standard ``doctest`` module. You
|
||||
can change the pattern by issuing::
|
||||
can change the pattern by issuing:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --doctest-glob='*.rst'
|
||||
|
||||
on the command line. Since version ``2.9``, ``--doctest-glob``
|
||||
can be given multiple times in the command-line.
|
||||
on the command line. ``--doctest-glob`` can be given multiple times in the command-line.
|
||||
|
||||
.. versionadded:: 3.1
|
||||
If you then have a text file like this:
|
||||
|
||||
You can specify the encoding that will be used for those doctest files
|
||||
using the ``doctest_encoding`` ini option:
|
||||
.. code-block:: text
|
||||
|
||||
.. code-block:: ini
|
||||
# content of test_example.txt
|
||||
|
||||
# content of pytest.ini
|
||||
[pytest]
|
||||
doctest_encoding = latin1
|
||||
hello this is a doctest
|
||||
>>> x = 3
|
||||
>>> x
|
||||
3
|
||||
|
||||
The default encoding is UTF-8.
|
||||
then you can just invoke ``pytest`` directly:
|
||||
|
||||
You can also trigger running of doctests
|
||||
from docstrings in all python modules (including regular
|
||||
python test modules)::
|
||||
.. code-block:: pytest
|
||||
|
||||
pytest --doctest-modules
|
||||
$ pytest
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 1 item
|
||||
|
||||
test_example.txt . [100%]
|
||||
|
||||
============================ 1 passed in 0.12s =============================
|
||||
|
||||
By default, pytest will collect ``test*.txt`` files looking for doctest directives, but you
|
||||
can pass additional globs using the ``--doctest-glob`` option (multi-allowed).
|
||||
|
||||
In addition to text files, you can also execute doctests directly from docstrings of your classes
|
||||
and functions, including from test modules:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of mymodule.py
|
||||
def something():
|
||||
""" a doctest in a docstring
|
||||
>>> something()
|
||||
42
|
||||
"""
|
||||
return 42
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest --doctest-modules
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 2 items
|
||||
|
||||
mymodule.py . [ 50%]
|
||||
test_example.txt . [100%]
|
||||
|
||||
============================ 2 passed in 0.12s =============================
|
||||
|
||||
You can make these changes permanent in your project by
|
||||
putting them into a pytest.ini file like this:
|
||||
@@ -39,40 +77,127 @@ putting them into a pytest.ini file like this:
|
||||
[pytest]
|
||||
addopts = --doctest-modules
|
||||
|
||||
If you then have a text file like this::
|
||||
.. note::
|
||||
|
||||
# content of example.rst
|
||||
The builtin pytest doctest supports only ``doctest`` blocks, but if you are looking
|
||||
for more advanced checking over *all* your documentation,
|
||||
including doctests, ``.. codeblock:: python`` Sphinx directive support,
|
||||
and any other examples your documentation may include, you may wish to
|
||||
consider `Sybil <https://sybil.readthedocs.io/en/latest/index.html>`__.
|
||||
It provides pytest integration out of the box.
|
||||
|
||||
hello this is a doctest
|
||||
>>> x = 3
|
||||
>>> x
|
||||
3
|
||||
|
||||
and another like this::
|
||||
Encoding
|
||||
--------
|
||||
|
||||
# content of mymodule.py
|
||||
def something():
|
||||
""" a doctest in a docstring
|
||||
>>> something()
|
||||
42
|
||||
"""
|
||||
return 42
|
||||
The default encoding is **UTF-8**, but you can specify the encoding
|
||||
that will be used for those doctest files using the
|
||||
``doctest_encoding`` ini option:
|
||||
|
||||
then you can just invoke ``pytest`` without command line options:
|
||||
.. code-block:: ini
|
||||
|
||||
.. code-block:: pytest
|
||||
# content of pytest.ini
|
||||
[pytest]
|
||||
doctest_encoding = latin1
|
||||
|
||||
$ pytest
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||
collected 1 item
|
||||
Using 'doctest' options
|
||||
-----------------------
|
||||
|
||||
mymodule.py . [100%]
|
||||
Python's standard ``doctest`` module provides some `options <https://docs.python.org/3/library/doctest.html#option-flags>`__
|
||||
to configure the strictness of doctest tests. In pytest, you can enable those flags using the
|
||||
configuration file.
|
||||
|
||||
========================= 1 passed in 0.12 seconds =========================
|
||||
For example, to make pytest ignore trailing whitespaces and ignore
|
||||
lengthy exception stack traces you can just write:
|
||||
|
||||
It is possible to use fixtures using the ``getfixture`` helper::
|
||||
.. code-block:: ini
|
||||
|
||||
[pytest]
|
||||
doctest_optionflags= NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL
|
||||
|
||||
Alternatively, options can be enabled by an inline comment in the doc test
|
||||
itself:
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
>>> something_that_raises() # doctest: +IGNORE_EXCEPTION_DETAIL
|
||||
Traceback (most recent call last):
|
||||
ValueError: ...
|
||||
|
||||
pytest also introduces new options:
|
||||
|
||||
* ``ALLOW_UNICODE``: when enabled, the ``u`` prefix is stripped from unicode
|
||||
strings in expected doctest output. This allows doctests to run in Python 2
|
||||
and Python 3 unchanged.
|
||||
|
||||
* ``ALLOW_BYTES``: similarly, the ``b`` prefix is stripped from byte strings
|
||||
in expected doctest output.
|
||||
|
||||
* ``NUMBER``: when enabled, floating-point numbers only need to match as far as
|
||||
the precision you have written in the expected doctest output. For example,
|
||||
the following output would only need to match to 2 decimal places::
|
||||
|
||||
>>> math.pi
|
||||
3.14
|
||||
|
||||
If you wrote ``3.1416`` then the actual output would need to match to 4
|
||||
decimal places; and so on.
|
||||
|
||||
This avoids false positives caused by limited floating-point precision, like
|
||||
this::
|
||||
|
||||
Expected:
|
||||
0.233
|
||||
Got:
|
||||
0.23300000000000001
|
||||
|
||||
``NUMBER`` also supports lists of floating-point numbers -- in fact, it
|
||||
matches floating-point numbers appearing anywhere in the output, even inside
|
||||
a string! This means that it may not be appropriate to enable globally in
|
||||
``doctest_optionflags`` in your configuration file.
|
||||
|
||||
|
||||
Continue on failure
|
||||
-------------------
|
||||
|
||||
By default, pytest would report only the first failure for a given doctest. If
|
||||
you want to continue the test even when you have failures, do:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --doctest-modules --doctest-continue-on-failure
|
||||
|
||||
|
||||
Output format
|
||||
-------------
|
||||
|
||||
You can change the diff output format on failure for your doctests
|
||||
by using one of standard doctest modules format in options
|
||||
(see :data:`python:doctest.REPORT_UDIFF`, :data:`python:doctest.REPORT_CDIFF`,
|
||||
:data:`python:doctest.REPORT_NDIFF`, :data:`python:doctest.REPORT_ONLY_FIRST_FAILURE`):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --doctest-modules --doctest-report none
|
||||
pytest --doctest-modules --doctest-report udiff
|
||||
pytest --doctest-modules --doctest-report cdiff
|
||||
pytest --doctest-modules --doctest-report ndiff
|
||||
pytest --doctest-modules --doctest-report only_first_failure
|
||||
|
||||
|
||||
pytest-specific features
|
||||
------------------------
|
||||
|
||||
Some features are provided to make writing doctests easier or with better integration with
|
||||
your existing test suite. Keep in mind however that by using those features you will make
|
||||
your doctests incompatible with the standard ``doctests`` module.
|
||||
|
||||
Using fixtures
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
It is possible to use fixtures using the ``getfixture`` helper:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
# content of example.rst
|
||||
>>> tmp = getfixture('tmpdir')
|
||||
@@ -82,68 +207,32 @@ It is possible to use fixtures using the ``getfixture`` helper::
|
||||
Also, :ref:`usefixtures` and :ref:`autouse` fixtures are supported
|
||||
when executing text doctest files.
|
||||
|
||||
The standard ``doctest`` module provides some setting flags to configure the
|
||||
strictness of doctest tests. In pytest, you can enable those flags using the
|
||||
configuration file. To make pytest ignore trailing whitespaces and ignore
|
||||
lengthy exception stack traces you can just write:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pytest]
|
||||
doctest_optionflags= NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL
|
||||
|
||||
pytest also introduces new options to allow doctests to run in Python 2 and
|
||||
Python 3 unchanged:
|
||||
|
||||
* ``ALLOW_UNICODE``: when enabled, the ``u`` prefix is stripped from unicode
|
||||
strings in expected doctest output.
|
||||
|
||||
* ``ALLOW_BYTES``: when enabled, the ``b`` prefix is stripped from byte strings
|
||||
in expected doctest output.
|
||||
|
||||
As with any other option flag, these flags can be enabled in ``pytest.ini`` using
|
||||
the ``doctest_optionflags`` ini option:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pytest]
|
||||
doctest_optionflags = ALLOW_UNICODE ALLOW_BYTES
|
||||
|
||||
|
||||
Alternatively, it can be enabled by an inline comment in the doc test
|
||||
itself::
|
||||
|
||||
# content of example.rst
|
||||
>>> get_unicode_greeting() # doctest: +ALLOW_UNICODE
|
||||
'Hello'
|
||||
|
||||
By default, pytest would report only the first failure for a given doctest. If
|
||||
you want to continue the test even when you have failures, do::
|
||||
|
||||
pytest --doctest-modules --doctest-continue-on-failure
|
||||
|
||||
|
||||
.. _`doctest_namespace`:
|
||||
|
||||
The 'doctest_namespace' fixture
|
||||
-------------------------------
|
||||
|
||||
.. versionadded:: 3.0
|
||||
'doctest_namespace' fixture
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``doctest_namespace`` fixture can be used to inject items into the
|
||||
namespace in which your doctests run. It is intended to be used within
|
||||
your own fixtures to provide the tests that use them with context.
|
||||
|
||||
``doctest_namespace`` is a standard ``dict`` object into which you
|
||||
place the objects you want to appear in the doctest namespace::
|
||||
place the objects you want to appear in the doctest namespace:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
import numpy
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def add_np(doctest_namespace):
|
||||
doctest_namespace['np'] = numpy
|
||||
doctest_namespace["np"] = numpy
|
||||
|
||||
which can then be used in your doctests directly::
|
||||
which can then be used in your doctests directly:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of numpy.py
|
||||
def arange():
|
||||
@@ -158,18 +247,16 @@ Note that like the normal ``conftest.py``, the fixtures are discovered in the di
|
||||
Meaning that if you put your doctest with your source code, the relevant conftest.py needs to be in the same directory tree.
|
||||
Fixtures will not be discovered in a sibling directory tree!
|
||||
|
||||
Output format
|
||||
-------------
|
||||
Skipping tests dynamically
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. versionadded:: 3.0
|
||||
.. versionadded:: 4.4
|
||||
|
||||
You can change the diff output format on failure for your doctests
|
||||
by using one of standard doctest modules format in options
|
||||
(see :data:`python:doctest.REPORT_UDIFF`, :data:`python:doctest.REPORT_CDIFF`,
|
||||
:data:`python:doctest.REPORT_NDIFF`, :data:`python:doctest.REPORT_ONLY_FIRST_FAILURE`)::
|
||||
You can use ``pytest.skip`` to dynamically skip doctests. For example:
|
||||
|
||||
pytest --doctest-modules --doctest-report none
|
||||
pytest --doctest-modules --doctest-report udiff
|
||||
pytest --doctest-modules --doctest-report cdiff
|
||||
pytest --doctest-modules --doctest-report ndiff
|
||||
pytest --doctest-modules --doctest-report only_first_failure
|
||||
.. code-block:: text
|
||||
|
||||
>>> import sys, pytest
|
||||
>>> if sys.platform.startswith('win'):
|
||||
... pytest.skip('this doctest does not work on Windows')
|
||||
...
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import six
|
||||
|
||||
import _pytest._code
|
||||
import pytest
|
||||
from pytest import raises
|
||||
@@ -22,7 +20,7 @@ def test_generative(param1, param2):
|
||||
assert param1 * 2 < param2
|
||||
|
||||
|
||||
class TestFailing(object):
|
||||
class TestFailing:
|
||||
def test_simple(self):
|
||||
def f():
|
||||
return 42
|
||||
@@ -42,7 +40,7 @@ class TestFailing(object):
|
||||
assert not f()
|
||||
|
||||
|
||||
class TestSpecialisedExplanations(object):
|
||||
class TestSpecialisedExplanations:
|
||||
def test_eq_text(self):
|
||||
assert "spam" == "eggs"
|
||||
|
||||
@@ -98,9 +96,33 @@ class TestSpecialisedExplanations(object):
|
||||
text = "head " * 50 + "f" * 70 + "tail " * 20
|
||||
assert "f" * 70 not in text
|
||||
|
||||
def test_eq_dataclass(self):
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Foo:
|
||||
a: int
|
||||
b: str
|
||||
|
||||
left = Foo(1, "b")
|
||||
right = Foo(1, "c")
|
||||
assert left == right
|
||||
|
||||
def test_eq_attrs(self):
|
||||
import attr
|
||||
|
||||
@attr.s
|
||||
class Foo:
|
||||
a = attr.ib()
|
||||
b = attr.ib()
|
||||
|
||||
left = Foo(1, "b")
|
||||
right = Foo(1, "c")
|
||||
assert left == right
|
||||
|
||||
|
||||
def test_attribute():
|
||||
class Foo(object):
|
||||
class Foo:
|
||||
b = 1
|
||||
|
||||
i = Foo()
|
||||
@@ -108,14 +130,14 @@ def test_attribute():
|
||||
|
||||
|
||||
def test_attribute_instance():
|
||||
class Foo(object):
|
||||
class Foo:
|
||||
b = 1
|
||||
|
||||
assert Foo().b == 2
|
||||
|
||||
|
||||
def test_attribute_failure():
|
||||
class Foo(object):
|
||||
class Foo:
|
||||
def _get_b(self):
|
||||
raise Exception("Failed to get attrib")
|
||||
|
||||
@@ -126,10 +148,10 @@ def test_attribute_failure():
|
||||
|
||||
|
||||
def test_attribute_multiple():
|
||||
class Foo(object):
|
||||
class Foo:
|
||||
b = 1
|
||||
|
||||
class Bar(object):
|
||||
class Bar:
|
||||
b = 2
|
||||
|
||||
assert Foo().b == Bar().b
|
||||
@@ -139,13 +161,13 @@ def globf(x):
|
||||
return x + 1
|
||||
|
||||
|
||||
class TestRaises(object):
|
||||
class TestRaises:
|
||||
def test_raises(self):
|
||||
s = "qwe" # NOQA
|
||||
raises(TypeError, "int(s)")
|
||||
s = "qwe"
|
||||
raises(TypeError, int, s)
|
||||
|
||||
def test_raises_doesnt(self):
|
||||
raises(IOError, "int('3')")
|
||||
raises(IOError, int, "3")
|
||||
|
||||
def test_raise(self):
|
||||
raise ValueError("demo error")
|
||||
@@ -155,7 +177,7 @@ class TestRaises(object):
|
||||
|
||||
def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
|
||||
items = [1, 2, 3]
|
||||
print("items is %r" % items)
|
||||
print("items is {!r}".format(items))
|
||||
a, b = items.pop()
|
||||
|
||||
def test_some_error(self):
|
||||
@@ -168,19 +190,20 @@ class TestRaises(object):
|
||||
|
||||
# thanks to Matthew Scott for this test
|
||||
def test_dynamic_compile_shows_nicely():
|
||||
import imp
|
||||
import importlib.util
|
||||
import sys
|
||||
|
||||
src = "def foo():\n assert 1 == 0\n"
|
||||
name = "abc-123"
|
||||
module = imp.new_module(name)
|
||||
spec = importlib.util.spec_from_loader(name, loader=None)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
code = _pytest._code.compile(src, name, "exec")
|
||||
six.exec_(code, module.__dict__)
|
||||
exec(code, module.__dict__)
|
||||
sys.modules[name] = module
|
||||
module.foo()
|
||||
|
||||
|
||||
class TestMoreErrors(object):
|
||||
class TestMoreErrors:
|
||||
def test_complex_error(self):
|
||||
def f():
|
||||
return 44
|
||||
@@ -230,16 +253,16 @@ class TestMoreErrors(object):
|
||||
x = 0
|
||||
|
||||
|
||||
class TestCustomAssertMsg(object):
|
||||
class TestCustomAssertMsg:
|
||||
def test_single_line(self):
|
||||
class A(object):
|
||||
class A:
|
||||
a = 1
|
||||
|
||||
b = 2
|
||||
assert A.a == b, "A.a appears not to be b"
|
||||
|
||||
def test_multiline(self):
|
||||
class A(object):
|
||||
class A:
|
||||
a = 1
|
||||
|
||||
b = 2
|
||||
@@ -248,7 +271,7 @@ class TestCustomAssertMsg(object):
|
||||
), "A.a appears not to be b\nor does not appear to be b\none of those"
|
||||
|
||||
def test_custom_repr(self):
|
||||
class JSON(object):
|
||||
class JSON:
|
||||
a = 1
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
@@ -9,5 +9,5 @@ def test_failure_demo_fails_properly(testdir):
|
||||
failure_demo.copy(target)
|
||||
failure_demo.copy(testdir.tmpdir.join(failure_demo.basename))
|
||||
result = testdir.runpytest(target, syspathinsert=True)
|
||||
result.stdout.fnmatch_lines(["*42 failed*"])
|
||||
result.stdout.fnmatch_lines(["*44 failed*"])
|
||||
assert result.ret != 0
|
||||
|
||||
@@ -2,7 +2,7 @@ def setup_module(module):
|
||||
module.TestStateFullThing.classcount = 0
|
||||
|
||||
|
||||
class TestStateFullThing(object):
|
||||
class TestStateFullThing:
|
||||
def setup_class(cls):
|
||||
cls.classcount += 1
|
||||
|
||||
|
||||
@@ -18,16 +18,16 @@ example: specifying and selecting acceptance tests
|
||||
return AcceptFixture(request)
|
||||
|
||||
|
||||
class AcceptFixture(object):
|
||||
class AcceptFixture:
|
||||
def __init__(self, request):
|
||||
if not request.config.getoption("acceptance"):
|
||||
pytest.skip("specify -A to run acceptance tests")
|
||||
self.tmpdir = request.config.mktemp(request.function.__name__, numbered=True)
|
||||
|
||||
def run(self, cmd):
|
||||
def run(self, *cmd):
|
||||
""" called by test code to execute an acceptance test. """
|
||||
self.tmpdir.chdir()
|
||||
return py.process.cmdexec(cmd)
|
||||
return subprocess.check_output(cmd).decode()
|
||||
|
||||
|
||||
and the actual test function example:
|
||||
@@ -36,7 +36,7 @@ and the actual test function example:
|
||||
|
||||
def test_some_acceptance_aspect(accept):
|
||||
accept.tmpdir.mkdir("somesub")
|
||||
result = accept.run("ls -la")
|
||||
result = accept.run("ls", "-la")
|
||||
assert "somesub" in result
|
||||
|
||||
If you run this test without specifying a command line option
|
||||
@@ -65,7 +65,7 @@ extend the `accept example`_ by putting this in our test module:
|
||||
return arg
|
||||
|
||||
|
||||
class TestSpecialAcceptance(object):
|
||||
class TestSpecialAcceptance:
|
||||
def test_sometest(self, accept):
|
||||
assert accept.tmpdir.join("special").check()
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ def setup(request):
|
||||
setup.finalize()
|
||||
|
||||
|
||||
class CostlySetup(object):
|
||||
class CostlySetup:
|
||||
def __init__(self):
|
||||
import time
|
||||
|
||||
|
||||
38
doc/en/example/fixtures/test_fixtures_order.py
Normal file
38
doc/en/example/fixtures/test_fixtures_order.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import pytest
|
||||
|
||||
# fixtures documentation order example
|
||||
order = []
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def s1():
|
||||
order.append("s1")
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def m1():
|
||||
order.append("m1")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def f1(f3):
|
||||
order.append("f1")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def f3():
|
||||
order.append("f3")
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def a1():
|
||||
order.append("a1")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def f2():
|
||||
order.append("f2")
|
||||
|
||||
|
||||
def test_order(f1, m1, f2, s1):
|
||||
assert order == ["s1", "m1", "a1", "f3", "f1", "f2"]
|
||||
@@ -4,28 +4,40 @@
|
||||
Working with custom markers
|
||||
=================================================
|
||||
|
||||
Here are some example using the :ref:`mark` mechanism.
|
||||
Here are some examples using the :ref:`mark` mechanism.
|
||||
|
||||
.. _`mark run`:
|
||||
|
||||
Marking test functions and selecting them for a run
|
||||
----------------------------------------------------
|
||||
|
||||
You can "mark" a test function with custom metadata like this::
|
||||
You can "mark" a test function with custom metadata like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_server.py
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.webtest
|
||||
def test_send_http():
|
||||
pass # perform some webtest test for your app
|
||||
pass # perform some webtest test for your app
|
||||
|
||||
|
||||
def test_something_quick():
|
||||
pass
|
||||
|
||||
|
||||
def test_another():
|
||||
pass
|
||||
class TestClass(object):
|
||||
|
||||
|
||||
class TestClass:
|
||||
def test_method(self):
|
||||
pass
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
|
||||
You can then restrict a test run to only run tests marked with ``webtest``:
|
||||
|
||||
@@ -33,14 +45,14 @@ You can then restrict a test run to only run tests marked with ``webtest``:
|
||||
|
||||
$ pytest -v -m webtest
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items / 3 deselected
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 4 items / 3 deselected / 1 selected
|
||||
|
||||
test_server.py::test_send_http PASSED [100%]
|
||||
|
||||
================== 1 passed, 3 deselected in 0.12 seconds ==================
|
||||
===================== 1 passed, 3 deselected in 0.12s ======================
|
||||
|
||||
Or the inverse, running all tests except the webtest ones:
|
||||
|
||||
@@ -48,16 +60,16 @@ Or the inverse, running all tests except the webtest ones:
|
||||
|
||||
$ pytest -v -m "not webtest"
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items / 1 deselected
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 4 items / 1 deselected / 3 selected
|
||||
|
||||
test_server.py::test_something_quick PASSED [ 33%]
|
||||
test_server.py::test_another PASSED [ 66%]
|
||||
test_server.py::TestClass::test_method PASSED [100%]
|
||||
|
||||
================== 3 passed, 1 deselected in 0.12 seconds ==================
|
||||
===================== 3 passed, 1 deselected in 0.12s ======================
|
||||
|
||||
Selecting tests based on their node ID
|
||||
--------------------------------------
|
||||
@@ -70,14 +82,14 @@ tests based on their module, class, method, or function name:
|
||||
|
||||
$ pytest -v test_server.py::TestClass::test_method
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 1 item
|
||||
|
||||
test_server.py::TestClass::test_method PASSED [100%]
|
||||
|
||||
========================= 1 passed in 0.12 seconds =========================
|
||||
============================ 1 passed in 0.12s =============================
|
||||
|
||||
You can also select on the class:
|
||||
|
||||
@@ -85,14 +97,14 @@ You can also select on the class:
|
||||
|
||||
$ pytest -v test_server.py::TestClass
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 1 item
|
||||
|
||||
test_server.py::TestClass::test_method PASSED [100%]
|
||||
|
||||
========================= 1 passed in 0.12 seconds =========================
|
||||
============================ 1 passed in 0.12s =============================
|
||||
|
||||
Or select multiple nodes:
|
||||
|
||||
@@ -100,15 +112,15 @@ Or select multiple nodes:
|
||||
|
||||
$ pytest -v test_server.py::TestClass test_server.py::test_send_http
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 2 items
|
||||
|
||||
test_server.py::TestClass::test_method PASSED [ 50%]
|
||||
test_server.py::test_send_http PASSED [100%]
|
||||
|
||||
========================= 2 passed in 0.12 seconds =========================
|
||||
============================ 2 passed in 0.12s =============================
|
||||
|
||||
.. _node-id:
|
||||
|
||||
@@ -140,14 +152,14 @@ select tests based on their names:
|
||||
|
||||
$ pytest -v -k http # running with the above defined example module
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items / 3 deselected
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 4 items / 3 deselected / 1 selected
|
||||
|
||||
test_server.py::test_send_http PASSED [100%]
|
||||
|
||||
================== 1 passed, 3 deselected in 0.12 seconds ==================
|
||||
===================== 1 passed, 3 deselected in 0.12s ======================
|
||||
|
||||
And you can also run all tests except the ones that match the keyword:
|
||||
|
||||
@@ -155,16 +167,16 @@ And you can also run all tests except the ones that match the keyword:
|
||||
|
||||
$ pytest -k "not send_http" -v
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items / 1 deselected
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 4 items / 1 deselected / 3 selected
|
||||
|
||||
test_server.py::test_something_quick PASSED [ 33%]
|
||||
test_server.py::test_another PASSED [ 66%]
|
||||
test_server.py::TestClass::test_method PASSED [100%]
|
||||
|
||||
================== 3 passed, 1 deselected in 0.12 seconds ==================
|
||||
===================== 3 passed, 1 deselected in 0.12s ======================
|
||||
|
||||
Or to select "http" and "quick" tests:
|
||||
|
||||
@@ -172,15 +184,15 @@ Or to select "http" and "quick" tests:
|
||||
|
||||
$ pytest -k "http or quick" -v
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items / 2 deselected
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 4 items / 2 deselected / 2 selected
|
||||
|
||||
test_server.py::test_send_http PASSED [ 50%]
|
||||
test_server.py::test_something_quick PASSED [100%]
|
||||
|
||||
================== 2 passed, 2 deselected in 0.12 seconds ==================
|
||||
===================== 2 passed, 2 deselected in 0.12s ======================
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -200,18 +212,22 @@ Or to select "http" and "quick" tests:
|
||||
Registering markers
|
||||
-------------------------------------
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
|
||||
.. ini-syntax for custom markers:
|
||||
|
||||
Registering markers for your test suite is simple::
|
||||
Registering markers for your test suite is simple:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# content of pytest.ini
|
||||
[pytest]
|
||||
markers =
|
||||
webtest: mark a test as a webtest.
|
||||
|
||||
You can ask which markers exist for your test suite - the list includes our just defined ``webtest`` markers::
|
||||
You can ask which markers exist for your test suite - the list includes our just defined ``webtest`` markers:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
$ pytest --markers
|
||||
@pytest.mark.webtest: mark a test as a webtest.
|
||||
@@ -245,7 +261,7 @@ For an example on how to add and work with markers from a plugin, see
|
||||
* Asking for existing markers via ``pytest --markers`` gives good output
|
||||
|
||||
* Typos in function markers are treated as an error if you use
|
||||
the ``--strict`` option.
|
||||
the ``--strict-markers`` option.
|
||||
|
||||
.. _`scoped-marking`:
|
||||
|
||||
@@ -253,33 +269,43 @@ Marking whole classes or modules
|
||||
----------------------------------------------------
|
||||
|
||||
You may use ``pytest.mark`` decorators with classes to apply markers to all of
|
||||
its test methods::
|
||||
its test methods:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_mark_classlevel.py
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.webtest
|
||||
class TestClass(object):
|
||||
class TestClass:
|
||||
def test_startup(self):
|
||||
pass
|
||||
|
||||
def test_startup_and_more(self):
|
||||
pass
|
||||
|
||||
This is equivalent to directly applying the decorator to the
|
||||
two test functions.
|
||||
|
||||
To remain backward-compatible with Python 2.4 you can also set a
|
||||
``pytestmark`` attribute on a TestClass like this::
|
||||
Due to legacy reasons, it is possible to set the ``pytestmark`` attribute on a TestClass like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import pytest
|
||||
|
||||
class TestClass(object):
|
||||
|
||||
class TestClass:
|
||||
pytestmark = pytest.mark.webtest
|
||||
|
||||
or if you need to use multiple markers you can use a list::
|
||||
or if you need to use multiple markers you can use a list:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import pytest
|
||||
|
||||
class TestClass(object):
|
||||
|
||||
class TestClass:
|
||||
pytestmark = [pytest.mark.webtest, pytest.mark.slowtest]
|
||||
|
||||
You can also set a module level marker::
|
||||
@@ -301,32 +327,24 @@ Marking individual tests when using parametrize
|
||||
|
||||
When using parametrize, applying a mark will make it apply
|
||||
to each individual test. However it is also possible to
|
||||
apply a marker to an individual test instance::
|
||||
apply a marker to an individual test instance:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.foo
|
||||
@pytest.mark.parametrize(("n", "expected"), [
|
||||
(1, 2),
|
||||
pytest.mark.bar((1, 3)),
|
||||
(2, 3),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
("n", "expected"), [(1, 2), pytest.param(1, 3, marks=pytest.mark.bar), (2, 3)]
|
||||
)
|
||||
def test_increment(n, expected):
|
||||
assert n + 1 == expected
|
||||
assert n + 1 == expected
|
||||
|
||||
In this example the mark "foo" will apply to each of the three
|
||||
tests, whereas the "bar" mark is only applied to the second test.
|
||||
Skip and xfail marks can also be applied in this way, see :ref:`skip/xfail with parametrize`.
|
||||
|
||||
.. note::
|
||||
|
||||
If the data you are parametrizing happen to be single callables, you need to be careful
|
||||
when marking these items. ``pytest.mark.xfail(my_func)`` won't work because it's also the
|
||||
signature of a function being decorated. To resolve this ambiguity, you need to pass a
|
||||
reason argument:
|
||||
``pytest.mark.xfail(func_bar, reason="Issue#7")``.
|
||||
|
||||
|
||||
.. _`adding a custom marker from a plugin`:
|
||||
|
||||
Custom marker and command line option to control test runs
|
||||
@@ -337,31 +355,46 @@ Custom marker and command line option to control test runs
|
||||
Plugins can provide custom markers and implement specific behaviour
|
||||
based on it. This is a self-contained example which adds a command
|
||||
line option and a parametrized test function marker to run tests
|
||||
specifies via named environments::
|
||||
specifies via named environments:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("-E", action="store", metavar="NAME",
|
||||
help="only run tests matching the environment NAME.")
|
||||
parser.addoption(
|
||||
"-E",
|
||||
action="store",
|
||||
metavar="NAME",
|
||||
help="only run tests matching the environment NAME.",
|
||||
)
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
# register an additional marker
|
||||
config.addinivalue_line("markers",
|
||||
"env(name): mark test to run only on named environment")
|
||||
config.addinivalue_line(
|
||||
"markers", "env(name): mark test to run only on named environment"
|
||||
)
|
||||
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
envnames = [mark.args[0] for mark in item.iter_markers(name='env')]
|
||||
envnames = [mark.args[0] for mark in item.iter_markers(name="env")]
|
||||
if envnames:
|
||||
if item.config.getoption("-E") not in envnames:
|
||||
pytest.skip("test requires env in %r" % envnames)
|
||||
pytest.skip("test requires env in {!r}".format(envnames))
|
||||
|
||||
A test file using this local plugin::
|
||||
A test file using this local plugin:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_someenv.py
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.env("stage1")
|
||||
def test_basic_db_operation():
|
||||
pass
|
||||
@@ -373,13 +406,14 @@ the test needs:
|
||||
|
||||
$ pytest -E stage2
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 1 item
|
||||
|
||||
test_someenv.py s [100%]
|
||||
|
||||
======================== 1 skipped in 0.12 seconds =========================
|
||||
============================ 1 skipped in 0.12s ============================
|
||||
|
||||
and here is one that specifies exactly the environment needed:
|
||||
|
||||
@@ -387,15 +421,18 @@ and here is one that specifies exactly the environment needed:
|
||||
|
||||
$ pytest -E stage1
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 1 item
|
||||
|
||||
test_someenv.py . [100%]
|
||||
|
||||
========================= 1 passed in 0.12 seconds =========================
|
||||
============================ 1 passed in 0.12s =============================
|
||||
|
||||
The ``--markers`` option always gives you a list of available markers::
|
||||
The ``--markers`` option always gives you a list of available markers:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
$ pytest --markers
|
||||
@pytest.mark.env(name): mark test to run only on named environment
|
||||
@@ -424,25 +461,32 @@ Passing a callable to custom markers
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
Below is the config file that will be used in the next examples::
|
||||
Below is the config file that will be used in the next examples:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
import sys
|
||||
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
for marker in item.iter_markers(name='my_marker'):
|
||||
for marker in item.iter_markers(name="my_marker"):
|
||||
print(marker)
|
||||
sys.stdout.flush()
|
||||
|
||||
A custom marker can have its argument set, i.e. ``args`` and ``kwargs`` properties, defined by either invoking it as a callable or using ``pytest.mark.MARKER_NAME.with_args``. These two methods achieve the same effect most of the time.
|
||||
|
||||
However, if there is a callable as the single positional argument with no keyword arguments, using the ``pytest.mark.MARKER_NAME(c)`` will not pass ``c`` as a positional argument but decorate ``c`` with the custom marker (see :ref:`MarkDecorator <mark>`). Fortunately, ``pytest.mark.MARKER_NAME.with_args`` comes to the rescue::
|
||||
However, if there is a callable as the single positional argument with no keyword arguments, using the ``pytest.mark.MARKER_NAME(c)`` will not pass ``c`` as a positional argument but decorate ``c`` with the custom marker (see :ref:`MarkDecorator <mark>`). Fortunately, ``pytest.mark.MARKER_NAME.with_args`` comes to the rescue:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_custom_marker.py
|
||||
import pytest
|
||||
|
||||
|
||||
def hello_world(*args, **kwargs):
|
||||
return 'Hello World'
|
||||
return "Hello World"
|
||||
|
||||
|
||||
@pytest.mark.my_marker.with_args(hello_world)
|
||||
def test_with_args():
|
||||
@@ -455,7 +499,7 @@ The output is as follows:
|
||||
$ pytest -q -s
|
||||
Mark(name='my_marker', args=(<function hello_world at 0xdeadbeef>,), kwargs={})
|
||||
.
|
||||
1 passed in 0.12 seconds
|
||||
1 passed in 0.01s
|
||||
|
||||
We can see that the custom marker has its argument set extended with the function ``hello_world``. This is the key difference between creating a custom marker as a callable, which invokes ``__call__`` behind the scenes, and using ``with_args``.
|
||||
|
||||
@@ -468,27 +512,34 @@ Reading markers which were set from multiple places
|
||||
.. regendoc:wipe
|
||||
|
||||
If you are heavily using markers in your test suite you may encounter the case where a marker is applied several times to a test function. From plugin
|
||||
code you can read over all such settings. Example::
|
||||
code you can read over all such settings. Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_mark_three_times.py
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.glob("module", x=1)
|
||||
|
||||
|
||||
@pytest.mark.glob("class", x=2)
|
||||
class TestClass(object):
|
||||
class TestClass:
|
||||
@pytest.mark.glob("function", x=3)
|
||||
def test_something(self):
|
||||
pass
|
||||
|
||||
Here we have the marker "glob" applied three times to the same
|
||||
test function. From a conftest file we can read it like this::
|
||||
test function. From a conftest file we can read it like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
import sys
|
||||
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
for mark in item.iter_markers(name='glob'):
|
||||
print("glob args=%s kwargs=%s" % (mark.args, mark.kwargs))
|
||||
for mark in item.iter_markers(name="glob"):
|
||||
print("glob args={} kwargs={}".format(mark.args, mark.kwargs))
|
||||
sys.stdout.flush()
|
||||
|
||||
Let's run this without capturing output and see what we get:
|
||||
@@ -500,7 +551,7 @@ Let's run this without capturing output and see what we get:
|
||||
glob args=('class',) kwargs={'x': 2}
|
||||
glob args=('module',) kwargs={'x': 1}
|
||||
.
|
||||
1 passed in 0.12 seconds
|
||||
1 passed in 0.02s
|
||||
|
||||
marking platform specific tests with pytest
|
||||
--------------------------------------------------------------
|
||||
@@ -511,7 +562,9 @@ Consider you have a test suite which marks tests for particular platforms,
|
||||
namely ``pytest.mark.darwin``, ``pytest.mark.win32`` etc. and you
|
||||
also have tests that run on all platforms and have no specific
|
||||
marker. If you now want to have a way to only run the tests
|
||||
for your particular platform, you could use the following plugin::
|
||||
for your particular platform, you could use the following plugin:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
#
|
||||
@@ -520,31 +573,38 @@ for your particular platform, you could use the following plugin::
|
||||
|
||||
ALL = set("darwin linux win32".split())
|
||||
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
supported_platforms = ALL.intersection(mark.name for mark in item.iter_markers())
|
||||
plat = sys.platform
|
||||
if supported_platforms and plat not in supported_platforms:
|
||||
pytest.skip("cannot run on platform %s" % (plat))
|
||||
pytest.skip("cannot run on platform {}".format(plat))
|
||||
|
||||
then tests will be skipped if they were specified for a different platform.
|
||||
Let's do a little test file to show how this looks like::
|
||||
Let's do a little test file to show how this looks like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_plat.py
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.darwin
|
||||
def test_if_apple_is_evil():
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.linux
|
||||
def test_if_linux_works():
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.win32
|
||||
def test_if_win32_crashes():
|
||||
pass
|
||||
|
||||
|
||||
def test_runs_everywhere():
|
||||
pass
|
||||
|
||||
@@ -554,15 +614,16 @@ then you will see two tests skipped and two executed tests as expected:
|
||||
|
||||
$ pytest -rs # this option reports skip reasons
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 4 items
|
||||
|
||||
test_plat.py s.s. [100%]
|
||||
========================= short test summary info ==========================
|
||||
SKIP [2] $REGENDOC_TMPDIR/conftest.py:12: cannot run on platform linux
|
||||
|
||||
=================== 2 passed, 2 skipped in 0.12 seconds ====================
|
||||
========================= short test summary info ==========================
|
||||
SKIPPED [2] $REGENDOC_TMPDIR/conftest.py:13: cannot run on platform linux
|
||||
======================= 2 passed, 2 skipped in 0.12s =======================
|
||||
|
||||
Note that if you specify a platform via the marker-command line option like this:
|
||||
|
||||
@@ -570,13 +631,14 @@ Note that if you specify a platform via the marker-command line option like this
|
||||
|
||||
$ pytest -m linux
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items / 3 deselected
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 4 items / 3 deselected / 1 selected
|
||||
|
||||
test_plat.py . [100%]
|
||||
|
||||
================== 1 passed, 3 deselected in 0.12 seconds ==================
|
||||
===================== 1 passed, 3 deselected in 0.12s ======================
|
||||
|
||||
then the unmarked-tests will not be run. It is thus a way to restrict the run to the specific tests.
|
||||
|
||||
@@ -588,28 +650,38 @@ Automatically adding markers based on test names
|
||||
If you a test suite where test function names indicate a certain
|
||||
type of test, you can implement a hook that automatically defines
|
||||
markers so that you can use the ``-m`` option with it. Let's look
|
||||
at this test module::
|
||||
at this test module:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_module.py
|
||||
|
||||
|
||||
def test_interface_simple():
|
||||
assert 0
|
||||
|
||||
|
||||
def test_interface_complex():
|
||||
assert 0
|
||||
|
||||
|
||||
def test_event_simple():
|
||||
assert 0
|
||||
|
||||
|
||||
def test_something_else():
|
||||
assert 0
|
||||
|
||||
We want to dynamically define two markers and can do it in a
|
||||
``conftest.py`` plugin::
|
||||
``conftest.py`` plugin:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(items):
|
||||
for item in items:
|
||||
if "interface" in item.nodeid:
|
||||
@@ -623,22 +695,23 @@ We can now use the ``-m option`` to select one set:
|
||||
|
||||
$ pytest -m interface --tb=short
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items / 2 deselected
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 4 items / 2 deselected / 2 selected
|
||||
|
||||
test_module.py FF [100%]
|
||||
|
||||
================================= FAILURES =================================
|
||||
__________________________ test_interface_simple ___________________________
|
||||
test_module.py:3: in test_interface_simple
|
||||
test_module.py:4: in test_interface_simple
|
||||
assert 0
|
||||
E assert 0
|
||||
__________________________ test_interface_complex __________________________
|
||||
test_module.py:6: in test_interface_complex
|
||||
test_module.py:8: in test_interface_complex
|
||||
assert 0
|
||||
E assert 0
|
||||
================== 2 failed, 2 deselected in 0.12 seconds ==================
|
||||
===================== 2 failed, 2 deselected in 0.12s ======================
|
||||
|
||||
or to select both "event" and "interface" tests:
|
||||
|
||||
@@ -646,23 +719,24 @@ or to select both "event" and "interface" tests:
|
||||
|
||||
$ pytest -m "interface or event" --tb=short
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items / 1 deselected
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 4 items / 1 deselected / 3 selected
|
||||
|
||||
test_module.py FFF [100%]
|
||||
|
||||
================================= FAILURES =================================
|
||||
__________________________ test_interface_simple ___________________________
|
||||
test_module.py:3: in test_interface_simple
|
||||
test_module.py:4: in test_interface_simple
|
||||
assert 0
|
||||
E assert 0
|
||||
__________________________ test_interface_complex __________________________
|
||||
test_module.py:6: in test_interface_complex
|
||||
test_module.py:8: in test_interface_complex
|
||||
assert 0
|
||||
E assert 0
|
||||
____________________________ test_event_simple _____________________________
|
||||
test_module.py:9: in test_event_simple
|
||||
test_module.py:12: in test_event_simple
|
||||
assert 0
|
||||
E assert 0
|
||||
================== 3 failed, 1 deselected in 0.12 seconds ==================
|
||||
===================== 3 failed, 1 deselected in 0.12s ======================
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
module containing a parametrized tests testing cross-python
|
||||
serialization via the pickle module.
|
||||
"""
|
||||
import shutil
|
||||
import subprocess
|
||||
import textwrap
|
||||
|
||||
import py
|
||||
|
||||
import pytest
|
||||
|
||||
pythonlist = ["python2.7", "python3.4", "python3.5"]
|
||||
pythonlist = ["python3.5", "python3.6", "python3.7"]
|
||||
|
||||
|
||||
@pytest.fixture(params=pythonlist)
|
||||
@@ -22,9 +22,9 @@ def python2(request, python1):
|
||||
return Python(request.param, python1.picklefile)
|
||||
|
||||
|
||||
class Python(object):
|
||||
class Python:
|
||||
def __init__(self, version, picklefile):
|
||||
self.pythonpath = py.path.local.sysfind(version)
|
||||
self.pythonpath = shutil.which(version)
|
||||
if not self.pythonpath:
|
||||
pytest.skip("{!r} not found".format(version))
|
||||
self.picklefile = picklefile
|
||||
@@ -43,7 +43,7 @@ class Python(object):
|
||||
)
|
||||
)
|
||||
)
|
||||
py.process.cmdexec("{} {}".format(self.pythonpath, dumpfile))
|
||||
subprocess.check_call((self.pythonpath, str(dumpfile)))
|
||||
|
||||
def load_and_is_true(self, expression):
|
||||
loadfile = self.picklefile.dirpath("load.py")
|
||||
@@ -63,10 +63,10 @@ class Python(object):
|
||||
)
|
||||
)
|
||||
print(loadfile)
|
||||
py.process.cmdexec("{} {}".format(self.pythonpath, loadfile))
|
||||
subprocess.check_call((self.pythonpath, str(loadfile)))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("obj", [42, {}, {1: 3}])
|
||||
def test_basic_objects(python1, python2, obj):
|
||||
python1.dumps(obj)
|
||||
python2.load_and_is_true("obj == %s" % obj)
|
||||
python2.load_and_is_true("obj == {}".format(obj))
|
||||
|
||||
@@ -12,14 +12,14 @@ A basic example for specifying tests in Yaml files
|
||||
.. _`pytest-yamlwsgi`: http://bitbucket.org/aafshar/pytest-yamlwsgi/src/tip/pytest_yamlwsgi.py
|
||||
.. _`PyYAML`: https://pypi.org/project/PyYAML/
|
||||
|
||||
Here is an example ``conftest.py`` (extracted from Ali Afshnars special purpose `pytest-yamlwsgi`_ plugin). This ``conftest.py`` will collect ``test*.yml`` files and will execute the yaml-formatted content as custom tests:
|
||||
Here is an example ``conftest.py`` (extracted from Ali Afshnars special purpose `pytest-yamlwsgi`_ plugin). This ``conftest.py`` will collect ``test*.yaml`` files and will execute the yaml-formatted content as custom tests:
|
||||
|
||||
.. include:: nonpython/conftest.py
|
||||
:literal:
|
||||
|
||||
You can create a simple example file:
|
||||
|
||||
.. include:: nonpython/test_simple.yml
|
||||
.. include:: nonpython/test_simple.yaml
|
||||
:literal:
|
||||
|
||||
and if you installed `PyYAML`_ or a compatible YAML-parser you can
|
||||
@@ -27,20 +27,21 @@ now execute the test specification:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
nonpython $ pytest test_simple.yml
|
||||
nonpython $ pytest test_simple.yaml
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR/nonpython
|
||||
collected 2 items
|
||||
|
||||
test_simple.yml F. [100%]
|
||||
test_simple.yaml F. [100%]
|
||||
|
||||
================================= FAILURES =================================
|
||||
______________________________ usecase: hello ______________________________
|
||||
usecase execution failed
|
||||
spec failed: 'some': 'other'
|
||||
no further details known at this point.
|
||||
==================== 1 failed, 1 passed in 0.12 seconds ====================
|
||||
======================= 1 failed, 1 passed in 0.12s ========================
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
@@ -63,20 +64,20 @@ consulted when reporting in ``verbose`` mode:
|
||||
|
||||
nonpython $ pytest -v
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR/nonpython
|
||||
collecting ... collected 2 items
|
||||
|
||||
test_simple.yml::hello FAILED [ 50%]
|
||||
test_simple.yml::ok PASSED [100%]
|
||||
test_simple.yaml::hello FAILED [ 50%]
|
||||
test_simple.yaml::ok PASSED [100%]
|
||||
|
||||
================================= FAILURES =================================
|
||||
______________________________ usecase: hello ______________________________
|
||||
usecase execution failed
|
||||
spec failed: 'some': 'other'
|
||||
no further details known at this point.
|
||||
==================== 1 failed, 1 passed in 0.12 seconds ====================
|
||||
======================= 1 failed, 1 passed in 0.12s ========================
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
@@ -87,12 +88,13 @@ interesting to just look at the collection tree:
|
||||
|
||||
nonpython $ pytest --collect-only
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR/nonpython
|
||||
collected 2 items
|
||||
<Package '$REGENDOC_TMPDIR/nonpython'>
|
||||
<YamlFile 'test_simple.yml'>
|
||||
<YamlItem 'hello'>
|
||||
<YamlItem 'ok'>
|
||||
<Package $REGENDOC_TMPDIR/nonpython>
|
||||
<YamlFile test_simple.yaml>
|
||||
<YamlItem hello>
|
||||
<YamlItem ok>
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
@@ -3,7 +3,7 @@ import pytest
|
||||
|
||||
|
||||
def pytest_collect_file(parent, path):
|
||||
if path.ext == ".yml" and path.basename.startswith("test"):
|
||||
if path.ext == ".yaml" and path.basename.startswith("test"):
|
||||
return YamlFile(path, parent)
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class YamlFile(pytest.File):
|
||||
|
||||
class YamlItem(pytest.Item):
|
||||
def __init__(self, name, parent, spec):
|
||||
super(YamlItem, self).__init__(name, parent)
|
||||
super().__init__(name, parent)
|
||||
self.spec = spec
|
||||
|
||||
def runtest(self):
|
||||
@@ -33,13 +33,13 @@ class YamlItem(pytest.Item):
|
||||
return "\n".join(
|
||||
[
|
||||
"usecase execution failed",
|
||||
" spec failed: %r: %r" % excinfo.value.args[1:3],
|
||||
" spec failed: {1!r}: {2!r}".format(*excinfo.value.args),
|
||||
" no further details known at this point.",
|
||||
]
|
||||
)
|
||||
|
||||
def reportinfo(self):
|
||||
return self.fspath, 0, "usecase: %s" % self.name
|
||||
return self.fspath, 0, "usecase: {}".format(self.name)
|
||||
|
||||
|
||||
class YamlException(Exception):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# test_simple.yml
|
||||
# test_simple.yaml
|
||||
ok:
|
||||
sub1: sub1
|
||||
|
||||
@@ -19,24 +19,30 @@ Generating parameters combinations, depending on command line
|
||||
|
||||
Let's say we want to execute a test with different computation
|
||||
parameters and the parameter range shall be determined by a command
|
||||
line argument. Let's first write a simple (do-nothing) computation test::
|
||||
line argument. Let's first write a simple (do-nothing) computation test:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_compute.py
|
||||
|
||||
|
||||
def test_compute(param1):
|
||||
assert param1 < 4
|
||||
|
||||
Now we add a test configuration like this::
|
||||
Now we add a test configuration like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--all", action="store_true",
|
||||
help="run all combinations")
|
||||
parser.addoption("--all", action="store_true", help="run all combinations")
|
||||
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
if 'param1' in metafunc.fixturenames:
|
||||
if metafunc.config.getoption('all'):
|
||||
if "param1" in metafunc.fixturenames:
|
||||
if metafunc.config.getoption("all"):
|
||||
end = 5
|
||||
else:
|
||||
end = 2
|
||||
@@ -48,7 +54,7 @@ This means that we only run 2 tests if we do not pass ``--all``:
|
||||
|
||||
$ pytest -q test_compute.py
|
||||
.. [100%]
|
||||
2 passed in 0.12 seconds
|
||||
2 passed in 0.01s
|
||||
|
||||
We run only two computations, so we see two dots.
|
||||
let's run the full monty:
|
||||
@@ -66,8 +72,8 @@ let's run the full monty:
|
||||
> assert param1 < 4
|
||||
E assert 4 < 4
|
||||
|
||||
test_compute.py:3: AssertionError
|
||||
1 failed, 4 passed in 0.12 seconds
|
||||
test_compute.py:4: AssertionError
|
||||
1 failed, 4 passed in 0.02s
|
||||
|
||||
As expected when running the full range of ``param1`` values
|
||||
we'll get an error on the last one.
|
||||
@@ -83,7 +89,9 @@ Running pytest with ``--collect-only`` will show the generated IDs.
|
||||
|
||||
Numbers, strings, booleans and None will have their usual string representation
|
||||
used in the test ID. For other objects, pytest will make a string based on
|
||||
the argument name::
|
||||
the argument name:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_time.py
|
||||
|
||||
@@ -112,7 +120,7 @@ the argument name::
|
||||
def idfn(val):
|
||||
if isinstance(val, (datetime,)):
|
||||
# note this wouldn't show any hours/minutes/seconds
|
||||
return val.strftime('%Y%m%d')
|
||||
return val.strftime("%Y%m%d")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("a,b,expected", testdata, ids=idfn)
|
||||
@@ -120,12 +128,18 @@ the argument name::
|
||||
diff = a - b
|
||||
assert diff == expected
|
||||
|
||||
@pytest.mark.parametrize("a,b,expected", [
|
||||
pytest.param(datetime(2001, 12, 12), datetime(2001, 12, 11),
|
||||
timedelta(1), id='forward'),
|
||||
pytest.param(datetime(2001, 12, 11), datetime(2001, 12, 12),
|
||||
timedelta(-1), id='backward'),
|
||||
])
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"a,b,expected",
|
||||
[
|
||||
pytest.param(
|
||||
datetime(2001, 12, 12), datetime(2001, 12, 11), timedelta(1), id="forward"
|
||||
),
|
||||
pytest.param(
|
||||
datetime(2001, 12, 11), datetime(2001, 12, 12), timedelta(-1), id="backward"
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_timedistance_v3(a, b, expected):
|
||||
diff = a - b
|
||||
assert diff == expected
|
||||
@@ -144,20 +158,21 @@ objects, they are still using the default pytest representation:
|
||||
|
||||
$ pytest test_time.py --collect-only
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 8 items
|
||||
<Module 'test_time.py'>
|
||||
<Function 'test_timedistance_v0[a0-b0-expected0]'>
|
||||
<Function 'test_timedistance_v0[a1-b1-expected1]'>
|
||||
<Function 'test_timedistance_v1[forward]'>
|
||||
<Function 'test_timedistance_v1[backward]'>
|
||||
<Function 'test_timedistance_v2[20011212-20011211-expected0]'>
|
||||
<Function 'test_timedistance_v2[20011211-20011212-expected1]'>
|
||||
<Function 'test_timedistance_v3[forward]'>
|
||||
<Function 'test_timedistance_v3[backward]'>
|
||||
<Module test_time.py>
|
||||
<Function test_timedistance_v0[a0-b0-expected0]>
|
||||
<Function test_timedistance_v0[a1-b1-expected1]>
|
||||
<Function test_timedistance_v1[forward]>
|
||||
<Function test_timedistance_v1[backward]>
|
||||
<Function test_timedistance_v2[20011212-20011211-expected0]>
|
||||
<Function test_timedistance_v2[20011211-20011212-expected1]>
|
||||
<Function test_timedistance_v3[forward]>
|
||||
<Function test_timedistance_v3[backward]>
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
In ``test_timedistance_v3``, we used ``pytest.param`` to specify the test IDs
|
||||
together with the actual data, instead of listing them separately.
|
||||
@@ -170,10 +185,13 @@ A quick port of "testscenarios"
|
||||
Here is a quick port to run tests configured with `test scenarios`_,
|
||||
an add-on from Robert Collins for the standard unittest framework. We
|
||||
only have to work a bit to construct the correct arguments for pytest's
|
||||
:py:func:`Metafunc.parametrize`::
|
||||
:py:func:`Metafunc.parametrize`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_scenarios.py
|
||||
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
idlist = []
|
||||
argvalues = []
|
||||
@@ -181,13 +199,15 @@ only have to work a bit to construct the correct arguments for pytest's
|
||||
idlist.append(scenario[0])
|
||||
items = scenario[1].items()
|
||||
argnames = [x[0] for x in items]
|
||||
argvalues.append(([x[1] for x in items]))
|
||||
argvalues.append([x[1] for x in items])
|
||||
metafunc.parametrize(argnames, argvalues, ids=idlist, scope="class")
|
||||
|
||||
scenario1 = ('basic', {'attribute': 'value'})
|
||||
scenario2 = ('advanced', {'attribute': 'value2'})
|
||||
|
||||
class TestSampleWithScenarios(object):
|
||||
scenario1 = ("basic", {"attribute": "value"})
|
||||
scenario2 = ("advanced", {"attribute": "value2"})
|
||||
|
||||
|
||||
class TestSampleWithScenarios:
|
||||
scenarios = [scenario1, scenario2]
|
||||
|
||||
def test_demo1(self, attribute):
|
||||
@@ -202,13 +222,14 @@ this is a fully self-contained example which you can run with:
|
||||
|
||||
$ pytest test_scenarios.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 4 items
|
||||
|
||||
test_scenarios.py .... [100%]
|
||||
|
||||
========================= 4 passed in 0.12 seconds =========================
|
||||
============================ 4 passed in 0.12s =============================
|
||||
|
||||
If you just collect tests you'll also nicely see 'advanced' and 'basic' as variants for the test function:
|
||||
|
||||
@@ -216,17 +237,18 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
|
||||
|
||||
$ pytest --collect-only test_scenarios.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 4 items
|
||||
<Module 'test_scenarios.py'>
|
||||
<Class 'TestSampleWithScenarios'>
|
||||
<Function 'test_demo1[basic]'>
|
||||
<Function 'test_demo2[basic]'>
|
||||
<Function 'test_demo1[advanced]'>
|
||||
<Function 'test_demo2[advanced]'>
|
||||
<Module test_scenarios.py>
|
||||
<Class TestSampleWithScenarios>
|
||||
<Function test_demo1[basic]>
|
||||
<Function test_demo2[basic]>
|
||||
<Function test_demo1[advanced]>
|
||||
<Function test_demo2[advanced]>
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
Note that we told ``metafunc.parametrize()`` that your scenario values
|
||||
should be considered class-scoped. With pytest-2.3 this leads to a
|
||||
@@ -240,12 +262,16 @@ Deferring the setup of parametrized resources
|
||||
The parametrization of test functions happens at collection
|
||||
time. It is a good idea to setup expensive resources like DB
|
||||
connections or subprocess only when the actual test is run.
|
||||
Here is a simple example how you can achieve that, first
|
||||
the actual test requiring a ``db`` object::
|
||||
Here is a simple example how you can achieve that. This test
|
||||
requires a ``db`` object fixture:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_backends.py
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def test_db_initialized(db):
|
||||
# a dummy test
|
||||
if db.__class__.__name__ == "DB2":
|
||||
@@ -253,20 +279,27 @@ the actual test requiring a ``db`` object::
|
||||
|
||||
We can now add a test configuration that generates two invocations of
|
||||
the ``test_db_initialized`` function and also implements a factory that
|
||||
creates a database object for the actual test invocations::
|
||||
creates a database object for the actual test invocations:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
import pytest
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
if 'db' in metafunc.fixturenames:
|
||||
metafunc.parametrize("db", ['d1', 'd2'], indirect=True)
|
||||
|
||||
class DB1(object):
|
||||
def pytest_generate_tests(metafunc):
|
||||
if "db" in metafunc.fixturenames:
|
||||
metafunc.parametrize("db", ["d1", "d2"], indirect=True)
|
||||
|
||||
|
||||
class DB1:
|
||||
"one database object"
|
||||
class DB2(object):
|
||||
|
||||
|
||||
class DB2:
|
||||
"alternative database object"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db(request):
|
||||
if request.param == "d1":
|
||||
@@ -282,14 +315,15 @@ Let's first see how it looks like at collection time:
|
||||
|
||||
$ pytest test_backends.py --collect-only
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 2 items
|
||||
<Module 'test_backends.py'>
|
||||
<Function 'test_db_initialized[d1]'>
|
||||
<Function 'test_db_initialized[d2]'>
|
||||
<Module test_backends.py>
|
||||
<Function test_db_initialized[d1]>
|
||||
<Function test_db_initialized[d2]>
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
And then when we run the test:
|
||||
|
||||
@@ -308,8 +342,8 @@ And then when we run the test:
|
||||
> pytest.fail("deliberately failing for demo purposes")
|
||||
E Failed: deliberately failing for demo purposes
|
||||
|
||||
test_backends.py:6: Failed
|
||||
1 failed, 1 passed in 0.12 seconds
|
||||
test_backends.py:8: Failed
|
||||
1 failed, 1 passed in 0.02s
|
||||
|
||||
The first invocation with ``db == "DB1"`` passed while the second with ``db == "DB2"`` failed. Our ``db`` fixture function has instantiated each of the DB values during the setup phase while the ``pytest_generate_tests`` generated two according calls to the ``test_db_initialized`` during the collection phase.
|
||||
|
||||
@@ -323,23 +357,29 @@ parameter on particular arguments. It can be done by passing list or tuple of
|
||||
arguments' names to ``indirect``. In the example below there is a function ``test_indirect`` which uses
|
||||
two fixtures: ``x`` and ``y``. Here we give to indirect the list, which contains the name of the
|
||||
fixture ``x``. The indirect parameter will be applied to this argument only, and the value ``a``
|
||||
will be passed to respective fixture function::
|
||||
will be passed to respective fixture function:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_indirect_list.py
|
||||
|
||||
import pytest
|
||||
@pytest.fixture(scope='function')
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def x(request):
|
||||
return request.param * 3
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def y(request):
|
||||
return request.param * 2
|
||||
|
||||
@pytest.mark.parametrize('x, y', [('a', 'b')], indirect=['x'])
|
||||
def test_indirect(x,y):
|
||||
assert x == 'aaa'
|
||||
assert y == 'b'
|
||||
|
||||
@pytest.mark.parametrize("x, y", [("a", "b")], indirect=["x"])
|
||||
def test_indirect(x, y):
|
||||
assert x == "aaa"
|
||||
assert y == "b"
|
||||
|
||||
The result of this test will be successful:
|
||||
|
||||
@@ -347,13 +387,14 @@ The result of this test will be successful:
|
||||
|
||||
$ pytest test_indirect_list.py --collect-only
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 1 item
|
||||
<Module 'test_indirect_list.py'>
|
||||
<Function 'test_indirect[a-b]'>
|
||||
<Module test_indirect_list.py>
|
||||
<Function test_indirect[a-b]>
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
@@ -365,30 +406,36 @@ Parametrizing test methods through per-class configuration
|
||||
|
||||
Here is an example ``pytest_generate_tests`` function implementing a
|
||||
parametrization scheme similar to Michael Foord's `unittest
|
||||
parametrizer`_ but in a lot less code::
|
||||
parametrizer`_ but in a lot less code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of ./test_parametrize.py
|
||||
import pytest
|
||||
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
# called once per each test function
|
||||
funcarglist = metafunc.cls.params[metafunc.function.__name__]
|
||||
argnames = sorted(funcarglist[0])
|
||||
metafunc.parametrize(argnames, [[funcargs[name] for name in argnames]
|
||||
for funcargs in funcarglist])
|
||||
metafunc.parametrize(
|
||||
argnames, [[funcargs[name] for name in argnames] for funcargs in funcarglist]
|
||||
)
|
||||
|
||||
class TestClass(object):
|
||||
|
||||
class TestClass:
|
||||
# a map specifying multiple argument sets for a test method
|
||||
params = {
|
||||
'test_equals': [dict(a=1, b=2), dict(a=3, b=3), ],
|
||||
'test_zerodivision': [dict(a=1, b=0), ],
|
||||
"test_equals": [dict(a=1, b=2), dict(a=3, b=3)],
|
||||
"test_zerodivision": [dict(a=1, b=0)],
|
||||
}
|
||||
|
||||
def test_equals(self, a, b):
|
||||
assert a == b
|
||||
|
||||
def test_zerodivision(self, a, b):
|
||||
pytest.raises(ZeroDivisionError, "a/b")
|
||||
with pytest.raises(ZeroDivisionError):
|
||||
a / b
|
||||
|
||||
Our test generator looks up a class-level definition which specifies which
|
||||
argument sets to use for each test function. Let's run it:
|
||||
@@ -406,8 +453,8 @@ argument sets to use for each test function. Let's run it:
|
||||
> assert a == b
|
||||
E assert 1 == 2
|
||||
|
||||
test_parametrize.py:18: AssertionError
|
||||
1 failed, 2 passed in 0.12 seconds
|
||||
test_parametrize.py:21: AssertionError
|
||||
1 failed, 2 passed in 0.03s
|
||||
|
||||
Indirect parametrization with multiple fixtures
|
||||
--------------------------------------------------------------
|
||||
@@ -423,15 +470,16 @@ is to be run with different sets of arguments for its three arguments:
|
||||
|
||||
.. literalinclude:: multipython.py
|
||||
|
||||
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize):
|
||||
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (3 interpreters times 3 interpreters times 3 objects to serialize/deserialize):
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
. $ pytest -rs -q multipython.py
|
||||
...sss...sssssssss...sss... [100%]
|
||||
ssssssssssss...ssssssssssss [100%]
|
||||
========================= short test summary info ==========================
|
||||
SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:30: 'python3.4' not found
|
||||
12 passed, 15 skipped in 0.12 seconds
|
||||
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:30: 'python3.5' not found
|
||||
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:30: 'python3.7' not found
|
||||
3 passed, 24 skipped in 0.24s
|
||||
|
||||
Indirect parametrization of optional implementations/imports
|
||||
--------------------------------------------------------------------
|
||||
@@ -440,36 +488,47 @@ If you want to compare the outcomes of several implementations of a given
|
||||
API, you can write test functions that receive the already imported implementations
|
||||
and get skipped in case the implementation is not importable/available. Let's
|
||||
say we have a "base" implementation and the other (possibly optimized ones)
|
||||
need to provide similar results::
|
||||
need to provide similar results:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def basemod(request):
|
||||
return pytest.importorskip("base")
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", params=["opt1", "opt2"])
|
||||
def optmod(request):
|
||||
return pytest.importorskip(request.param)
|
||||
|
||||
And then a base implementation of a simple function::
|
||||
And then a base implementation of a simple function:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of base.py
|
||||
def func1():
|
||||
return 1
|
||||
|
||||
And an optimized version::
|
||||
And an optimized version:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of opt1.py
|
||||
def func1():
|
||||
return 1.0001
|
||||
|
||||
And finally a little test module::
|
||||
And finally a little test module:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_module.py
|
||||
|
||||
|
||||
def test_func1(basemod, optmod):
|
||||
assert round(basemod.func1(), 3) == round(optmod.func1(), 3)
|
||||
|
||||
@@ -480,15 +539,16 @@ If you run this with reporting for skips enabled:
|
||||
|
||||
$ pytest -rs test_module.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 2 items
|
||||
|
||||
test_module.py .s [100%]
|
||||
========================= short test summary info ==========================
|
||||
SKIP [1] $REGENDOC_TMPDIR/conftest.py:11: could not import 'opt2'
|
||||
|
||||
=================== 1 passed, 1 skipped in 0.12 seconds ====================
|
||||
========================= short test summary info ==========================
|
||||
SKIPPED [1] $REGENDOC_TMPDIR/conftest.py:13: could not import 'opt2': No module named 'opt2'
|
||||
======================= 1 passed, 1 skipped in 0.12s =======================
|
||||
|
||||
You'll see that we don't have an ``opt2`` module and thus the second test run
|
||||
of our ``test_func1`` was skipped. A few notes:
|
||||
@@ -508,21 +568,25 @@ Set marks or test ID for individual parametrized test
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Use ``pytest.param`` to apply marks or set test ID to individual parametrized test.
|
||||
For example::
|
||||
For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_pytest_param_example.py
|
||||
import pytest
|
||||
@pytest.mark.parametrize('test_input,expected', [
|
||||
('3+5', 8),
|
||||
pytest.param('1+7', 8,
|
||||
marks=pytest.mark.basic),
|
||||
pytest.param('2+4', 6,
|
||||
marks=pytest.mark.basic,
|
||||
id='basic_2+4'),
|
||||
pytest.param('6*9', 42,
|
||||
marks=[pytest.mark.basic, pytest.mark.xfail],
|
||||
id='basic_6*9'),
|
||||
])
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_input,expected",
|
||||
[
|
||||
("3+5", 8),
|
||||
pytest.param("1+7", 8, marks=pytest.mark.basic),
|
||||
pytest.param("2+4", 6, marks=pytest.mark.basic, id="basic_2+4"),
|
||||
pytest.param(
|
||||
"6*9", 42, marks=[pytest.mark.basic, pytest.mark.xfail], id="basic_6*9"
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_eval(test_input, expected):
|
||||
assert eval(test_input) == expected
|
||||
|
||||
@@ -537,16 +601,16 @@ Then run ``pytest`` with verbose mode and with only the ``basic`` marker:
|
||||
|
||||
$ pytest -v -m basic
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 17 items / 14 deselected
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 18 items / 15 deselected / 3 selected
|
||||
|
||||
test_pytest_param_example.py::test_eval[1+7-8] PASSED [ 33%]
|
||||
test_pytest_param_example.py::test_eval[basic_2+4] PASSED [ 66%]
|
||||
test_pytest_param_example.py::test_eval[basic_6*9] xfail [100%]
|
||||
test_pytest_param_example.py::test_eval[basic_6*9] XFAIL [100%]
|
||||
|
||||
============ 2 passed, 14 deselected, 1 xfailed in 0.12 seconds ============
|
||||
=============== 2 passed, 15 deselected, 1 xfailed in 0.12s ================
|
||||
|
||||
As the result:
|
||||
|
||||
@@ -556,3 +620,62 @@ As the result:
|
||||
- The test ``test_eval[1+7-8]`` passed, but the name is autogenerated and confusing.
|
||||
- The test ``test_eval[basic_2+4]`` passed.
|
||||
- The test ``test_eval[basic_6*9]`` was expected to fail and did fail.
|
||||
|
||||
.. _`parametrizing_conditional_raising`:
|
||||
|
||||
Parametrizing conditional raising
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Use :func:`pytest.raises` with the
|
||||
:ref:`pytest.mark.parametrize ref` decorator to write parametrized tests
|
||||
in which some tests raise exceptions and others do not.
|
||||
|
||||
It is helpful to define a no-op context manager ``does_not_raise`` to serve
|
||||
as a complement to ``raises``. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from contextlib import contextmanager
|
||||
import pytest
|
||||
|
||||
|
||||
@contextmanager
|
||||
def does_not_raise():
|
||||
yield
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"example_input,expectation",
|
||||
[
|
||||
(3, does_not_raise()),
|
||||
(2, does_not_raise()),
|
||||
(1, does_not_raise()),
|
||||
(0, pytest.raises(ZeroDivisionError)),
|
||||
],
|
||||
)
|
||||
def test_division(example_input, expectation):
|
||||
"""Test how much I know division."""
|
||||
with expectation:
|
||||
assert (6 / example_input) is not None
|
||||
|
||||
In the example above, the first three test cases should run unexceptionally,
|
||||
while the fourth should raise ``ZeroDivisionError``.
|
||||
|
||||
If you're only supporting Python 3.7+, you can simply use ``nullcontext``
|
||||
to define ``does_not_raise``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from contextlib import nullcontext as does_not_raise
|
||||
|
||||
Or, if you're supporting Python 3.3+ you can use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from contextlib import ExitStack as does_not_raise
|
||||
|
||||
Or, if desired, you can ``pip install contextlib2`` and use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from contextlib2 import ExitStack as does_not_raise
|
||||
|
||||
@@ -6,7 +6,7 @@ def test_function():
|
||||
pass
|
||||
|
||||
|
||||
class TestClass(object):
|
||||
class TestClass:
|
||||
def test_method(self):
|
||||
pass
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ Ignore paths during test collection
|
||||
|
||||
You can easily ignore certain test directories and modules during collection
|
||||
by passing the ``--ignore=path`` option on the cli. ``pytest`` allows multiple
|
||||
``--ignore`` options. Example::
|
||||
``--ignore`` options. Example:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
tests/
|
||||
|-- example
|
||||
@@ -29,7 +31,7 @@ you will see that ``pytest`` only collects test-modules, which do not match the
|
||||
.. code-block:: pytest
|
||||
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 5 items
|
||||
|
||||
@@ -41,6 +43,9 @@ you will see that ``pytest`` only collects test-modules, which do not match the
|
||||
|
||||
========================= 5 passed in 0.02 seconds =========================
|
||||
|
||||
The ``--ignore-glob`` option allows to ignore test file paths based on Unix shell-style wildcards.
|
||||
If you want to exclude test-modules that end with ``_01.py``, execute ``pytest`` with ``--ignore-glob='*_01.py'``.
|
||||
|
||||
Deselect tests during test collection
|
||||
-------------------------------------
|
||||
|
||||
@@ -54,7 +59,9 @@ Keeping duplicate paths specified from command line
|
||||
----------------------------------------------------
|
||||
|
||||
Default behavior of ``pytest`` is to ignore duplicate paths specified from the command line.
|
||||
Example::
|
||||
Example:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
pytest path_a path_a
|
||||
|
||||
@@ -65,7 +72,9 @@ Example::
|
||||
Just collect tests once.
|
||||
|
||||
To collect duplicate tests, use the ``--keep-duplicates`` option on the cli.
|
||||
Example::
|
||||
Example:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
pytest --keep-duplicates path_a path_a
|
||||
|
||||
@@ -75,7 +84,9 @@ Example::
|
||||
|
||||
As the collector just works on directories, if you specify twice a single test file, ``pytest`` will
|
||||
still collect it twice, no matter if the ``--keep-duplicates`` is not specified.
|
||||
Example::
|
||||
Example:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
pytest test_a.py test_a.py
|
||||
|
||||
@@ -87,7 +98,9 @@ Example::
|
||||
Changing directory recursion
|
||||
-----------------------------------------------------
|
||||
|
||||
You can set the :confval:`norecursedirs` option in an ini-file, for example your ``pytest.ini`` in the project root directory::
|
||||
You can set the :confval:`norecursedirs` option in an ini-file, for example your ``pytest.ini`` in the project root directory:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# content of pytest.ini
|
||||
[pytest]
|
||||
@@ -103,7 +116,9 @@ Changing naming conventions
|
||||
You can configure different naming conventions by setting
|
||||
the :confval:`python_files`, :confval:`python_classes` and
|
||||
:confval:`python_functions` configuration options.
|
||||
Here is an example::
|
||||
Here is an example:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# content of pytest.ini
|
||||
# Example 1: have pytest look for "check" instead of "test"
|
||||
@@ -116,12 +131,15 @@ Here is an example::
|
||||
|
||||
This would make ``pytest`` look for tests in files that match the ``check_*
|
||||
.py`` glob-pattern, ``Check`` prefixes in classes, and functions and methods
|
||||
that match ``*_check``. For example, if we have::
|
||||
that match ``*_check``. For example, if we have:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of check_myapp.py
|
||||
class CheckMyApp(object):
|
||||
class CheckMyApp:
|
||||
def simple_check(self):
|
||||
pass
|
||||
|
||||
def complex_check(self):
|
||||
pass
|
||||
|
||||
@@ -131,17 +149,20 @@ The test collection would look like this:
|
||||
|
||||
$ pytest --collect-only
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||
collected 2 items
|
||||
<Module 'check_myapp.py'>
|
||||
<Class 'CheckMyApp'>
|
||||
<Function 'simple_check'>
|
||||
<Function 'complex_check'>
|
||||
<Module check_myapp.py>
|
||||
<Class CheckMyApp>
|
||||
<Function simple_check>
|
||||
<Function complex_check>
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
You can check for multiple glob patterns by adding a space between the patterns::
|
||||
You can check for multiple glob patterns by adding a space between the patterns:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# Example 2: have pytest look for files with "test" and "example"
|
||||
# content of pytest.ini, tox.ini, or setup.cfg file (replace "pytest"
|
||||
@@ -161,13 +182,17 @@ Interpreting cmdline arguments as Python packages
|
||||
You can use the ``--pyargs`` option to make ``pytest`` try
|
||||
interpreting arguments as python package names, deriving
|
||||
their file system path and then running the test. For
|
||||
example if you have unittest2 installed you can type::
|
||||
example if you have unittest2 installed you can type:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --pyargs unittest2.test.test_skipping -q
|
||||
|
||||
which would run the respective test module. Like with
|
||||
other options, through an ini-file and the :confval:`addopts` option you
|
||||
can make this change more permanently::
|
||||
can make this change more permanently:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# content of pytest.ini
|
||||
[pytest]
|
||||
@@ -186,16 +211,17 @@ You can always peek at the collection tree without running tests like this:
|
||||
|
||||
. $ pytest --collect-only pythoncollection.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||
collected 3 items
|
||||
<Module 'CWD/pythoncollection.py'>
|
||||
<Function 'test_function'>
|
||||
<Class 'TestClass'>
|
||||
<Function 'test_method'>
|
||||
<Function 'test_anothermethod'>
|
||||
<Module CWD/pythoncollection.py>
|
||||
<Function test_function>
|
||||
<Class TestClass>
|
||||
<Function test_method>
|
||||
<Function test_anothermethod>
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
.. _customizing-test-collection:
|
||||
|
||||
@@ -204,7 +230,9 @@ Customizing test collection
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
You can easily instruct ``pytest`` to discover tests from every Python file::
|
||||
You can easily instruct ``pytest`` to discover tests from every Python file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# content of pytest.ini
|
||||
[pytest]
|
||||
@@ -213,7 +241,9 @@ You can easily instruct ``pytest`` to discover tests from every Python file::
|
||||
However, many projects will have a ``setup.py`` which they don't want to be
|
||||
imported. Moreover, there may files only importable by a specific python
|
||||
version. For such cases you can dynamically define files to be ignored by
|
||||
listing them in a ``conftest.py`` file::
|
||||
listing them in a ``conftest.py`` file:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
import sys
|
||||
@@ -222,7 +252,9 @@ listing them in a ``conftest.py`` file::
|
||||
if sys.version_info[0] > 2:
|
||||
collect_ignore.append("pkg/module_py2.py")
|
||||
|
||||
and then if you have a module file like this::
|
||||
and then if you have a module file like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of pkg/module_py2.py
|
||||
def test_only_on_python2():
|
||||
@@ -231,10 +263,12 @@ and then if you have a module file like this::
|
||||
except Exception, e:
|
||||
pass
|
||||
|
||||
and a ``setup.py`` dummy file like this::
|
||||
and a ``setup.py`` dummy file like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of setup.py
|
||||
0/0 # will raise exception if imported
|
||||
0 / 0 # will raise exception if imported
|
||||
|
||||
If you run with a Python 2 interpreter then you will find the one test and will
|
||||
leave out the ``setup.py`` file:
|
||||
@@ -258,8 +292,25 @@ file will be left out:
|
||||
|
||||
$ pytest --collect-only
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||
collected 0 items
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
It's also possible to ignore files based on Unix shell-style wildcards by adding
|
||||
patterns to ``collect_ignore_glob``.
|
||||
|
||||
The following example ``conftest.py`` ignores the file ``setup.py`` and in
|
||||
addition all files that end with ``*_py2.py`` when executed with a Python 3
|
||||
interpreter:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
import sys
|
||||
|
||||
collect_ignore = ["setup.py"]
|
||||
if sys.version_info[0] > 2:
|
||||
collect_ignore_glob = ["*_py2.py"]
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
|
||||
.. _`tbreportdemo`:
|
||||
|
||||
Demo of Python failure reports with pytest
|
||||
==================================================
|
||||
==========================================
|
||||
|
||||
Here is a nice run of several tens of failures
|
||||
and how ``pytest`` presents things (unfortunately
|
||||
not showing the nice colors here in the HTML that you
|
||||
get on the terminal - we are working on that):
|
||||
Here is a nice run of several failures and how ``pytest`` presents things:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
assertion $ pytest failure_demo.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR/assertion, inifile:
|
||||
collected 42 items
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR/assertion
|
||||
collected 44 items
|
||||
|
||||
failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF [100%]
|
||||
failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF [100%]
|
||||
|
||||
================================= FAILURES =================================
|
||||
___________________________ test_generative[3-6] ___________________________
|
||||
@@ -29,7 +26,7 @@ get on the terminal - we are working on that):
|
||||
> assert param1 * 2 < param2
|
||||
E assert (3 * 2) < 6
|
||||
|
||||
failure_demo.py:22: AssertionError
|
||||
failure_demo.py:20: AssertionError
|
||||
_________________________ TestFailing.test_simple __________________________
|
||||
|
||||
self = <failure_demo.TestFailing object at 0xdeadbeef>
|
||||
@@ -46,7 +43,7 @@ get on the terminal - we are working on that):
|
||||
E + where 42 = <function TestFailing.test_simple.<locals>.f at 0xdeadbeef>()
|
||||
E + and 43 = <function TestFailing.test_simple.<locals>.g at 0xdeadbeef>()
|
||||
|
||||
failure_demo.py:33: AssertionError
|
||||
failure_demo.py:31: AssertionError
|
||||
____________________ TestFailing.test_simple_multiline _____________________
|
||||
|
||||
self = <failure_demo.TestFailing object at 0xdeadbeef>
|
||||
@@ -54,7 +51,7 @@ get on the terminal - we are working on that):
|
||||
def test_simple_multiline(self):
|
||||
> otherfunc_multi(42, 6 * 9)
|
||||
|
||||
failure_demo.py:36:
|
||||
failure_demo.py:34:
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
|
||||
a = 42, b = 54
|
||||
@@ -63,7 +60,7 @@ get on the terminal - we are working on that):
|
||||
> assert a == b
|
||||
E assert 42 == 54
|
||||
|
||||
failure_demo.py:17: AssertionError
|
||||
failure_demo.py:15: AssertionError
|
||||
___________________________ TestFailing.test_not ___________________________
|
||||
|
||||
self = <failure_demo.TestFailing object at 0xdeadbeef>
|
||||
@@ -76,7 +73,7 @@ get on the terminal - we are working on that):
|
||||
E assert not 42
|
||||
E + where 42 = <function TestFailing.test_not.<locals>.f at 0xdeadbeef>()
|
||||
|
||||
failure_demo.py:42: AssertionError
|
||||
failure_demo.py:40: AssertionError
|
||||
_________________ TestSpecialisedExplanations.test_eq_text _________________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -87,7 +84,7 @@ get on the terminal - we are working on that):
|
||||
E - spam
|
||||
E + eggs
|
||||
|
||||
failure_demo.py:47: AssertionError
|
||||
failure_demo.py:45: AssertionError
|
||||
_____________ TestSpecialisedExplanations.test_eq_similar_text _____________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -100,7 +97,7 @@ get on the terminal - we are working on that):
|
||||
E + foo 2 bar
|
||||
E ? ^
|
||||
|
||||
failure_demo.py:50: AssertionError
|
||||
failure_demo.py:48: AssertionError
|
||||
____________ TestSpecialisedExplanations.test_eq_multiline_text ____________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -113,7 +110,7 @@ get on the terminal - we are working on that):
|
||||
E + eggs
|
||||
E bar
|
||||
|
||||
failure_demo.py:53: AssertionError
|
||||
failure_demo.py:51: AssertionError
|
||||
______________ TestSpecialisedExplanations.test_eq_long_text _______________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -122,7 +119,7 @@ get on the terminal - we are working on that):
|
||||
a = "1" * 100 + "a" + "2" * 100
|
||||
b = "1" * 100 + "b" + "2" * 100
|
||||
> assert a == b
|
||||
E AssertionError: assert '111111111111...2222222222222' == '1111111111111...2222222222222'
|
||||
E AssertionError: assert '111111111111...2222222222222' == '111111111111...2222222222222'
|
||||
E Skipping 90 identical leading characters in diff, use -v to show
|
||||
E Skipping 91 identical trailing characters in diff, use -v to show
|
||||
E - 1111111111a222222222
|
||||
@@ -130,7 +127,7 @@ get on the terminal - we are working on that):
|
||||
E + 1111111111b222222222
|
||||
E ? ^
|
||||
|
||||
failure_demo.py:58: AssertionError
|
||||
failure_demo.py:56: AssertionError
|
||||
_________ TestSpecialisedExplanations.test_eq_long_text_multiline __________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -139,7 +136,7 @@ get on the terminal - we are working on that):
|
||||
a = "1\n" * 100 + "a" + "2\n" * 100
|
||||
b = "1\n" * 100 + "b" + "2\n" * 100
|
||||
> assert a == b
|
||||
E AssertionError: assert '1\n1\n1\n1\n...n2\n2\n2\n2\n' == '1\n1\n1\n1\n1...n2\n2\n2\n2\n'
|
||||
E AssertionError: assert '1\n1\n1\n1\n...n2\n2\n2\n2\n' == '1\n1\n1\n1\n...n2\n2\n2\n2\n'
|
||||
E Skipping 190 identical leading characters in diff, use -v to show
|
||||
E Skipping 191 identical trailing characters in diff, use -v to show
|
||||
E 1
|
||||
@@ -150,7 +147,7 @@ get on the terminal - we are working on that):
|
||||
E
|
||||
E ...Full output truncated (7 lines hidden), use '-vv' to show
|
||||
|
||||
failure_demo.py:63: AssertionError
|
||||
failure_demo.py:61: AssertionError
|
||||
_________________ TestSpecialisedExplanations.test_eq_list _________________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -161,7 +158,7 @@ get on the terminal - we are working on that):
|
||||
E At index 2 diff: 2 != 3
|
||||
E Use -v to get the full diff
|
||||
|
||||
failure_demo.py:66: AssertionError
|
||||
failure_demo.py:64: AssertionError
|
||||
______________ TestSpecialisedExplanations.test_eq_list_long _______________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -174,7 +171,7 @@ get on the terminal - we are working on that):
|
||||
E At index 100 diff: 1 != 2
|
||||
E Use -v to get the full diff
|
||||
|
||||
failure_demo.py:71: AssertionError
|
||||
failure_demo.py:69: AssertionError
|
||||
_________________ TestSpecialisedExplanations.test_eq_dict _________________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -185,14 +182,14 @@ get on the terminal - we are working on that):
|
||||
E Omitting 1 identical items, use -vv to show
|
||||
E Differing items:
|
||||
E {'b': 1} != {'b': 2}
|
||||
E Left contains more items:
|
||||
E Left contains 1 more item:
|
||||
E {'c': 0}
|
||||
E Right contains more items:
|
||||
E Right contains 1 more item:
|
||||
E {'d': 0}...
|
||||
E
|
||||
E ...Full output truncated (2 lines hidden), use '-vv' to show
|
||||
|
||||
failure_demo.py:74: AssertionError
|
||||
failure_demo.py:72: AssertionError
|
||||
_________________ TestSpecialisedExplanations.test_eq_set __________________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -210,7 +207,7 @@ get on the terminal - we are working on that):
|
||||
E
|
||||
E ...Full output truncated (2 lines hidden), use '-vv' to show
|
||||
|
||||
failure_demo.py:77: AssertionError
|
||||
failure_demo.py:75: AssertionError
|
||||
_____________ TestSpecialisedExplanations.test_eq_longer_list ______________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -218,10 +215,10 @@ get on the terminal - we are working on that):
|
||||
def test_eq_longer_list(self):
|
||||
> assert [1, 2] == [1, 2, 3]
|
||||
E assert [1, 2] == [1, 2, 3]
|
||||
E Right contains more items, first extra item: 3
|
||||
E Right contains one more item: 3
|
||||
E Use -v to get the full diff
|
||||
|
||||
failure_demo.py:80: AssertionError
|
||||
failure_demo.py:78: AssertionError
|
||||
_________________ TestSpecialisedExplanations.test_in_list _________________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -230,7 +227,7 @@ get on the terminal - we are working on that):
|
||||
> assert 1 in [0, 2, 3, 4, 5]
|
||||
E assert 1 in [0, 2, 3, 4, 5]
|
||||
|
||||
failure_demo.py:83: AssertionError
|
||||
failure_demo.py:81: AssertionError
|
||||
__________ TestSpecialisedExplanations.test_not_in_text_multiline __________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -238,7 +235,7 @@ get on the terminal - we are working on that):
|
||||
def test_not_in_text_multiline(self):
|
||||
text = "some multiline\ntext\nwhich\nincludes foo\nand a\ntail"
|
||||
> assert "foo" not in text
|
||||
E AssertionError: assert 'foo' not in 'some multiline\ntext\nw...ncludes foo\nand a\ntail'
|
||||
E AssertionError: assert 'foo' not in 'some multil...nand a\ntail'
|
||||
E 'foo' is contained here:
|
||||
E some multiline
|
||||
E text
|
||||
@@ -249,7 +246,7 @@ get on the terminal - we are working on that):
|
||||
E
|
||||
E ...Full output truncated (2 lines hidden), use '-vv' to show
|
||||
|
||||
failure_demo.py:87: AssertionError
|
||||
failure_demo.py:85: AssertionError
|
||||
___________ TestSpecialisedExplanations.test_not_in_text_single ____________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -262,7 +259,7 @@ get on the terminal - we are working on that):
|
||||
E single foo line
|
||||
E ? +++
|
||||
|
||||
failure_demo.py:91: AssertionError
|
||||
failure_demo.py:89: AssertionError
|
||||
_________ TestSpecialisedExplanations.test_not_in_text_single_long _________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -270,12 +267,12 @@ get on the terminal - we are working on that):
|
||||
def test_not_in_text_single_long(self):
|
||||
text = "head " * 50 + "foo " + "tail " * 20
|
||||
> assert "foo" not in text
|
||||
E AssertionError: assert 'foo' not in 'head head head head hea...ail tail tail tail tail '
|
||||
E AssertionError: assert 'foo' not in 'head head h...l tail tail '
|
||||
E 'foo' is contained here:
|
||||
E head head foo tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
|
||||
E ? +++
|
||||
|
||||
failure_demo.py:95: AssertionError
|
||||
failure_demo.py:93: AssertionError
|
||||
______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
@@ -283,16 +280,58 @@ get on the terminal - we are working on that):
|
||||
def test_not_in_text_single_long_term(self):
|
||||
text = "head " * 50 + "f" * 70 + "tail " * 20
|
||||
> assert "f" * 70 not in text
|
||||
E AssertionError: assert 'fffffffffff...ffffffffffff' not in 'head head he...l tail tail '
|
||||
E AssertionError: assert 'fffffffffff...ffffffffffff' not in 'head head h...l tail tail '
|
||||
E 'ffffffffffffffffff...fffffffffffffffffff' is contained here:
|
||||
E head head fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffftail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
|
||||
E ? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
failure_demo.py:99: AssertionError
|
||||
failure_demo.py:97: AssertionError
|
||||
______________ TestSpecialisedExplanations.test_eq_dataclass _______________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
|
||||
def test_eq_dataclass(self):
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Foo:
|
||||
a: int
|
||||
b: str
|
||||
|
||||
left = Foo(1, "b")
|
||||
right = Foo(1, "c")
|
||||
> assert left == right
|
||||
E AssertionError: assert TestSpecialis...oo(a=1, b='b') == TestSpecialis...oo(a=1, b='c')
|
||||
E Omitting 1 identical items, use -vv to show
|
||||
E Differing attributes:
|
||||
E b: 'b' != 'c'
|
||||
|
||||
failure_demo.py:109: AssertionError
|
||||
________________ TestSpecialisedExplanations.test_eq_attrs _________________
|
||||
|
||||
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
|
||||
|
||||
def test_eq_attrs(self):
|
||||
import attr
|
||||
|
||||
@attr.s
|
||||
class Foo:
|
||||
a = attr.ib()
|
||||
b = attr.ib()
|
||||
|
||||
left = Foo(1, "b")
|
||||
right = Foo(1, "c")
|
||||
> assert left == right
|
||||
E AssertionError: assert Foo(a=1, b='b') == Foo(a=1, b='c')
|
||||
E Omitting 1 identical items, use -vv to show
|
||||
E Differing attributes:
|
||||
E b: 'b' != 'c'
|
||||
|
||||
failure_demo.py:121: AssertionError
|
||||
______________________________ test_attribute ______________________________
|
||||
|
||||
def test_attribute():
|
||||
class Foo(object):
|
||||
class Foo:
|
||||
b = 1
|
||||
|
||||
i = Foo()
|
||||
@@ -300,11 +339,11 @@ get on the terminal - we are working on that):
|
||||
E assert 1 == 2
|
||||
E + where 1 = <failure_demo.test_attribute.<locals>.Foo object at 0xdeadbeef>.b
|
||||
|
||||
failure_demo.py:107: AssertionError
|
||||
failure_demo.py:129: AssertionError
|
||||
_________________________ test_attribute_instance __________________________
|
||||
|
||||
def test_attribute_instance():
|
||||
class Foo(object):
|
||||
class Foo:
|
||||
b = 1
|
||||
|
||||
> assert Foo().b == 2
|
||||
@@ -312,11 +351,11 @@ get on the terminal - we are working on that):
|
||||
E + where 1 = <failure_demo.test_attribute_instance.<locals>.Foo object at 0xdeadbeef>.b
|
||||
E + where <failure_demo.test_attribute_instance.<locals>.Foo object at 0xdeadbeef> = <class 'failure_demo.test_attribute_instance.<locals>.Foo'>()
|
||||
|
||||
failure_demo.py:114: AssertionError
|
||||
failure_demo.py:136: AssertionError
|
||||
__________________________ test_attribute_failure __________________________
|
||||
|
||||
def test_attribute_failure():
|
||||
class Foo(object):
|
||||
class Foo:
|
||||
def _get_b(self):
|
||||
raise Exception("Failed to get attrib")
|
||||
|
||||
@@ -325,7 +364,7 @@ get on the terminal - we are working on that):
|
||||
i = Foo()
|
||||
> assert i.b == 2
|
||||
|
||||
failure_demo.py:125:
|
||||
failure_demo.py:147:
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
|
||||
self = <failure_demo.test_attribute_failure.<locals>.Foo object at 0xdeadbeef>
|
||||
@@ -334,14 +373,14 @@ get on the terminal - we are working on that):
|
||||
> raise Exception("Failed to get attrib")
|
||||
E Exception: Failed to get attrib
|
||||
|
||||
failure_demo.py:120: Exception
|
||||
failure_demo.py:142: Exception
|
||||
_________________________ test_attribute_multiple __________________________
|
||||
|
||||
def test_attribute_multiple():
|
||||
class Foo(object):
|
||||
class Foo:
|
||||
b = 1
|
||||
|
||||
class Bar(object):
|
||||
class Bar:
|
||||
b = 2
|
||||
|
||||
> assert Foo().b == Bar().b
|
||||
@@ -351,31 +390,26 @@ get on the terminal - we are working on that):
|
||||
E + and 2 = <failure_demo.test_attribute_multiple.<locals>.Bar object at 0xdeadbeef>.b
|
||||
E + where <failure_demo.test_attribute_multiple.<locals>.Bar object at 0xdeadbeef> = <class 'failure_demo.test_attribute_multiple.<locals>.Bar'>()
|
||||
|
||||
failure_demo.py:135: AssertionError
|
||||
failure_demo.py:157: AssertionError
|
||||
__________________________ TestRaises.test_raises __________________________
|
||||
|
||||
self = <failure_demo.TestRaises object at 0xdeadbeef>
|
||||
|
||||
def test_raises(self):
|
||||
s = "qwe" # NOQA
|
||||
> raises(TypeError, "int(s)")
|
||||
s = "qwe"
|
||||
> raises(TypeError, int, s)
|
||||
E ValueError: invalid literal for int() with base 10: 'qwe'
|
||||
|
||||
failure_demo.py:145:
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
|
||||
> int(s)
|
||||
E ValueError: invalid literal for int() with base 10: 'qwe'
|
||||
|
||||
<0-codegen $REGENDOC_TMPDIR/assertion/failure_demo.py:145>:1: ValueError
|
||||
failure_demo.py:167: ValueError
|
||||
______________________ TestRaises.test_raises_doesnt _______________________
|
||||
|
||||
self = <failure_demo.TestRaises object at 0xdeadbeef>
|
||||
|
||||
def test_raises_doesnt(self):
|
||||
> raises(IOError, "int('3')")
|
||||
> raises(IOError, int, "3")
|
||||
E Failed: DID NOT RAISE <class 'OSError'>
|
||||
|
||||
failure_demo.py:148: Failed
|
||||
failure_demo.py:170: Failed
|
||||
__________________________ TestRaises.test_raise ___________________________
|
||||
|
||||
self = <failure_demo.TestRaises object at 0xdeadbeef>
|
||||
@@ -384,7 +418,7 @@ get on the terminal - we are working on that):
|
||||
> raise ValueError("demo error")
|
||||
E ValueError: demo error
|
||||
|
||||
failure_demo.py:151: ValueError
|
||||
failure_demo.py:173: ValueError
|
||||
________________________ TestRaises.test_tupleerror ________________________
|
||||
|
||||
self = <failure_demo.TestRaises object at 0xdeadbeef>
|
||||
@@ -393,18 +427,18 @@ get on the terminal - we are working on that):
|
||||
> a, b = [1] # NOQA
|
||||
E ValueError: not enough values to unpack (expected 2, got 1)
|
||||
|
||||
failure_demo.py:154: ValueError
|
||||
failure_demo.py:176: ValueError
|
||||
______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______
|
||||
|
||||
self = <failure_demo.TestRaises object at 0xdeadbeef>
|
||||
|
||||
def test_reinterpret_fails_with_print_for_the_fun_of_it(self):
|
||||
items = [1, 2, 3]
|
||||
print("items is %r" % items)
|
||||
print("items is {!r}".format(items))
|
||||
> a, b = items.pop()
|
||||
E TypeError: 'int' object is not iterable
|
||||
|
||||
failure_demo.py:159: TypeError
|
||||
failure_demo.py:181: TypeError
|
||||
--------------------------- Captured stdout call ---------------------------
|
||||
items is [1, 2, 3]
|
||||
________________________ TestRaises.test_some_error ________________________
|
||||
@@ -415,29 +449,30 @@ get on the terminal - we are working on that):
|
||||
> if namenotexi: # NOQA
|
||||
E NameError: name 'namenotexi' is not defined
|
||||
|
||||
failure_demo.py:162: NameError
|
||||
failure_demo.py:184: NameError
|
||||
____________________ test_dynamic_compile_shows_nicely _____________________
|
||||
|
||||
def test_dynamic_compile_shows_nicely():
|
||||
import imp
|
||||
import importlib.util
|
||||
import sys
|
||||
|
||||
src = "def foo():\n assert 1 == 0\n"
|
||||
name = "abc-123"
|
||||
module = imp.new_module(name)
|
||||
spec = importlib.util.spec_from_loader(name, loader=None)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
code = _pytest._code.compile(src, name, "exec")
|
||||
six.exec_(code, module.__dict__)
|
||||
exec(code, module.__dict__)
|
||||
sys.modules[name] = module
|
||||
> module.foo()
|
||||
|
||||
failure_demo.py:180:
|
||||
failure_demo.py:203:
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
|
||||
def foo():
|
||||
> assert 1 == 0
|
||||
E AssertionError
|
||||
|
||||
<2-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:177>:2: AssertionError
|
||||
<0-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:200>:2: AssertionError
|
||||
____________________ TestMoreErrors.test_complex_error _____________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
|
||||
@@ -451,9 +486,9 @@ get on the terminal - we are working on that):
|
||||
|
||||
> somefunc(f(), g())
|
||||
|
||||
failure_demo.py:191:
|
||||
failure_demo.py:214:
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
failure_demo.py:13: in somefunc
|
||||
failure_demo.py:11: in somefunc
|
||||
otherfunc(x, y)
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
|
||||
@@ -463,7 +498,7 @@ get on the terminal - we are working on that):
|
||||
> assert a == b
|
||||
E assert 44 == 43
|
||||
|
||||
failure_demo.py:9: AssertionError
|
||||
failure_demo.py:7: AssertionError
|
||||
___________________ TestMoreErrors.test_z1_unpack_error ____________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
|
||||
@@ -473,7 +508,7 @@ get on the terminal - we are working on that):
|
||||
> a, b = items
|
||||
E ValueError: not enough values to unpack (expected 2, got 0)
|
||||
|
||||
failure_demo.py:195: ValueError
|
||||
failure_demo.py:218: ValueError
|
||||
____________________ TestMoreErrors.test_z2_type_error _____________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
|
||||
@@ -483,7 +518,7 @@ get on the terminal - we are working on that):
|
||||
> a, b = items
|
||||
E TypeError: 'int' object is not iterable
|
||||
|
||||
failure_demo.py:199: TypeError
|
||||
failure_demo.py:222: TypeError
|
||||
______________________ TestMoreErrors.test_startswith ______________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
|
||||
@@ -496,7 +531,7 @@ get on the terminal - we are working on that):
|
||||
E + where False = <built-in method startswith of str object at 0xdeadbeef>('456')
|
||||
E + where <built-in method startswith of str object at 0xdeadbeef> = '123'.startswith
|
||||
|
||||
failure_demo.py:204: AssertionError
|
||||
failure_demo.py:227: AssertionError
|
||||
__________________ TestMoreErrors.test_startswith_nested ___________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
|
||||
@@ -515,7 +550,7 @@ get on the terminal - we are working on that):
|
||||
E + where '123' = <function TestMoreErrors.test_startswith_nested.<locals>.f at 0xdeadbeef>()
|
||||
E + and '456' = <function TestMoreErrors.test_startswith_nested.<locals>.g at 0xdeadbeef>()
|
||||
|
||||
failure_demo.py:213: AssertionError
|
||||
failure_demo.py:236: AssertionError
|
||||
_____________________ TestMoreErrors.test_global_func ______________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
|
||||
@@ -526,7 +561,7 @@ get on the terminal - we are working on that):
|
||||
E + where False = isinstance(43, float)
|
||||
E + where 43 = globf(42)
|
||||
|
||||
failure_demo.py:216: AssertionError
|
||||
failure_demo.py:239: AssertionError
|
||||
_______________________ TestMoreErrors.test_instance _______________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
|
||||
@@ -537,7 +572,7 @@ get on the terminal - we are working on that):
|
||||
E assert 42 != 42
|
||||
E + where 42 = <failure_demo.TestMoreErrors object at 0xdeadbeef>.x
|
||||
|
||||
failure_demo.py:220: AssertionError
|
||||
failure_demo.py:243: AssertionError
|
||||
_______________________ TestMoreErrors.test_compare ________________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
|
||||
@@ -547,7 +582,7 @@ get on the terminal - we are working on that):
|
||||
E assert 11 < 5
|
||||
E + where 11 = globf(10)
|
||||
|
||||
failure_demo.py:223: AssertionError
|
||||
failure_demo.py:246: AssertionError
|
||||
_____________________ TestMoreErrors.test_try_finally ______________________
|
||||
|
||||
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
|
||||
@@ -558,13 +593,13 @@ get on the terminal - we are working on that):
|
||||
> assert x == 0
|
||||
E assert 1 == 0
|
||||
|
||||
failure_demo.py:228: AssertionError
|
||||
failure_demo.py:251: AssertionError
|
||||
___________________ TestCustomAssertMsg.test_single_line ___________________
|
||||
|
||||
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
|
||||
|
||||
def test_single_line(self):
|
||||
class A(object):
|
||||
class A:
|
||||
a = 1
|
||||
|
||||
b = 2
|
||||
@@ -573,13 +608,13 @@ get on the terminal - we are working on that):
|
||||
E assert 1 == 2
|
||||
E + where 1 = <class 'failure_demo.TestCustomAssertMsg.test_single_line.<locals>.A'>.a
|
||||
|
||||
failure_demo.py:239: AssertionError
|
||||
failure_demo.py:262: AssertionError
|
||||
____________________ TestCustomAssertMsg.test_multiline ____________________
|
||||
|
||||
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
|
||||
|
||||
def test_multiline(self):
|
||||
class A(object):
|
||||
class A:
|
||||
a = 1
|
||||
|
||||
b = 2
|
||||
@@ -592,13 +627,13 @@ get on the terminal - we are working on that):
|
||||
E assert 1 == 2
|
||||
E + where 1 = <class 'failure_demo.TestCustomAssertMsg.test_multiline.<locals>.A'>.a
|
||||
|
||||
failure_demo.py:246: AssertionError
|
||||
failure_demo.py:269: AssertionError
|
||||
___________________ TestCustomAssertMsg.test_custom_repr ___________________
|
||||
|
||||
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
|
||||
|
||||
def test_custom_repr(self):
|
||||
class JSON(object):
|
||||
class JSON:
|
||||
a = 1
|
||||
|
||||
def __repr__(self):
|
||||
@@ -614,5 +649,5 @@ get on the terminal - we are working on that):
|
||||
E assert 1 == 2
|
||||
E + where 1 = This is JSON\n{\n 'foo': 'bar'\n}.a
|
||||
|
||||
failure_demo.py:259: AssertionError
|
||||
======================== 42 failed in 0.12 seconds =========================
|
||||
failure_demo.py:282: AssertionError
|
||||
============================ 44 failed in 0.12s ============================
|
||||
|
||||
@@ -65,7 +65,7 @@ Let's run this without supplying our new option:
|
||||
test_sample.py:6: AssertionError
|
||||
--------------------------- Captured stdout call ---------------------------
|
||||
first
|
||||
1 failed in 0.12 seconds
|
||||
1 failed in 0.02s
|
||||
|
||||
And now with supplying a command line option:
|
||||
|
||||
@@ -89,7 +89,7 @@ And now with supplying a command line option:
|
||||
test_sample.py:6: AssertionError
|
||||
--------------------------- Captured stdout call ---------------------------
|
||||
second
|
||||
1 failed in 0.12 seconds
|
||||
1 failed in 0.02s
|
||||
|
||||
You can see that the command line option arrived in our test. This
|
||||
completes the basic pattern. However, one often rather wants to process
|
||||
@@ -107,7 +107,7 @@ the command line arguments before they get processed:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
# setuptools plugin
|
||||
import sys
|
||||
|
||||
|
||||
@@ -127,11 +127,12 @@ directory with the above conftest.py:
|
||||
|
||||
$ pytest
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 0 items
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
.. _`excontrolskip`:
|
||||
|
||||
@@ -156,6 +157,10 @@ line option to control skipping of ``pytest.mark.slow`` marked tests:
|
||||
)
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
config.addinivalue_line("markers", "slow: mark test as slow to run")
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
if config.getoption("--runslow"):
|
||||
# --runslow given in cli: do not skip slow tests
|
||||
@@ -187,15 +192,16 @@ and when running it will see a skipped "slow" test:
|
||||
|
||||
$ pytest -rs # "-rs" means report details on the little 's'
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 2 items
|
||||
|
||||
test_module.py .s [100%]
|
||||
========================= short test summary info ==========================
|
||||
SKIP [1] test_module.py:8: need --runslow option to run
|
||||
|
||||
=================== 1 passed, 1 skipped in 0.12 seconds ====================
|
||||
========================= short test summary info ==========================
|
||||
SKIPPED [1] test_module.py:8: need --runslow option to run
|
||||
======================= 1 passed, 1 skipped in 0.12s =======================
|
||||
|
||||
Or run it including the ``slow`` marked test:
|
||||
|
||||
@@ -203,13 +209,14 @@ Or run it including the ``slow`` marked test:
|
||||
|
||||
$ pytest --runslow
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 2 items
|
||||
|
||||
test_module.py .. [100%]
|
||||
|
||||
========================= 2 passed in 0.12 seconds =========================
|
||||
============================ 2 passed in 0.12s =============================
|
||||
|
||||
Writing well integrated assertion helpers
|
||||
--------------------------------------------------
|
||||
@@ -231,7 +238,7 @@ Example:
|
||||
def checkconfig(x):
|
||||
__tracebackhide__ = True
|
||||
if not hasattr(x, "config"):
|
||||
pytest.fail("not configured: %s" % (x,))
|
||||
pytest.fail("not configured: {}".format(x))
|
||||
|
||||
|
||||
def test_something():
|
||||
@@ -254,7 +261,7 @@ Let's run our little function:
|
||||
E Failed: not configured: 42
|
||||
|
||||
test_checkconfig.py:11: Failed
|
||||
1 failed in 0.12 seconds
|
||||
1 failed in 0.02s
|
||||
|
||||
If you only want to hide certain exceptions, you can set ``__tracebackhide__``
|
||||
to a callable which gets the ``ExceptionInfo`` object. You can for example use
|
||||
@@ -273,7 +280,7 @@ this to make sure unexpected exception types aren't hidden:
|
||||
def checkconfig(x):
|
||||
__tracebackhide__ = operator.methodcaller("errisinstance", ConfigException)
|
||||
if not hasattr(x, "config"):
|
||||
raise ConfigException("not configured: %s" % (x,))
|
||||
raise ConfigException("not configured: {}".format(x))
|
||||
|
||||
|
||||
def test_something():
|
||||
@@ -345,12 +352,13 @@ which will add the string to the test header accordingly:
|
||||
|
||||
$ pytest
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
project deps: mylib-1.1
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 0 items
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
@@ -373,14 +381,14 @@ which will add info only when run with "--v":
|
||||
|
||||
$ pytest -v
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
info1: did you know that ...
|
||||
did you?
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 0 items
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
and nothing when run plainly:
|
||||
|
||||
@@ -388,11 +396,12 @@ and nothing when run plainly:
|
||||
|
||||
$ pytest
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 0 items
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
profiling test duration
|
||||
--------------------------
|
||||
@@ -427,8 +436,9 @@ Now we can profile which test functions execute the slowest:
|
||||
|
||||
$ pytest --durations=3
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 3 items
|
||||
|
||||
test_some_are_slow.py ... [100%]
|
||||
@@ -437,7 +447,7 @@ Now we can profile which test functions execute the slowest:
|
||||
0.30s call test_some_are_slow.py::test_funcslow2
|
||||
0.20s call test_some_are_slow.py::test_funcslow1
|
||||
0.10s call test_some_are_slow.py::test_funcfast
|
||||
========================= 3 passed in 0.12 seconds =========================
|
||||
============================ 3 passed in 0.12s =============================
|
||||
|
||||
incremental testing - test steps
|
||||
---------------------------------------------------
|
||||
@@ -468,7 +478,7 @@ an ``incremental`` marker which is to be used on classes:
|
||||
if "incremental" in item.keywords:
|
||||
previousfailed = getattr(item.parent, "_previousfailed", None)
|
||||
if previousfailed is not None:
|
||||
pytest.xfail("previous test failed (%s)" % previousfailed.name)
|
||||
pytest.xfail("previous test failed ({})".format(previousfailed.name))
|
||||
|
||||
These two hook implementations work together to abort incremental-marked
|
||||
tests in a class. Here is a test module example:
|
||||
@@ -481,7 +491,7 @@ tests in a class. Here is a test module example:
|
||||
|
||||
|
||||
@pytest.mark.incremental
|
||||
class TestUserHandling(object):
|
||||
class TestUserHandling:
|
||||
def test_login(self):
|
||||
pass
|
||||
|
||||
@@ -501,8 +511,9 @@ If we run this:
|
||||
|
||||
$ pytest -rx
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 4 items
|
||||
|
||||
test_step.py .Fx. [100%]
|
||||
@@ -520,7 +531,7 @@ If we run this:
|
||||
========================= short test summary info ==========================
|
||||
XFAIL test_step.py::TestUserHandling::test_deletion
|
||||
reason: previous test failed (test_modification)
|
||||
============== 1 failed, 2 passed, 1 xfailed in 0.12 seconds ===============
|
||||
================== 1 failed, 2 passed, 1 xfailed in 0.12s ==================
|
||||
|
||||
We'll see that ``test_deletion`` was not executed because ``test_modification``
|
||||
failed. It is reported as an "expected failure".
|
||||
@@ -545,7 +556,7 @@ Here is an example for making a ``db`` fixture available in a directory:
|
||||
import pytest
|
||||
|
||||
|
||||
class DB(object):
|
||||
class DB:
|
||||
pass
|
||||
|
||||
|
||||
@@ -584,8 +595,9 @@ We can run this:
|
||||
|
||||
$ pytest
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 7 items
|
||||
|
||||
test_step.py .Fx. [ 57%]
|
||||
@@ -598,7 +610,7 @@ We can run this:
|
||||
file $REGENDOC_TMPDIR/b/test_error.py, line 1
|
||||
def test_root(db): # no db here, will error out
|
||||
E fixture 'db' not found
|
||||
> available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_xml_attribute, record_xml_property, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory
|
||||
> available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory
|
||||
> use 'pytest --fixtures [testpath]' for help on them.
|
||||
|
||||
$REGENDOC_TMPDIR/b/test_error.py:1
|
||||
@@ -632,7 +644,7 @@ We can run this:
|
||||
E assert 0
|
||||
|
||||
a/test_db2.py:2: AssertionError
|
||||
========== 3 failed, 2 passed, 1 xfailed, 1 error in 0.12 seconds ==========
|
||||
============= 3 failed, 2 passed, 1 xfailed, 1 error in 0.12s ==============
|
||||
|
||||
The two test modules in the ``a`` directory see the same ``db`` fixture instance
|
||||
while the one test in the sister-directory ``b`` doesn't see it. We could of course
|
||||
@@ -672,7 +684,7 @@ case we just write some information out to a ``failures`` file:
|
||||
with open("failures", mode) as f:
|
||||
# let's also access a fixture for the fun of it
|
||||
if "tmpdir" in item.fixturenames:
|
||||
extra = " (%s)" % item.funcargs["tmpdir"]
|
||||
extra = " ({})".format(item.funcargs["tmpdir"])
|
||||
else:
|
||||
extra = ""
|
||||
|
||||
@@ -697,8 +709,9 @@ and run them:
|
||||
|
||||
$ pytest test_module.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 2 items
|
||||
|
||||
test_module.py FF [100%]
|
||||
@@ -720,9 +733,11 @@ and run them:
|
||||
E assert 0
|
||||
|
||||
test_module.py:6: AssertionError
|
||||
========================= 2 failed in 0.12 seconds =========================
|
||||
============================ 2 failed in 0.12s =============================
|
||||
|
||||
you will have a "failures" file which contains the failing test ids::
|
||||
you will have a "failures" file which contains the failing test ids:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cat failures
|
||||
test_module.py::test_fail1 (PYTEST_TMPDIR/test_fail10)
|
||||
@@ -798,8 +813,9 @@ and run it:
|
||||
|
||||
$ pytest -s test_module.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 3 items
|
||||
|
||||
test_module.py Esetting up a test failed! test_module.py::test_setup_fails
|
||||
@@ -832,7 +848,7 @@ and run it:
|
||||
E assert 0
|
||||
|
||||
test_module.py:19: AssertionError
|
||||
==================== 2 failed, 1 error in 0.12 seconds =====================
|
||||
======================== 2 failed, 1 error in 0.12s ========================
|
||||
|
||||
You'll see that the fixture finalizers could use the precise reporting
|
||||
information.
|
||||
@@ -842,7 +858,7 @@ information.
|
||||
``PYTEST_CURRENT_TEST`` environment variable
|
||||
--------------------------------------------
|
||||
|
||||
.. versionadded:: 3.2
|
||||
|
||||
|
||||
Sometimes a test session might get stuck and there might be no easy way to figure out
|
||||
which test got stuck, for example if pytest was run in quiet mode (``-q``) or you don't have access to the console
|
||||
@@ -925,6 +941,8 @@ like ``pytest-timeout`` they must be imported explicitly and passed on to pytest
|
||||
|
||||
|
||||
This allows you to execute tests using the frozen
|
||||
application with standard ``pytest`` command-line options::
|
||||
application with standard ``pytest`` command-line options:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./app_main --pytest --verbose --tb=long --junitxml=results.xml test-suite/
|
||||
|
||||
@@ -5,30 +5,36 @@ A session-scoped fixture effectively has access to all
|
||||
collected test items. Here is an example of a fixture
|
||||
function which walks all collected tests and looks
|
||||
if their test class defines a ``callme`` method and
|
||||
calls it::
|
||||
calls it:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def callattr_ahead_of_alltests(request):
|
||||
print("callattr_ahead_of_alltests called")
|
||||
seen = set([None])
|
||||
seen = {None}
|
||||
session = request.node
|
||||
for item in session.items:
|
||||
cls = item.getparent(pytest.Class)
|
||||
if cls not in seen:
|
||||
if hasattr(cls.obj, "callme"):
|
||||
cls.obj.callme()
|
||||
cls.obj.callme()
|
||||
seen.add(cls)
|
||||
|
||||
test classes may now define a ``callme`` method which
|
||||
will be called ahead of running any tests::
|
||||
will be called ahead of running any tests:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_module.py
|
||||
|
||||
class TestHello(object):
|
||||
|
||||
class TestHello:
|
||||
@classmethod
|
||||
def callme(cls):
|
||||
print("callme called!")
|
||||
@@ -39,16 +45,20 @@ will be called ahead of running any tests::
|
||||
def test_method2(self):
|
||||
print("test_method1 called")
|
||||
|
||||
class TestOther(object):
|
||||
|
||||
class TestOther:
|
||||
@classmethod
|
||||
def callme(cls):
|
||||
print("callme other called")
|
||||
|
||||
def test_other(self):
|
||||
print("test other")
|
||||
|
||||
|
||||
# works with unittest as well ...
|
||||
import unittest
|
||||
|
||||
|
||||
class SomeTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def callme(self):
|
||||
@@ -71,4 +81,4 @@ If you run this without output capturing:
|
||||
.test other
|
||||
.test_unit1 method called
|
||||
.
|
||||
4 passed in 0.12 seconds
|
||||
4 passed in 0.01s
|
||||
|
||||
@@ -15,7 +15,9 @@ Running an existing test suite with pytest
|
||||
Say you want to contribute to an existing repository somewhere.
|
||||
After pulling the code into your development space using some
|
||||
flavor of version control and (optionally) setting up a virtualenv
|
||||
you will want to run::
|
||||
you will want to run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd <repository>
|
||||
pip install -e . # Environment dependent alternatives include
|
||||
|
||||
@@ -7,7 +7,7 @@ pytest fixtures: explicit, modular, scalable
|
||||
|
||||
.. currentmodule:: _pytest.python
|
||||
|
||||
.. versionadded:: 2.0/2.3/2.4
|
||||
|
||||
|
||||
.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
|
||||
.. _`purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software
|
||||
@@ -49,20 +49,25 @@ argument. For each argument name, a fixture function with that name provides
|
||||
the fixture object. Fixture functions are registered by marking them with
|
||||
:py:func:`@pytest.fixture <_pytest.python.fixture>`. Let's look at a simple
|
||||
self-contained test module containing a fixture and a test function
|
||||
using it::
|
||||
using it:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of ./test_smtpsimple.py
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def smtp_connection():
|
||||
import smtplib
|
||||
|
||||
return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
||||
|
||||
|
||||
def test_ehlo(smtp_connection):
|
||||
response, msg = smtp_connection.ehlo()
|
||||
assert response == 250
|
||||
assert 0 # for demo purposes
|
||||
assert 0 # for demo purposes
|
||||
|
||||
Here, the ``test_ehlo`` needs the ``smtp_connection`` fixture value. pytest
|
||||
will discover and call the :py:func:`@pytest.fixture <_pytest.python.fixture>`
|
||||
@@ -72,8 +77,9 @@ marked ``smtp_connection`` fixture function. Running the test looks like this:
|
||||
|
||||
$ pytest test_smtpsimple.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 1 item
|
||||
|
||||
test_smtpsimple.py F [100%]
|
||||
@@ -86,11 +92,11 @@ marked ``smtp_connection`` fixture function. Running the test looks like this:
|
||||
def test_ehlo(smtp_connection):
|
||||
response, msg = smtp_connection.ehlo()
|
||||
assert response == 250
|
||||
> assert 0 # for demo purposes
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_smtpsimple.py:11: AssertionError
|
||||
========================= 1 failed in 0.12 seconds =========================
|
||||
test_smtpsimple.py:14: AssertionError
|
||||
============================ 1 failed in 0.12s =============================
|
||||
|
||||
In the failure traceback we see that the test function was called with a
|
||||
``smtp_connection`` argument, the ``smtplib.SMTP()`` instance created by the fixture
|
||||
@@ -113,7 +119,9 @@ with a list of available function arguments.
|
||||
|
||||
.. note::
|
||||
|
||||
You can always issue ::
|
||||
You can always issue:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --fixtures test_simplefactory.py
|
||||
|
||||
@@ -177,12 +185,15 @@ Possible values for ``scope`` are: ``function``, ``class``, ``module``, ``packag
|
||||
|
||||
The next example puts the fixture function into a separate ``conftest.py`` file
|
||||
so that tests from multiple test modules in the directory can
|
||||
access the fixture function::
|
||||
access the fixture function:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
import pytest
|
||||
import smtplib
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def smtp_connection():
|
||||
return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
||||
@@ -190,16 +201,20 @@ access the fixture function::
|
||||
The name of the fixture again is ``smtp_connection`` and you can access its
|
||||
result by listing the name ``smtp_connection`` as an input parameter in any
|
||||
test or fixture function (in or below the directory where ``conftest.py`` is
|
||||
located)::
|
||||
located):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_module.py
|
||||
|
||||
|
||||
def test_ehlo(smtp_connection):
|
||||
response, msg = smtp_connection.ehlo()
|
||||
assert response == 250
|
||||
assert b"smtp.gmail.com" in msg
|
||||
assert 0 # for demo purposes
|
||||
|
||||
|
||||
def test_noop(smtp_connection):
|
||||
response, msg = smtp_connection.noop()
|
||||
assert response == 250
|
||||
@@ -212,8 +227,9 @@ inspect what is going on and can now run the tests:
|
||||
|
||||
$ pytest test_module.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 2 items
|
||||
|
||||
test_module.py FF [100%]
|
||||
@@ -230,7 +246,7 @@ inspect what is going on and can now run the tests:
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:6: AssertionError
|
||||
test_module.py:7: AssertionError
|
||||
________________________________ test_noop _________________________________
|
||||
|
||||
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
|
||||
@@ -241,8 +257,8 @@ inspect what is going on and can now run the tests:
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:11: AssertionError
|
||||
========================= 2 failed in 0.12 seconds =========================
|
||||
test_module.py:13: AssertionError
|
||||
============================ 2 failed in 0.12s =============================
|
||||
|
||||
You see the two ``assert 0`` failing and more importantly you can also see
|
||||
that the same (module-scoped) ``smtp_connection`` object was passed into the
|
||||
@@ -272,7 +288,7 @@ Finally, the ``class`` scope will invoke the fixture once per test *class*.
|
||||
``package`` scope (experimental)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. versionadded:: 3.7
|
||||
|
||||
|
||||
In pytest 3.7 the ``package`` scope has been introduced. Package-scoped fixtures
|
||||
are finalized when the last test of a *package* finishes.
|
||||
@@ -285,51 +301,29 @@ are finalized when the last test of a *package* finishes.
|
||||
Use this new feature sparingly and please make sure to report any issues you find.
|
||||
|
||||
|
||||
Higher-scoped fixtures are instantiated first
|
||||
---------------------------------------------
|
||||
Order: Higher-scoped fixtures are instantiated first
|
||||
----------------------------------------------------
|
||||
|
||||
|
||||
.. versionadded:: 3.5
|
||||
|
||||
Within a function request for features, fixture of higher-scopes (such as ``session``) are instantiated first than
|
||||
lower-scoped fixtures (such as ``function`` or ``class``). The relative order of fixtures of same scope follows
|
||||
the declared order in the test function and honours dependencies between fixtures.
|
||||
the declared order in the test function and honours dependencies between fixtures. Autouse fixtures will be
|
||||
instantiated before explicitly used fixtures.
|
||||
|
||||
Consider the code below:
|
||||
|
||||
.. code-block:: python
|
||||
.. literalinclude:: example/fixtures/test_fixtures_order.py
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def s1():
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def m1():
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def f1(tmpdir):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def f2():
|
||||
pass
|
||||
|
||||
|
||||
def test_foo(f1, m1, f2, s1):
|
||||
...
|
||||
|
||||
|
||||
The fixtures requested by ``test_foo`` will be instantiated in the following order:
|
||||
The fixtures requested by ``test_order`` will be instantiated in the following order:
|
||||
|
||||
1. ``s1``: is the highest-scoped fixture (``session``).
|
||||
2. ``m1``: is the second highest-scoped fixture (``module``).
|
||||
3. ``tmpdir``: is a ``function``-scoped fixture, required by ``f1``: it needs to be instantiated at this point
|
||||
because it is a dependency of ``f1``.
|
||||
4. ``f1``: is the first ``function``-scoped fixture in ``test_foo`` parameter list.
|
||||
5. ``f2``: is the last ``function``-scoped fixture in ``test_foo`` parameter list.
|
||||
3. ``a1``: is a ``function``-scoped ``autouse`` fixture: it will be instantiated before other fixtures
|
||||
within the same scope.
|
||||
4. ``f3``: is a ``function``-scoped fixture, required by ``f1``: it needs to be instantiated at this point
|
||||
5. ``f1``: is the first ``function``-scoped fixture in ``test_order`` parameter list.
|
||||
6. ``f2``: is the last ``function``-scoped fixture in ``test_order`` parameter list.
|
||||
|
||||
|
||||
.. _`finalization`:
|
||||
@@ -360,12 +354,14 @@ The ``print`` and ``smtp.close()`` statements will execute when the last test in
|
||||
the module has finished execution, regardless of the exception status of the
|
||||
tests.
|
||||
|
||||
Let's execute it::
|
||||
Let's execute it:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
$ pytest -s -q --tb=no
|
||||
FFteardown smtp
|
||||
|
||||
2 failed in 0.12 seconds
|
||||
2 failed in 0.79s
|
||||
|
||||
We see that the ``smtp_connection`` instance is finalized after the two
|
||||
tests finished execution. Note that if we decorated our fixture
|
||||
@@ -394,6 +390,34 @@ The ``smtp_connection`` connection will be closed after the test finished
|
||||
execution because the ``smtp_connection`` object automatically closes when
|
||||
the ``with`` statement ends.
|
||||
|
||||
Using the contextlib.ExitStack context manager finalizers will always be called
|
||||
regardless if the fixture *setup* code raises an exception. This is handy to properly
|
||||
close all resources created by a fixture even if one of them fails to be created/acquired:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_yield3.py
|
||||
|
||||
import contextlib
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def connect(port):
|
||||
... # create connection
|
||||
yield
|
||||
... # close connection
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def equipments():
|
||||
with contextlib.ExitStack() as stack:
|
||||
yield [stack.enter_context(connect(port)) for port in ("C1", "C3", "C28")]
|
||||
|
||||
In the example above, if ``"C28"`` fails with an exception, ``"C1"`` and ``"C3"`` will still
|
||||
be properly closed.
|
||||
|
||||
Note that if an exception happens during the *setup* code (before the ``yield`` keyword), the
|
||||
*teardown* code (after the ``yield``) will not be called.
|
||||
|
||||
@@ -422,27 +446,39 @@ Here's the ``smtp_connection`` fixture changed to use ``addfinalizer`` for clean
|
||||
return smtp_connection # provide the fixture value
|
||||
|
||||
|
||||
Here's the ``equipments`` fixture changed to use ``addfinalizer`` for cleanup:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_yield3.py
|
||||
|
||||
import contextlib
|
||||
import functools
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def connect(port):
|
||||
... # create connection
|
||||
yield
|
||||
... # close connection
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def equipments(request):
|
||||
r = []
|
||||
for port in ("C1", "C3", "C28"):
|
||||
cm = connect(port)
|
||||
equip = cm.__enter__()
|
||||
request.addfinalizer(functools.partial(cm.__exit__, None, None, None))
|
||||
r.append(equip)
|
||||
return r
|
||||
|
||||
|
||||
Both ``yield`` and ``addfinalizer`` methods work similarly by calling their code after the test
|
||||
ends, but ``addfinalizer`` has two key differences over ``yield``:
|
||||
|
||||
1. It is possible to register multiple finalizer functions.
|
||||
|
||||
2. Finalizers will always be called regardless if the fixture *setup* code raises an exception.
|
||||
This is handy to properly close all resources created by a fixture even if one of them
|
||||
fails to be created/acquired::
|
||||
|
||||
@pytest.fixture
|
||||
def equipments(request):
|
||||
r = []
|
||||
for port in ('C1', 'C3', 'C28'):
|
||||
equip = connect(port)
|
||||
request.addfinalizer(equip.disconnect)
|
||||
r.append(equip)
|
||||
return r
|
||||
|
||||
In the example above, if ``"C28"`` fails with an exception, ``"C1"`` and ``"C3"`` will still
|
||||
be properly closed. Of course, if an exception happens before the finalize function is
|
||||
registered then it will not be executed.
|
||||
ends. Of course, if an exception happens before the finalize function is registered then it
|
||||
will not be executed.
|
||||
|
||||
|
||||
.. _`request-context`:
|
||||
@@ -453,36 +489,44 @@ Fixtures can introspect the requesting test context
|
||||
Fixture functions can accept the :py:class:`request <FixtureRequest>` object
|
||||
to introspect the "requesting" test function, class or module context.
|
||||
Further extending the previous ``smtp_connection`` fixture example, let's
|
||||
read an optional server URL from the test module which uses our fixture::
|
||||
read an optional server URL from the test module which uses our fixture:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
import pytest
|
||||
import smtplib
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def smtp_connection(request):
|
||||
server = getattr(request.module, "smtpserver", "smtp.gmail.com")
|
||||
smtp_connection = smtplib.SMTP(server, 587, timeout=5)
|
||||
yield smtp_connection
|
||||
print("finalizing %s (%s)" % (smtp_connection, server))
|
||||
print("finalizing {} ({})".format(smtp_connection, server))
|
||||
smtp_connection.close()
|
||||
|
||||
We use the ``request.module`` attribute to optionally obtain an
|
||||
``smtpserver`` attribute from the test module. If we just execute
|
||||
again, nothing much has changed::
|
||||
again, nothing much has changed:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
$ pytest -s -q --tb=no
|
||||
FFfinalizing <smtplib.SMTP object at 0xdeadbeef> (smtp.gmail.com)
|
||||
|
||||
2 failed in 0.12 seconds
|
||||
2 failed in 0.77s
|
||||
|
||||
Let's quickly create another test module that actually sets the
|
||||
server URL in its module namespace::
|
||||
server URL in its module namespace:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_anothersmtp.py
|
||||
|
||||
smtpserver = "mail.python.org" # will be read by smtp fixture
|
||||
|
||||
|
||||
def test_showhelo(smtp_connection):
|
||||
assert 0, smtp_connection.helo()
|
||||
|
||||
@@ -494,7 +538,7 @@ Running it:
|
||||
F [100%]
|
||||
================================= FAILURES =================================
|
||||
______________________________ test_showhelo _______________________________
|
||||
test_anothersmtp.py:5: in test_showhelo
|
||||
test_anothersmtp.py:6: in test_showhelo
|
||||
assert 0, smtp_connection.helo()
|
||||
E AssertionError: (250, b'mail.python.org')
|
||||
E assert 0
|
||||
@@ -514,16 +558,14 @@ of a fixture is needed multiple times in a single test. Instead of returning
|
||||
data directly, the fixture instead returns a function which generates the data.
|
||||
This function can then be called multiple times in the test.
|
||||
|
||||
Factories can have have parameters as needed::
|
||||
Factories can have parameters as needed:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.fixture
|
||||
def make_customer_record():
|
||||
|
||||
def _make_customer_record(name):
|
||||
return {
|
||||
"name": name,
|
||||
"orders": []
|
||||
}
|
||||
return {"name": name, "orders": []}
|
||||
|
||||
return _make_customer_record
|
||||
|
||||
@@ -533,7 +575,9 @@ Factories can have have parameters as needed::
|
||||
customer_2 = make_customer_record("Mike")
|
||||
customer_3 = make_customer_record("Meredith")
|
||||
|
||||
If the data created by the factory requires managing, the fixture can take care of that::
|
||||
If the data created by the factory requires managing, the fixture can take care of that:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.fixture
|
||||
def make_customer_record():
|
||||
@@ -572,18 +616,20 @@ configured in multiple ways.
|
||||
Extending the previous example, we can flag the fixture to create two
|
||||
``smtp_connection`` fixture instances which will cause all tests using the fixture
|
||||
to run twice. The fixture function gets access to each parameter
|
||||
through the special :py:class:`request <FixtureRequest>` object::
|
||||
through the special :py:class:`request <FixtureRequest>` object:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
import pytest
|
||||
import smtplib
|
||||
|
||||
@pytest.fixture(scope="module",
|
||||
params=["smtp.gmail.com", "mail.python.org"])
|
||||
|
||||
@pytest.fixture(scope="module", params=["smtp.gmail.com", "mail.python.org"])
|
||||
def smtp_connection(request):
|
||||
smtp_connection = smtplib.SMTP(request.param, 587, timeout=5)
|
||||
yield smtp_connection
|
||||
print("finalizing %s" % smtp_connection)
|
||||
print("finalizing {}".format(smtp_connection))
|
||||
smtp_connection.close()
|
||||
|
||||
The main change is the declaration of ``params`` with
|
||||
@@ -608,7 +654,7 @@ So let's just do another run:
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:6: AssertionError
|
||||
test_module.py:7: AssertionError
|
||||
________________________ test_noop[smtp.gmail.com] _________________________
|
||||
|
||||
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
|
||||
@@ -619,7 +665,7 @@ So let's just do another run:
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:11: AssertionError
|
||||
test_module.py:13: AssertionError
|
||||
________________________ test_ehlo[mail.python.org] ________________________
|
||||
|
||||
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
|
||||
@@ -628,9 +674,9 @@ So let's just do another run:
|
||||
response, msg = smtp_connection.ehlo()
|
||||
assert response == 250
|
||||
> assert b"smtp.gmail.com" in msg
|
||||
E AssertionError: assert b'smtp.gmail.com' in b'mail.python.org\nPIPELINING\nSIZE 51200000\nETRN\nSTARTTLS\nAUTH DIGEST-MD5 NTLM CRAM-MD5\nENHANCEDSTATUSCODES\n8BITMIME\nDSN\nSMTPUTF8'
|
||||
E AssertionError: assert b'smtp.gmail.com' in b'mail.python.org\nPIPELINING\nSIZE 51200000\nETRN\nSTARTTLS\nAUTH DIGEST-MD5 NTLM CRAM-MD5\nENHANCEDSTATUSCODES\n8BITMIME\nDSN\nSMTPUTF8\nCHUNKING'
|
||||
|
||||
test_module.py:5: AssertionError
|
||||
test_module.py:6: AssertionError
|
||||
-------------------------- Captured stdout setup ---------------------------
|
||||
finalizing <smtplib.SMTP object at 0xdeadbeef>
|
||||
________________________ test_noop[mail.python.org] ________________________
|
||||
@@ -643,10 +689,10 @@ So let's just do another run:
|
||||
> assert 0 # for demo purposes
|
||||
E assert 0
|
||||
|
||||
test_module.py:11: AssertionError
|
||||
test_module.py:13: AssertionError
|
||||
------------------------- Captured stdout teardown -------------------------
|
||||
finalizing <smtplib.SMTP object at 0xdeadbeef>
|
||||
4 failed in 0.12 seconds
|
||||
4 failed in 1.69s
|
||||
|
||||
We see that our two test functions each ran twice, against the different
|
||||
``smtp_connection`` instances. Note also, that with the ``mail.python.org``
|
||||
@@ -664,28 +710,35 @@ Numbers, strings, booleans and None will have their usual string
|
||||
representation used in the test ID. For other objects, pytest will
|
||||
make a string based on the argument name. It is possible to customise
|
||||
the string used in a test ID for a certain fixture value by using the
|
||||
``ids`` keyword argument::
|
||||
``ids`` keyword argument:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_ids.py
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(params=[0, 1], ids=["spam", "ham"])
|
||||
def a(request):
|
||||
return request.param
|
||||
|
||||
|
||||
def test_a(a):
|
||||
pass
|
||||
|
||||
|
||||
def idfn(fixture_value):
|
||||
if fixture_value == 0:
|
||||
return "eggs"
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@pytest.fixture(params=[0, 1], ids=idfn)
|
||||
def b(request):
|
||||
return request.param
|
||||
|
||||
|
||||
def test_b(b):
|
||||
pass
|
||||
|
||||
@@ -700,24 +753,25 @@ Running the above tests results in the following test IDs being used:
|
||||
|
||||
$ pytest --collect-only
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 10 items
|
||||
<Module 'test_anothersmtp.py'>
|
||||
<Function 'test_showhelo[smtp.gmail.com]'>
|
||||
<Function 'test_showhelo[mail.python.org]'>
|
||||
<Module 'test_ids.py'>
|
||||
<Function 'test_a[spam]'>
|
||||
<Function 'test_a[ham]'>
|
||||
<Function 'test_b[eggs]'>
|
||||
<Function 'test_b[1]'>
|
||||
<Module 'test_module.py'>
|
||||
<Function 'test_ehlo[smtp.gmail.com]'>
|
||||
<Function 'test_noop[smtp.gmail.com]'>
|
||||
<Function 'test_ehlo[mail.python.org]'>
|
||||
<Function 'test_noop[mail.python.org]'>
|
||||
<Module test_anothersmtp.py>
|
||||
<Function test_showhelo[smtp.gmail.com]>
|
||||
<Function test_showhelo[mail.python.org]>
|
||||
<Module test_ids.py>
|
||||
<Function test_a[spam]>
|
||||
<Function test_a[ham]>
|
||||
<Function test_b[eggs]>
|
||||
<Function test_b[1]>
|
||||
<Module test_module.py>
|
||||
<Function test_ehlo[smtp.gmail.com]>
|
||||
<Function test_noop[smtp.gmail.com]>
|
||||
<Function test_ehlo[mail.python.org]>
|
||||
<Function test_noop[mail.python.org]>
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
========================== no tests ran in 0.12s ===========================
|
||||
|
||||
.. _`fixture-parametrize-marks`:
|
||||
|
||||
@@ -727,14 +781,19 @@ Using marks with parametrized fixtures
|
||||
:func:`pytest.param` can be used to apply marks in values sets of parametrized fixtures in the same way
|
||||
that they can be used with :ref:`@pytest.mark.parametrize <@pytest.mark.parametrize>`.
|
||||
|
||||
Example::
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_fixture_marks.py
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(params=[0, 1, pytest.param(2, marks=pytest.mark.skip)])
|
||||
def data_set(request):
|
||||
return request.param
|
||||
|
||||
|
||||
def test_data(data_set):
|
||||
pass
|
||||
|
||||
@@ -744,16 +803,16 @@ Running this test will *skip* the invocation of ``data_set`` with value ``2``:
|
||||
|
||||
$ pytest test_fixture_marks.py -v
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 3 items
|
||||
|
||||
test_fixture_marks.py::test_data[0] PASSED [ 33%]
|
||||
test_fixture_marks.py::test_data[1] PASSED [ 66%]
|
||||
test_fixture_marks.py::test_data[2] SKIPPED [100%]
|
||||
|
||||
=================== 2 passed, 1 skipped in 0.12 seconds ====================
|
||||
======================= 2 passed, 1 skipped in 0.12s =======================
|
||||
|
||||
.. _`interdependent fixtures`:
|
||||
|
||||
@@ -765,20 +824,25 @@ can use other fixtures themselves. This contributes to a modular design
|
||||
of your fixtures and allows re-use of framework-specific fixtures across
|
||||
many projects. As a simple example, we can extend the previous example
|
||||
and instantiate an object ``app`` where we stick the already defined
|
||||
``smtp_connection`` resource into it::
|
||||
``smtp_connection`` resource into it:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_appsetup.py
|
||||
|
||||
import pytest
|
||||
|
||||
class App(object):
|
||||
|
||||
class App:
|
||||
def __init__(self, smtp_connection):
|
||||
self.smtp_connection = smtp_connection
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def app(smtp_connection):
|
||||
return App(smtp_connection)
|
||||
|
||||
|
||||
def test_smtp_connection_exists(app):
|
||||
assert app.smtp_connection
|
||||
|
||||
@@ -789,22 +853,22 @@ Here we declare an ``app`` fixture which receives the previously defined
|
||||
|
||||
$ pytest -v test_appsetup.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 2 items
|
||||
|
||||
test_appsetup.py::test_smtp_connection_exists[smtp.gmail.com] PASSED [ 50%]
|
||||
test_appsetup.py::test_smtp_connection_exists[mail.python.org] PASSED [100%]
|
||||
|
||||
========================= 2 passed in 0.12 seconds =========================
|
||||
============================ 2 passed in 0.12s =============================
|
||||
|
||||
Due to the parametrization of ``smtp_connection``, the test will run twice with two
|
||||
different ``App`` instances and respective smtp servers. There is no
|
||||
need for the ``app`` fixture to be aware of the ``smtp_connection``
|
||||
parametrization because pytest will fully analyse the fixture dependency graph.
|
||||
|
||||
Note, that the ``app`` fixture has a scope of ``module`` and uses a
|
||||
Note that the ``app`` fixture has a scope of ``module`` and uses a
|
||||
module-scoped ``smtp_connection`` fixture. The example would still work if
|
||||
``smtp_connection`` was cached on a ``session`` scope: it is fine for fixtures to use
|
||||
"broader" scoped fixtures but not the other way round:
|
||||
@@ -827,31 +891,40 @@ this eases testing of applications which create and use global state.
|
||||
|
||||
The following example uses two parametrized fixtures, one of which is
|
||||
scoped on a per-module basis, and all the functions perform ``print`` calls
|
||||
to show the setup/teardown flow::
|
||||
to show the setup/teardown flow:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_module.py
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", params=["mod1", "mod2"])
|
||||
def modarg(request):
|
||||
param = request.param
|
||||
print(" SETUP modarg %s" % param)
|
||||
print(" SETUP modarg", param)
|
||||
yield param
|
||||
print(" TEARDOWN modarg %s" % param)
|
||||
print(" TEARDOWN modarg", param)
|
||||
|
||||
@pytest.fixture(scope="function", params=[1,2])
|
||||
|
||||
@pytest.fixture(scope="function", params=[1, 2])
|
||||
def otherarg(request):
|
||||
param = request.param
|
||||
print(" SETUP otherarg %s" % param)
|
||||
print(" SETUP otherarg", param)
|
||||
yield param
|
||||
print(" TEARDOWN otherarg %s" % param)
|
||||
print(" TEARDOWN otherarg", param)
|
||||
|
||||
|
||||
def test_0(otherarg):
|
||||
print(" RUN test0 with otherarg %s" % otherarg)
|
||||
print(" RUN test0 with otherarg", otherarg)
|
||||
|
||||
|
||||
def test_1(modarg):
|
||||
print(" RUN test1 with modarg %s" % modarg)
|
||||
print(" RUN test1 with modarg", modarg)
|
||||
|
||||
|
||||
def test_2(otherarg, modarg):
|
||||
print(" RUN test2 with otherarg %s and modarg %s" % (otherarg, modarg))
|
||||
print(" RUN test2 with otherarg {} and modarg {}".format(otherarg, modarg))
|
||||
|
||||
|
||||
Let's run the tests in verbose mode and with looking at the print-output:
|
||||
@@ -860,9 +933,9 @@ Let's run the tests in verbose mode and with looking at the print-output:
|
||||
|
||||
$ pytest -v -s test_module.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collecting ... collected 8 items
|
||||
|
||||
test_module.py::test_0[1] SETUP otherarg 1
|
||||
@@ -898,7 +971,7 @@ Let's run the tests in verbose mode and with looking at the print-output:
|
||||
TEARDOWN modarg mod2
|
||||
|
||||
|
||||
========================= 8 passed in 0.12 seconds =========================
|
||||
============================ 8 passed in 0.12s =============================
|
||||
|
||||
You can see that the parametrized module-scoped ``modarg`` resource caused an
|
||||
ordering of test execution that lead to the fewest possible "active" resources.
|
||||
@@ -926,7 +999,9 @@ current working directory but otherwise do not care for the concrete
|
||||
directory. Here is how you can use the standard `tempfile
|
||||
<http://docs.python.org/library/tempfile.html>`_ and pytest fixtures to
|
||||
achieve it. We separate the creation of the fixture into a conftest.py
|
||||
file::
|
||||
file:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
@@ -934,19 +1009,23 @@ file::
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def cleandir():
|
||||
newpath = tempfile.mkdtemp()
|
||||
os.chdir(newpath)
|
||||
|
||||
and declare its use in a test module via a ``usefixtures`` marker::
|
||||
and declare its use in a test module via a ``usefixtures`` marker:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_setenv.py
|
||||
import os
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("cleandir")
|
||||
class TestDirectoryInit(object):
|
||||
class TestDirectoryInit:
|
||||
def test_cwd_starts_empty(self):
|
||||
assert os.listdir(os.getcwd()) == []
|
||||
with open("myfile", "w") as f:
|
||||
@@ -964,7 +1043,7 @@ to verify our fixture is activated and the tests pass:
|
||||
|
||||
$ pytest -q
|
||||
.. [100%]
|
||||
2 passed in 0.12 seconds
|
||||
2 passed in 0.01s
|
||||
|
||||
You can specify multiple fixtures like this:
|
||||
|
||||
@@ -1023,25 +1102,32 @@ without declaring a function argument explicitly or a `usefixtures`_ decorator.
|
||||
As a practical example, suppose we have a database fixture which has a
|
||||
begin/rollback/commit architecture and we want to automatically surround
|
||||
each test method by a transaction and a rollback. Here is a dummy
|
||||
self-contained implementation of this idea::
|
||||
self-contained implementation of this idea:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_db_transact.py
|
||||
|
||||
import pytest
|
||||
|
||||
class DB(object):
|
||||
|
||||
class DB:
|
||||
def __init__(self):
|
||||
self.intransaction = []
|
||||
|
||||
def begin(self, name):
|
||||
self.intransaction.append(name)
|
||||
|
||||
def rollback(self):
|
||||
self.intransaction.pop()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def db():
|
||||
return DB()
|
||||
|
||||
class TestClass(object):
|
||||
|
||||
class TestClass:
|
||||
@pytest.fixture(autouse=True)
|
||||
def transact(self, request, db):
|
||||
db.begin(request.function.__name__)
|
||||
@@ -1065,7 +1151,7 @@ If we run it, we get two passing tests:
|
||||
|
||||
$ pytest -q
|
||||
.. [100%]
|
||||
2 passed in 0.12 seconds
|
||||
2 passed in 0.01s
|
||||
|
||||
Here is how autouse fixtures work in other scopes:
|
||||
|
||||
@@ -1089,7 +1175,9 @@ Here is how autouse fixtures work in other scopes:
|
||||
Note that the above ``transact`` fixture may very well be a fixture that
|
||||
you want to make available in your project without having it generally
|
||||
active. The canonical way to do that is to put the transact definition
|
||||
into a conftest.py file **without** using ``autouse``::
|
||||
into a conftest.py file **without** using ``autouse``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
@pytest.fixture
|
||||
@@ -1098,10 +1186,12 @@ into a conftest.py file **without** using ``autouse``::
|
||||
yield
|
||||
db.rollback()
|
||||
|
||||
and then e.g. have a TestClass using it by declaring the need::
|
||||
and then e.g. have a TestClass using it by declaring the need:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.usefixtures("transact")
|
||||
class TestClass(object):
|
||||
class TestClass:
|
||||
def test_method1(self):
|
||||
...
|
||||
|
||||
@@ -1170,6 +1260,8 @@ Given the tests file structure is:
|
||||
|
||||
conftest.py
|
||||
# content of tests/conftest.py
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def username():
|
||||
return 'username'
|
||||
|
||||
@@ -122,4 +122,4 @@ Resources
|
||||
* Google:
|
||||
|
||||
* `Flaky Tests at Google and How We Mitigate Them <https://testing.googleblog.com/2016/05/flaky-tests-at-google-and-how-we.html>`_ by John Micco, 2016
|
||||
* `Where do Google's flaky tests come from? <https://docs.google.com/document/d/1mZ0-Kc97DI_F3tf_GBW_NB_aqka-P1jVOsFfufxqUUM/edit#heading=h.ec0r4fypsleh>`_ by Jeff Listfield, 2017
|
||||
* `Where do Google's flaky tests come from? <https://testing.googleblog.com/2017/04/where-do-our-flaky-tests-come-from.html>`_ by Jeff Listfield, 2017
|
||||
|
||||
@@ -21,19 +21,23 @@ funcarg for a test function is required. If a factory wants to
|
||||
re-use a resource across different scopes, it often used
|
||||
the ``request.cached_setup()`` helper to manage caching of
|
||||
resources. Here is a basic example how we could implement
|
||||
a per-session Database object::
|
||||
a per-session Database object:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
class Database(object):
|
||||
class Database:
|
||||
def __init__(self):
|
||||
print("database instance created")
|
||||
|
||||
def destroy(self):
|
||||
print("database instance destroyed")
|
||||
|
||||
|
||||
def pytest_funcarg__db(request):
|
||||
return request.cached_setup(setup=DataBase,
|
||||
teardown=lambda db: db.destroy,
|
||||
scope="session")
|
||||
return request.cached_setup(
|
||||
setup=DataBase, teardown=lambda db: db.destroy, scope="session"
|
||||
)
|
||||
|
||||
There are several limitations and difficulties with this approach:
|
||||
|
||||
@@ -68,7 +72,9 @@ Direct scoping of fixture/funcarg factories
|
||||
|
||||
Instead of calling cached_setup() with a cache scope, you can use the
|
||||
:ref:`@pytest.fixture <pytest.fixture>` decorator and directly state
|
||||
the scope::
|
||||
the scope:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def db(request):
|
||||
@@ -90,11 +96,13 @@ Previously, funcarg factories could not directly cause parametrization.
|
||||
You needed to specify a ``@parametrize`` decorator on your test function
|
||||
or implement a ``pytest_generate_tests`` hook to perform
|
||||
parametrization, i.e. calling a test multiple times with different value
|
||||
sets. pytest-2.3 introduces a decorator for use on the factory itself::
|
||||
sets. pytest-2.3 introduces a decorator for use on the factory itself:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.fixture(params=["mysql", "pg"])
|
||||
def db(request):
|
||||
... # use request.param
|
||||
... # use request.param
|
||||
|
||||
Here the factory will be invoked twice (with the respective "mysql"
|
||||
and "pg" values set as ``request.param`` attributes) and all of
|
||||
@@ -107,7 +115,9 @@ allow to re-use already written factories because effectively
|
||||
parametrized via
|
||||
:py:func:`~_pytest.python.Metafunc.parametrize(indirect=True)` calls.
|
||||
|
||||
Of course it's perfectly fine to combine parametrization and scoping::
|
||||
Of course it's perfectly fine to combine parametrization and scoping:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.fixture(scope="session", params=["mysql", "pg"])
|
||||
def db(request):
|
||||
@@ -128,7 +138,9 @@ No ``pytest_funcarg__`` prefix when using @fixture decorator
|
||||
|
||||
When using the ``@fixture`` decorator the name of the function
|
||||
denotes the name under which the resource can be accessed as a function
|
||||
argument::
|
||||
argument:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.fixture()
|
||||
def db(request):
|
||||
@@ -137,7 +149,9 @@ argument::
|
||||
The name under which the funcarg resource can be requested is ``db``.
|
||||
|
||||
You can still use the "old" non-decorator way of specifying funcarg factories
|
||||
aka::
|
||||
aka:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def pytest_funcarg__db(request):
|
||||
...
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
Installation and Getting Started
|
||||
===================================
|
||||
|
||||
**Pythons**: Python 2.7, 3.4, 3.5, 3.6, 3.7, Jython, PyPy-2.3
|
||||
**Pythons**: Python 3.5, 3.6, 3.7, PyPy3
|
||||
|
||||
**Platforms**: Unix/Posix and Windows
|
||||
**Platforms**: Linux and Windows
|
||||
|
||||
**PyPI package name**: `pytest <https://pypi.org/project/pytest/>`_
|
||||
|
||||
**Dependencies**: `py <https://pypi.org/project/py/>`_,
|
||||
`colorama (Windows) <https://pypi.org/project/colorama/>`_,
|
||||
|
||||
**Documentation as PDF**: `download latest <https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
|
||||
|
||||
``pytest`` is a framework that makes building simple and scalable tests easy. Tests are expressive and readable—no boilerplate code required. Get started in minutes with a small unit test or complex functional test for your application or library.
|
||||
@@ -20,26 +17,33 @@ Installation and Getting Started
|
||||
Install ``pytest``
|
||||
----------------------------------------
|
||||
|
||||
1. Run the following command in your command line::
|
||||
1. Run the following command in your command line:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
2. Check that you installed the correct version::
|
||||
2. Check that you installed the correct version:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest --version
|
||||
This is pytest version 4.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py
|
||||
This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py
|
||||
|
||||
.. _`simpletest`:
|
||||
|
||||
Create your first test
|
||||
----------------------------------------------------------
|
||||
|
||||
Create a simple test function with just four lines of code::
|
||||
Create a simple test function with just four lines of code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_sample.py
|
||||
def func(x):
|
||||
return x + 1
|
||||
|
||||
|
||||
def test_answer():
|
||||
assert func(3) == 5
|
||||
|
||||
@@ -49,8 +53,9 @@ That’s it. You can now execute the test function:
|
||||
|
||||
$ pytest
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 1 item
|
||||
|
||||
test_sample.py F [100%]
|
||||
@@ -63,8 +68,8 @@ That’s it. You can now execute the test function:
|
||||
E assert 4 == 5
|
||||
E + where 4 = func(3)
|
||||
|
||||
test_sample.py:5: AssertionError
|
||||
========================= 1 failed in 0.12 seconds =========================
|
||||
test_sample.py:6: AssertionError
|
||||
============================ 1 failed in 0.12s =============================
|
||||
|
||||
This test returns a failure report because ``func(3)`` does not return ``5``.
|
||||
|
||||
@@ -81,13 +86,18 @@ Run multiple tests
|
||||
Assert that a certain exception is raised
|
||||
--------------------------------------------------------------
|
||||
|
||||
Use the ``raises`` helper to assert that some code raises an exception::
|
||||
Use the :ref:`raises <assertraises>` helper to assert that some code raises an exception:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_sysexit.py
|
||||
import pytest
|
||||
|
||||
|
||||
def f():
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def test_mytest():
|
||||
with pytest.raises(SystemExit):
|
||||
f()
|
||||
@@ -98,22 +108,24 @@ Execute the test function with “quiet” reporting mode:
|
||||
|
||||
$ pytest -q test_sysexit.py
|
||||
. [100%]
|
||||
1 passed in 0.12 seconds
|
||||
1 passed in 0.01s
|
||||
|
||||
Group multiple tests in a class
|
||||
--------------------------------------------------------------
|
||||
|
||||
Once you develop multiple tests, you may want to group them into a class. pytest makes it easy to create a class containing more than one test::
|
||||
Once you develop multiple tests, you may want to group them into a class. pytest makes it easy to create a class containing more than one test:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_class.py
|
||||
class TestClass(object):
|
||||
class TestClass:
|
||||
def test_one(self):
|
||||
x = "this"
|
||||
assert 'h' in x
|
||||
assert "h" in x
|
||||
|
||||
def test_two(self):
|
||||
x = "hello"
|
||||
assert hasattr(x, 'check')
|
||||
assert hasattr(x, "check")
|
||||
|
||||
``pytest`` discovers all tests following its :ref:`Conventions for Python test discovery <test discovery>`, so it finds both ``test_`` prefixed functions. There is no need to subclass anything. We can simply run the module by passing its filename:
|
||||
|
||||
@@ -128,19 +140,21 @@ Once you develop multiple tests, you may want to group them into a class. pytest
|
||||
|
||||
def test_two(self):
|
||||
x = "hello"
|
||||
> assert hasattr(x, 'check')
|
||||
> assert hasattr(x, "check")
|
||||
E AssertionError: assert False
|
||||
E + where False = hasattr('hello', 'check')
|
||||
|
||||
test_class.py:8: AssertionError
|
||||
1 failed, 1 passed in 0.12 seconds
|
||||
1 failed, 1 passed in 0.02s
|
||||
|
||||
The first test passed and the second failed. You can easily see the intermediate values in the assertion to help you understand the reason for the failure.
|
||||
|
||||
Request a unique temporary directory for functional tests
|
||||
--------------------------------------------------------------
|
||||
|
||||
``pytest`` provides `Builtin fixtures/function arguments <https://docs.pytest.org/en/latest/builtin.html#builtinfixtures>`_ to request arbitrary resources, like a unique temporary directory::
|
||||
``pytest`` provides `Builtin fixtures/function arguments <https://docs.pytest.org/en/latest/builtin.html>`_ to request arbitrary resources, like a unique temporary directory:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_tmpdir.py
|
||||
def test_needsfiles(tmpdir):
|
||||
@@ -166,11 +180,13 @@ List the name ``tmpdir`` in the test function signature and ``pytest`` will look
|
||||
test_tmpdir.py:3: AssertionError
|
||||
--------------------------- Captured stdout call ---------------------------
|
||||
PYTEST_TMPDIR/test_needsfiles0
|
||||
1 failed in 0.12 seconds
|
||||
1 failed in 0.02s
|
||||
|
||||
More info on tmpdir handling is available at :ref:`Temporary directories and files <tmpdir handling>`.
|
||||
|
||||
Find out what kind of builtin :ref:`pytest fixtures <fixtures>` exist with the command::
|
||||
Find out what kind of builtin :ref:`pytest fixtures <fixtures>` exist with the command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --fixtures # shows builtin and custom fixtures
|
||||
|
||||
|
||||
@@ -7,18 +7,22 @@ Good Integration Practices
|
||||
Install package with pip
|
||||
-------------------------------------------------
|
||||
|
||||
For development, we recommend to use virtualenv_ environments and pip_
|
||||
for installing your application and any dependencies
|
||||
as well as the ``pytest`` package itself. This ensures your code and
|
||||
dependencies are isolated from the system Python installation.
|
||||
For development, we recommend you use venv_ for virtual environments and
|
||||
pip_ for installing your application and any dependencies,
|
||||
as well as the ``pytest`` package itself.
|
||||
This ensures your code and dependencies are isolated from your system Python installation.
|
||||
|
||||
First you need to place a ``setup.py`` file in the root of your package with the following minimum content::
|
||||
Next, place a ``setup.py`` file in the root of your package with the following minimum content:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(name="PACKAGENAME", packages=find_packages())
|
||||
|
||||
Where ``PACKAGENAME`` is the name of your package. You can then install your package in "editable" mode by running from the same directory::
|
||||
Where ``PACKAGENAME`` is the name of your package. You can then install your package in "editable" mode by running from the same directory:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install -e .
|
||||
|
||||
@@ -41,8 +45,8 @@ Conventions for Python test discovery
|
||||
* In those directories, search for ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
|
||||
* From those files, collect test items:
|
||||
|
||||
* ``test_`` prefixed test functions or methods outside of class
|
||||
* ``test_`` prefixed test functions or methods inside ``Test`` prefixed test classes (without an ``__init__`` method)
|
||||
* ``test`` prefixed test functions or methods outside of class
|
||||
* ``test`` prefixed test functions or methods inside ``Test`` prefixed test classes (without an ``__init__`` method)
|
||||
|
||||
For examples of how to customize your test discovery :doc:`example/pythoncollection`.
|
||||
|
||||
@@ -60,7 +64,9 @@ Tests outside application code
|
||||
|
||||
Putting tests into an extra directory outside your actual application code
|
||||
might be useful if you have many functional tests or for other reasons want
|
||||
to keep tests separate from actual application code (often a good idea)::
|
||||
to keep tests separate from actual application code (often a good idea):
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
setup.py
|
||||
mypkg/
|
||||
@@ -72,8 +78,18 @@ to keep tests separate from actual application code (often a good idea)::
|
||||
test_view.py
|
||||
...
|
||||
|
||||
This way your tests can run easily against an installed version
|
||||
of ``mypkg``.
|
||||
This has the following benefits:
|
||||
|
||||
* Your tests can run against an installed version after executing ``pip install .``.
|
||||
* Your tests can run against the local copy with an editable install after executing ``pip install --editable .``.
|
||||
* If you don't have a ``setup.py`` file and are relying on the fact that Python by default puts the current
|
||||
directory in ``sys.path`` to import your package, you can execute ``python -m pytest`` to execute the tests against the
|
||||
local copy directly, without using ``pip``.
|
||||
|
||||
.. note::
|
||||
|
||||
See :ref:`pythonpath` for more information about the difference between calling ``pytest`` and
|
||||
``python -m pytest``.
|
||||
|
||||
Note that using this scheme your test files must have **unique names**, because
|
||||
``pytest`` will import them as *top-level* modules since there are no packages
|
||||
@@ -82,7 +98,9 @@ be imported as ``test_app`` and ``test_view`` top-level modules by adding ``test
|
||||
``sys.path``.
|
||||
|
||||
If you need to have test modules with the same name, you might add ``__init__.py`` files to your
|
||||
``tests`` folder and subfolders, changing them to packages::
|
||||
``tests`` folder and subfolders, changing them to packages:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
setup.py
|
||||
mypkg/
|
||||
@@ -104,7 +122,9 @@ This is problematic if you are using a tool like `tox`_ to test your package in
|
||||
because you want to test the *installed* version of your package, not the local code from the repository.
|
||||
|
||||
In this situation, it is **strongly** suggested to use a ``src`` layout where application root package resides in a
|
||||
sub-directory of your root::
|
||||
sub-directory of your root:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
setup.py
|
||||
src/
|
||||
@@ -130,7 +150,9 @@ Tests as part of application code
|
||||
|
||||
Inlining test directories into your application package
|
||||
is useful if you have direct relation between tests and application modules and
|
||||
want to distribute them along with your application::
|
||||
want to distribute them along with your application:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
setup.py
|
||||
mypkg/
|
||||
@@ -143,7 +165,9 @@ want to distribute them along with your application::
|
||||
test_view.py
|
||||
...
|
||||
|
||||
In this scheme, it is easy to run your tests using the ``--pyargs`` option::
|
||||
In this scheme, it is easy to run your tests using the ``--pyargs`` option:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --pyargs mypkg
|
||||
|
||||
@@ -209,101 +233,4 @@ against your source code checkout, helping to detect packaging
|
||||
glitches.
|
||||
|
||||
|
||||
Integrating with setuptools / ``python setup.py test`` / ``pytest-runner``
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
You can integrate test runs into your setuptools based project
|
||||
with the `pytest-runner <https://pypi.org/project/pytest-runner/>`_ plugin.
|
||||
|
||||
Add this to ``setup.py`` file:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
# ...,
|
||||
setup_requires=["pytest-runner", ...],
|
||||
tests_require=["pytest", ...],
|
||||
# ...,
|
||||
)
|
||||
|
||||
|
||||
And create an alias into ``setup.cfg`` file:
|
||||
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[aliases]
|
||||
test=pytest
|
||||
|
||||
If you now type::
|
||||
|
||||
python setup.py test
|
||||
|
||||
this will execute your tests using ``pytest-runner``. As this is a
|
||||
standalone version of ``pytest`` no prior installation whatsoever is
|
||||
required for calling the test command. You can also pass additional
|
||||
arguments to pytest such as your test directory or other
|
||||
options using ``--addopts``.
|
||||
|
||||
You can also specify other pytest-ini options in your ``setup.cfg`` file
|
||||
by putting them into a ``[tool:pytest]`` section:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[tool:pytest]
|
||||
addopts = --verbose
|
||||
python_files = testing/*/*.py
|
||||
|
||||
|
||||
Manual Integration
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If for some reason you don't want/can't use ``pytest-runner``, you can write
|
||||
your own setuptools Test command for invoking pytest.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import sys
|
||||
|
||||
from setuptools.command.test import test as TestCommand
|
||||
|
||||
|
||||
class PyTest(TestCommand):
|
||||
user_options = [("pytest-args=", "a", "Arguments to pass to pytest")]
|
||||
|
||||
def initialize_options(self):
|
||||
TestCommand.initialize_options(self)
|
||||
self.pytest_args = ""
|
||||
|
||||
def run_tests(self):
|
||||
import shlex
|
||||
|
||||
# import here, cause outside the eggs aren't loaded
|
||||
import pytest
|
||||
|
||||
errno = pytest.main(shlex.split(self.pytest_args))
|
||||
sys.exit(errno)
|
||||
|
||||
|
||||
setup(
|
||||
# ...,
|
||||
tests_require=["pytest"],
|
||||
cmdclass={"pytest": PyTest},
|
||||
)
|
||||
|
||||
Now if you run::
|
||||
|
||||
python setup.py test
|
||||
|
||||
this will download ``pytest`` if needed and then run your tests
|
||||
as you would expect it to. You can pass a single string of arguments
|
||||
using the ``--pytest-args`` or ``-a`` command-line option. For example::
|
||||
|
||||
python setup.py test -a "--durations=5"
|
||||
|
||||
is equivalent to running ``pytest --durations=5``.
|
||||
|
||||
|
||||
.. include:: links.inc
|
||||
|
||||
@@ -4,10 +4,121 @@ Historical Notes
|
||||
This page lists features or behavior from previous versions of pytest which have changed over the years. They are
|
||||
kept here as a historical note so users looking at old code can find documentation related to them.
|
||||
|
||||
|
||||
.. _marker-revamp:
|
||||
|
||||
Marker revamp and iteration
|
||||
---------------------------
|
||||
|
||||
.. versionchanged:: 3.6
|
||||
|
||||
pytest's marker implementation traditionally worked by simply updating the ``__dict__`` attribute of functions to cumulatively add markers. As a result, markers would unintentionally be passed along class hierarchies in surprising ways. Further, the API for retrieving them was inconsistent, as markers from parameterization would be stored differently than markers applied using the ``@pytest.mark`` decorator and markers added via ``node.add_marker``.
|
||||
|
||||
This state of things made it technically next to impossible to use data from markers correctly without having a deep understanding of the internals, leading to subtle and hard to understand bugs in more advanced usages.
|
||||
|
||||
Depending on how a marker got declared/changed one would get either a ``MarkerInfo`` which might contain markers from sibling classes,
|
||||
``MarkDecorators`` when marks came from parameterization or from a ``node.add_marker`` call, discarding prior marks. Also ``MarkerInfo`` acts like a single mark, when it in fact represents a merged view on multiple marks with the same name.
|
||||
|
||||
On top of that markers were not accessible in the same way for modules, classes, and functions/methods.
|
||||
In fact, markers were only accessible in functions, even if they were declared on classes/modules.
|
||||
|
||||
A new API to access markers has been introduced in pytest 3.6 in order to solve the problems with
|
||||
the initial design, providing the :func:`_pytest.nodes.Node.iter_markers` method to iterate over
|
||||
markers in a consistent manner and reworking the internals, which solved a great deal of problems
|
||||
with the initial design.
|
||||
|
||||
|
||||
.. _update marker code:
|
||||
|
||||
Updating code
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The old ``Node.get_marker(name)`` function is considered deprecated because it returns an internal ``MarkerInfo`` object
|
||||
which contains the merged name, ``*args`` and ``**kwargs`` of all the markers which apply to that node.
|
||||
|
||||
In general there are two scenarios on how markers should be handled:
|
||||
|
||||
1. Marks overwrite each other. Order matters but you only want to think of your mark as a single item. E.g.
|
||||
``log_level('info')`` at a module level can be overwritten by ``log_level('debug')`` for a specific test.
|
||||
|
||||
In this case, use ``Node.get_closest_marker(name)``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# replace this:
|
||||
marker = item.get_marker("log_level")
|
||||
if marker:
|
||||
level = marker.args[0]
|
||||
|
||||
# by this:
|
||||
marker = item.get_closest_marker("log_level")
|
||||
if marker:
|
||||
level = marker.args[0]
|
||||
|
||||
2. Marks compose in an additive manner. E.g. ``skipif(condition)`` marks mean you just want to evaluate all of them,
|
||||
order doesn't even matter. You probably want to think of your marks as a set here.
|
||||
|
||||
In this case iterate over each mark and handle their ``*args`` and ``**kwargs`` individually.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# replace this
|
||||
skipif = item.get_marker("skipif")
|
||||
if skipif:
|
||||
for condition in skipif.args:
|
||||
# eval condition
|
||||
...
|
||||
|
||||
# by this:
|
||||
for skipif in item.iter_markers("skipif"):
|
||||
condition = skipif.args[0]
|
||||
# eval condition
|
||||
|
||||
|
||||
If you are unsure or have any questions, please consider opening
|
||||
`an issue <https://github.com/pytest-dev/pytest/issues>`_.
|
||||
|
||||
Related issues
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Here is a non-exhaustive list of issues fixed by the new implementation:
|
||||
|
||||
* Marks don't pick up nested classes (`#199 <https://github.com/pytest-dev/pytest/issues/199>`_).
|
||||
|
||||
* Markers stain on all related classes (`#568 <https://github.com/pytest-dev/pytest/issues/568>`_).
|
||||
|
||||
* Combining marks - args and kwargs calculation (`#2897 <https://github.com/pytest-dev/pytest/issues/2897>`_).
|
||||
|
||||
* ``request.node.get_marker('name')`` returns ``None`` for markers applied in classes (`#902 <https://github.com/pytest-dev/pytest/issues/902>`_).
|
||||
|
||||
* Marks applied in parametrize are stored as markdecorator (`#2400 <https://github.com/pytest-dev/pytest/issues/2400>`_).
|
||||
|
||||
* Fix marker interaction in a backward incompatible way (`#1670 <https://github.com/pytest-dev/pytest/issues/1670>`_).
|
||||
|
||||
* Refactor marks to get rid of the current "marks transfer" mechanism (`#2363 <https://github.com/pytest-dev/pytest/issues/2363>`_).
|
||||
|
||||
* Introduce FunctionDefinition node, use it in generate_tests (`#2522 <https://github.com/pytest-dev/pytest/issues/2522>`_).
|
||||
|
||||
* Remove named marker attributes and collect markers in items (`#891 <https://github.com/pytest-dev/pytest/issues/891>`_).
|
||||
|
||||
* skipif mark from parametrize hides module level skipif mark (`#1540 <https://github.com/pytest-dev/pytest/issues/1540>`_).
|
||||
|
||||
* skipif + parametrize not skipping tests (`#1296 <https://github.com/pytest-dev/pytest/issues/1296>`_).
|
||||
|
||||
* Marker transfer incompatible with inheritance (`#535 <https://github.com/pytest-dev/pytest/issues/535>`_).
|
||||
|
||||
More details can be found in the `original PR <https://github.com/pytest-dev/pytest/pull/3317>`_.
|
||||
|
||||
.. note::
|
||||
|
||||
in a future major relase of pytest we will introduce class based markers,
|
||||
at which point markers will no longer be limited to instances of :py:class:`Mark`.
|
||||
|
||||
|
||||
cache plugin integrated into the core
|
||||
-------------------------------------
|
||||
|
||||
.. versionadded:: 2.8
|
||||
|
||||
|
||||
The functionality of the :ref:`core cache <cache>` plugin was previously distributed
|
||||
as a third party plugin named ``pytest-cache``. The core plugin
|
||||
@@ -18,7 +129,7 @@ can only store/receive data between test runs that is json-serializable.
|
||||
funcargs and ``pytest_funcarg__``
|
||||
---------------------------------
|
||||
|
||||
.. versionchanged:: 2.3
|
||||
|
||||
|
||||
In versions prior to 2.3 there was no ``@pytest.fixture`` marker
|
||||
and you had to use a magic ``pytest_funcarg__NAME`` prefix
|
||||
@@ -30,7 +141,7 @@ functions.
|
||||
``@pytest.yield_fixture`` decorator
|
||||
-----------------------------------
|
||||
|
||||
.. versionchanged:: 2.10
|
||||
|
||||
|
||||
Prior to version 2.10, in order to use a ``yield`` statement to execute teardown code one
|
||||
had to mark a fixture using the ``yield_fixture`` marker. From 2.10 onward, normal
|
||||
@@ -41,7 +152,7 @@ and considered deprecated.
|
||||
``[pytest]`` header in ``setup.cfg``
|
||||
------------------------------------
|
||||
|
||||
.. versionchanged:: 3.0
|
||||
|
||||
|
||||
Prior to 3.0, the supported section name was ``[pytest]``. Due to how
|
||||
this may collide with some distutils commands, the recommended
|
||||
@@ -54,17 +165,19 @@ name is ``[pytest]``.
|
||||
Applying marks to ``@pytest.mark.parametrize`` parameters
|
||||
---------------------------------------------------------
|
||||
|
||||
.. versionchanged:: 3.1
|
||||
|
||||
|
||||
Prior to version 3.1 the supported mechanism for marking values
|
||||
used the syntax::
|
||||
used the syntax:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import pytest
|
||||
@pytest.mark.parametrize("test_input,expected", [
|
||||
("3+5", 8),
|
||||
("2+4", 6),
|
||||
pytest.mark.xfail(("6*9", 42),),
|
||||
])
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_input,expected", [("3+5", 8), ("2+4", 6), pytest.mark.xfail(("6*9", 42))]
|
||||
)
|
||||
def test_eval(test_input, expected):
|
||||
assert eval(test_input) == expected
|
||||
|
||||
@@ -78,7 +191,7 @@ The old syntax is planned to be removed in pytest-4.0.
|
||||
``@pytest.mark.parametrize`` argument names as a tuple
|
||||
------------------------------------------------------
|
||||
|
||||
.. versionchanged:: 2.4
|
||||
|
||||
|
||||
In versions prior to 2.4 one needed to specify the argument
|
||||
names as a tuple. This remains valid but the simpler ``"name1,name2,..."``
|
||||
@@ -89,7 +202,7 @@ it's easier to write and produces less line noise.
|
||||
setup: is now an "autouse fixture"
|
||||
----------------------------------
|
||||
|
||||
.. versionchanged:: 2.3
|
||||
|
||||
|
||||
During development prior to the pytest-2.3 release the name
|
||||
``pytest.setup`` was used but before the release it was renamed
|
||||
@@ -102,12 +215,16 @@ namely :ref:`autouse fixtures`
|
||||
Conditions as strings instead of booleans
|
||||
-----------------------------------------
|
||||
|
||||
.. versionchanged:: 2.4
|
||||
|
||||
|
||||
Prior to pytest-2.4 the only way to specify skipif/xfail conditions was
|
||||
to use strings::
|
||||
to use strings:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
@pytest.mark.skipif("sys.version_info >= (3,3)")
|
||||
def test_function():
|
||||
...
|
||||
@@ -139,17 +256,20 @@ dictionary which is constructed as follows:
|
||||
expression is applied.
|
||||
|
||||
The pytest ``config`` object allows you to skip based on a test
|
||||
configuration value which you might have added::
|
||||
configuration value which you might have added:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.skipif("not config.getvalue('db')")
|
||||
def test_function(...):
|
||||
def test_function():
|
||||
...
|
||||
|
||||
The equivalent with "boolean conditions" is::
|
||||
The equivalent with "boolean conditions" is:
|
||||
|
||||
@pytest.mark.skipif(not pytest.config.getvalue("db"),
|
||||
reason="--db was not specified")
|
||||
def test_function(...):
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.skipif(not pytest.config.getvalue("db"), reason="--db was not specified")
|
||||
def test_function():
|
||||
pass
|
||||
|
||||
.. note::
|
||||
@@ -162,14 +282,18 @@ The equivalent with "boolean conditions" is::
|
||||
``pytest.set_trace()``
|
||||
----------------------
|
||||
|
||||
.. versionchanged:: 2.4
|
||||
|
||||
Previous to version 2.4 to set a break point in code one needed to use ``pytest.set_trace()``::
|
||||
|
||||
Previous to version 2.4 to set a break point in code one needed to use ``pytest.set_trace()``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def test_function():
|
||||
...
|
||||
pytest.set_trace() # invoke PDB debugger and tracing
|
||||
pytest.set_trace() # invoke PDB debugger and tracing
|
||||
|
||||
|
||||
This is no longer needed and one can use the native ``import pdb;pdb.set_trace()`` call directly.
|
||||
@@ -179,7 +303,7 @@ For more details see :ref:`breakpoints`.
|
||||
"compat" properties
|
||||
-------------------
|
||||
|
||||
.. deprecated:: 3.9
|
||||
|
||||
|
||||
Access of ``Module``, ``Function``, ``Class``, ``Instance``, ``File`` and ``Item`` through ``Node`` instances have long
|
||||
been documented as deprecated, but started to emit warnings from pytest ``3.9`` and onward.
|
||||
|
||||
@@ -28,8 +28,9 @@ To execute it:
|
||||
|
||||
$ pytest
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 1 item
|
||||
|
||||
test_sample.py F [100%]
|
||||
@@ -43,7 +44,7 @@ To execute it:
|
||||
E + where 4 = inc(3)
|
||||
|
||||
test_sample.py:6: AssertionError
|
||||
========================= 1 failed in 0.12 seconds =========================
|
||||
============================ 1 failed in 0.12s =============================
|
||||
|
||||
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used.
|
||||
See :ref:`Getting Started <getstarted>` for more examples.
|
||||
@@ -60,7 +61,7 @@ Features
|
||||
|
||||
- Can run :ref:`unittest <unittest>` (including trial) and :ref:`nose <noseintegration>` test suites out of the box;
|
||||
|
||||
- Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (untested);
|
||||
- Python 3.5+ and PyPy 3;
|
||||
|
||||
- Rich plugin architecture, with over 315+ `external plugins <http://plugincompat.herokuapp.com>`_ and thriving community;
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ License
|
||||
|
||||
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
|
||||
|
||||
::
|
||||
.. code-block:: text
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2004-2017 Holger Krekel and others
|
||||
Copyright (c) 2004-2019 Holger Krekel and others
|
||||
|
||||
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
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
.. _`distribute docs`:
|
||||
.. _`distribute`: https://pypi.org/project/distribute/
|
||||
.. _`pip`: https://pypi.org/project/pip/
|
||||
.. _`venv`: https://docs.python.org/3/library/venv.html
|
||||
.. _`virtualenv`: https://pypi.org/project/virtualenv/
|
||||
.. _hudson: http://hudson-ci.org/
|
||||
.. _jenkins: http://jenkins-ci.org/
|
||||
|
||||
@@ -3,17 +3,21 @@
|
||||
Logging
|
||||
-------
|
||||
|
||||
.. versionadded:: 3.3
|
||||
.. versionchanged:: 3.4
|
||||
|
||||
|
||||
|
||||
pytest captures log messages of level ``WARNING`` or above automatically and displays them in their own section
|
||||
for each failed test in the same manner as captured stdout and stderr.
|
||||
|
||||
Running without options::
|
||||
Running without options:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest
|
||||
|
||||
Shows failed tests like so::
|
||||
Shows failed tests like so:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
----------------------- Captured stdlog call ----------------------
|
||||
test_reporting.py 26 WARNING text going to logger
|
||||
@@ -27,12 +31,16 @@ By default each captured log message shows the module, line number, log level
|
||||
and message.
|
||||
|
||||
If desired the log and date format can be specified to
|
||||
anything that the logging module supports by passing specific formatting options::
|
||||
anything that the logging module supports by passing specific formatting options:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --log-format="%(asctime)s %(levelname)s %(message)s" \
|
||||
--log-date-format="%Y-%m-%d %H:%M:%S"
|
||||
|
||||
Shows failed tests like so::
|
||||
Shows failed tests like so:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
----------------------- Captured stdlog call ----------------------
|
||||
2010-04-10 14:48:44 WARNING text going to logger
|
||||
@@ -51,7 +59,9 @@ These options can also be customized through ``pytest.ini`` file:
|
||||
log_date_format = %Y-%m-%d %H:%M:%S
|
||||
|
||||
Further it is possible to disable reporting of captured content (stdout,
|
||||
stderr and logs) on failed tests completely with::
|
||||
stderr and logs) on failed tests completely with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --show-capture=no
|
||||
|
||||
@@ -60,7 +70,9 @@ caplog fixture
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Inside tests it is possible to change the log level for the captured log
|
||||
messages. This is supported by the ``caplog`` fixture::
|
||||
messages. This is supported by the ``caplog`` fixture:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_foo(caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
@@ -68,59 +80,69 @@ messages. This is supported by the ``caplog`` fixture::
|
||||
|
||||
By default the level is set on the root logger,
|
||||
however as a convenience it is also possible to set the log level of any
|
||||
logger::
|
||||
logger:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_foo(caplog):
|
||||
caplog.set_level(logging.CRITICAL, logger='root.baz')
|
||||
caplog.set_level(logging.CRITICAL, logger="root.baz")
|
||||
pass
|
||||
|
||||
The log levels set are restored automatically at the end of the test.
|
||||
|
||||
It is also possible to use a context manager to temporarily change the log
|
||||
level inside a ``with`` block::
|
||||
level inside a ``with`` block:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_bar(caplog):
|
||||
with caplog.at_level(logging.INFO):
|
||||
pass
|
||||
|
||||
Again, by default the level of the root logger is affected but the level of any
|
||||
logger can be changed instead with::
|
||||
logger can be changed instead with:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_bar(caplog):
|
||||
with caplog.at_level(logging.CRITICAL, logger='root.baz'):
|
||||
with caplog.at_level(logging.CRITICAL, logger="root.baz"):
|
||||
pass
|
||||
|
||||
Lastly all the logs sent to the logger during the test run are made available on
|
||||
the fixture in the form of both the ``logging.LogRecord`` instances and the final log text.
|
||||
This is useful for when you want to assert on the contents of a message::
|
||||
This is useful for when you want to assert on the contents of a message:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_baz(caplog):
|
||||
func_under_test()
|
||||
for record in caplog.records:
|
||||
assert record.levelname != 'CRITICAL'
|
||||
assert 'wally' not in caplog.text
|
||||
assert record.levelname != "CRITICAL"
|
||||
assert "wally" not in caplog.text
|
||||
|
||||
For all the available attributes of the log records see the
|
||||
``logging.LogRecord`` class.
|
||||
|
||||
You can also resort to ``record_tuples`` if all you want to do is to ensure,
|
||||
that certain messages have been logged under a given logger name with a given
|
||||
severity and message::
|
||||
severity and message:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_foo(caplog):
|
||||
logging.getLogger().info('boo %s', 'arg')
|
||||
logging.getLogger().info("boo %s", "arg")
|
||||
|
||||
assert caplog.record_tuples == [
|
||||
('root', logging.INFO, 'boo arg'),
|
||||
]
|
||||
assert caplog.record_tuples == [("root", logging.INFO, "boo arg")]
|
||||
|
||||
You can call ``caplog.clear()`` to reset the captured log records in a test::
|
||||
You can call ``caplog.clear()`` to reset the captured log records in a test:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_something_with_clearing_records(caplog):
|
||||
some_method_that_creates_log_records()
|
||||
caplog.clear()
|
||||
your_test_method()
|
||||
assert ['Foo'] == [rec.message for rec in caplog.records]
|
||||
assert ["Foo"] == [rec.message for rec in caplog.records]
|
||||
|
||||
|
||||
The ``caplog.records`` attribute contains records from the current stage only, so
|
||||
@@ -133,7 +155,6 @@ the records for the ``setup`` and ``call`` stages during teardown like so:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def window(caplog):
|
||||
window = create_window()
|
||||
@@ -198,6 +219,9 @@ option names are:
|
||||
* ``log_file_format``
|
||||
* ``log_file_date_format``
|
||||
|
||||
You can call ``set_log_path()`` to customize the log_file path dynamically. This functionality
|
||||
is considered **experimental**.
|
||||
|
||||
.. _log_release_notes:
|
||||
|
||||
Release notes
|
||||
|
||||
163
doc/en/mark.rst
163
doc/en/mark.rst
@@ -1,9 +1,7 @@
|
||||
|
||||
.. _mark:
|
||||
|
||||
Marking test functions with attributes
|
||||
=================================================================
|
||||
|
||||
======================================
|
||||
|
||||
By using the ``pytest.mark`` helper you can easily set
|
||||
metadata on your test functions. There are
|
||||
@@ -17,8 +15,10 @@ some builtin markers, for example:
|
||||
to the same test function.
|
||||
|
||||
It's easy to create custom markers or to apply markers
|
||||
to whole test classes or modules. See :ref:`mark examples` for examples
|
||||
which also serve as documentation.
|
||||
to whole test classes or modules. Those markers can be used by plugins, and also
|
||||
are commonly used to :ref:`select tests <mark run>` on the command-line with the ``-m`` option.
|
||||
|
||||
See :ref:`mark examples` for examples which also serve as documentation.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -26,134 +26,53 @@ which also serve as documentation.
|
||||
:ref:`fixtures <fixtures>`.
|
||||
|
||||
|
||||
Raising errors on unknown marks: --strict
|
||||
-----------------------------------------
|
||||
Registering marks
|
||||
-----------------
|
||||
|
||||
When the ``--strict`` command-line flag is passed, any marks not registered in the ``pytest.ini`` file will trigger an error.
|
||||
|
||||
Marks can be registered like this:
|
||||
You can register custom marks in your ``pytest.ini`` file like this:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pytest]
|
||||
markers =
|
||||
slow
|
||||
slow: marks tests as slow (deselect with '-m "not slow"')
|
||||
serial
|
||||
|
||||
This can be used to prevent users mistyping mark names by accident. Test suites that want to enforce this
|
||||
should add ``--strict`` to ``addopts``:
|
||||
Note that everything after the ``:`` is an optional description.
|
||||
|
||||
Alternatively, you can register new markers programmatically in a
|
||||
:ref:`pytest_configure <initialization-hooks>` hook:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def pytest_configure(config):
|
||||
config.addinivalue_line(
|
||||
"markers", "env(name): mark test to run only on named environment"
|
||||
)
|
||||
|
||||
|
||||
Registered marks appear in pytest's help text and do not emit warnings (see the next section). It
|
||||
is recommended that third-party plugins always :ref:`register their markers <registering-markers>`.
|
||||
|
||||
.. _unknown-marks:
|
||||
|
||||
Raising errors on unknown marks
|
||||
-------------------------------
|
||||
|
||||
Unregistered marks applied with the ``@pytest.mark.name_of_the_mark`` decorator
|
||||
will always emit a warning in order to avoid silently doing something
|
||||
surprising due to mis-typed names. As described in the previous section, you can disable
|
||||
the warning for custom marks by registering them in your ``pytest.ini`` file or
|
||||
using a custom ``pytest_configure`` hook.
|
||||
|
||||
When the ``--strict-markers`` command-line flag is passed, any unknown marks applied
|
||||
with the ``@pytest.mark.name_of_the_mark`` decorator will trigger an error. You can
|
||||
enforce this validation in your project by adding ``--strict-markers`` to ``addopts``:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pytest]
|
||||
addopts = --strict
|
||||
addopts = --strict-markers
|
||||
markers =
|
||||
slow
|
||||
slow: marks tests as slow (deselect with '-m "not slow"')
|
||||
serial
|
||||
|
||||
|
||||
.. _marker-revamp:
|
||||
|
||||
Marker revamp and iteration
|
||||
---------------------------
|
||||
|
||||
.. versionadded:: 3.6
|
||||
|
||||
pytest's marker implementation traditionally worked by simply updating the ``__dict__`` attribute of functions to cumulatively add markers. As a result, markers would unintentionally be passed along class hierarchies in surprising ways. Further, the API for retrieving them was inconsistent, as markers from parameterization would be stored differently than markers applied using the ``@pytest.mark`` decorator and markers added via ``node.add_marker``.
|
||||
|
||||
This state of things made it technically next to impossible to use data from markers correctly without having a deep understanding of the internals, leading to subtle and hard to understand bugs in more advanced usages.
|
||||
|
||||
Depending on how a marker got declared/changed one would get either a ``MarkerInfo`` which might contain markers from sibling classes,
|
||||
``MarkDecorators`` when marks came from parameterization or from a ``node.add_marker`` call, discarding prior marks. Also ``MarkerInfo`` acts like a single mark, when it in fact represents a merged view on multiple marks with the same name.
|
||||
|
||||
On top of that markers were not accessible the same way for modules, classes, and functions/methods.
|
||||
In fact, markers were only accessible in functions, even if they were declared on classes/modules.
|
||||
|
||||
A new API to access markers has been introduced in pytest 3.6 in order to solve the problems with the initial design, providing :func:`_pytest.nodes.Node.iter_markers` method to iterate over markers in a consistent manner and reworking the internals, which solved great deal of problems with the initial design.
|
||||
|
||||
|
||||
.. _update marker code:
|
||||
|
||||
Updating code
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The old ``Node.get_marker(name)`` function is considered deprecated because it returns an internal ``MarkerInfo`` object
|
||||
which contains the merged name, ``*args`` and ``**kwargs`` of all the markers which apply to that node.
|
||||
|
||||
In general there are two scenarios on how markers should be handled:
|
||||
|
||||
1. Marks overwrite each other. Order matters but you only want to think of your mark as a single item. E.g.
|
||||
``log_level('info')`` at a module level can be overwritten by ``log_level('debug')`` for a specific test.
|
||||
|
||||
In this case, use ``Node.get_closest_marker(name)``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# replace this:
|
||||
marker = item.get_marker("log_level")
|
||||
if marker:
|
||||
level = marker.args[0]
|
||||
|
||||
# by this:
|
||||
marker = item.get_closest_marker("log_level")
|
||||
if marker:
|
||||
level = marker.args[0]
|
||||
|
||||
2. Marks compose in an additive manner. E.g. ``skipif(condition)`` marks mean you just want to evaluate all of them,
|
||||
order doesn't even matter. You probably want to think of your marks as a set here.
|
||||
|
||||
In this case iterate over each mark and handle their ``*args`` and ``**kwargs`` individually.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# replace this
|
||||
skipif = item.get_marker("skipif")
|
||||
if skipif:
|
||||
for condition in skipif.args:
|
||||
# eval condition
|
||||
...
|
||||
|
||||
# by this:
|
||||
for skipif in item.iter_markers("skipif"):
|
||||
condition = skipif.args[0]
|
||||
# eval condition
|
||||
|
||||
|
||||
If you are unsure or have any questions, please consider opening
|
||||
`an issue <https://github.com/pytest-dev/pytest/issues>`_.
|
||||
|
||||
Related issues
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Here is a non-exhaustive list of issues fixed by the new implementation:
|
||||
|
||||
* Marks don't pick up nested classes (`#199 <https://github.com/pytest-dev/pytest/issues/199>`_).
|
||||
|
||||
* Markers stain on all related classes (`#568 <https://github.com/pytest-dev/pytest/issues/568>`_).
|
||||
|
||||
* Combining marks - args and kwargs calculation (`#2897 <https://github.com/pytest-dev/pytest/issues/2897>`_).
|
||||
|
||||
* ``request.node.get_marker('name')`` returns ``None`` for markers applied in classes (`#902 <https://github.com/pytest-dev/pytest/issues/902>`_).
|
||||
|
||||
* Marks applied in parametrize are stored as markdecorator (`#2400 <https://github.com/pytest-dev/pytest/issues/2400>`_).
|
||||
|
||||
* Fix marker interaction in a backward incompatible way (`#1670 <https://github.com/pytest-dev/pytest/issues/1670>`_).
|
||||
|
||||
* Refactor marks to get rid of the current "marks transfer" mechanism (`#2363 <https://github.com/pytest-dev/pytest/issues/2363>`_).
|
||||
|
||||
* Introduce FunctionDefinition node, use it in generate_tests (`#2522 <https://github.com/pytest-dev/pytest/issues/2522>`_).
|
||||
|
||||
* Remove named marker attributes and collect markers in items (`#891 <https://github.com/pytest-dev/pytest/issues/891>`_).
|
||||
|
||||
* skipif mark from parametrize hides module level skipif mark (`#1540 <https://github.com/pytest-dev/pytest/issues/1540>`_).
|
||||
|
||||
* skipif + parametrize not skipping tests (`#1296 <https://github.com/pytest-dev/pytest/issues/1296>`_).
|
||||
|
||||
* Marker transfer incompatible with inheritance (`#535 <https://github.com/pytest-dev/pytest/issues/535>`_).
|
||||
|
||||
More details can be found in the `original PR <https://github.com/pytest-dev/pytest/pull/3317>`_.
|
||||
|
||||
.. note::
|
||||
|
||||
in a future major relase of pytest we will introduce class based markers,
|
||||
at which point markers will no longer be limited to instances of :py:class:`Mark`
|
||||
|
||||
@@ -8,46 +8,218 @@ Sometimes tests need to invoke functionality which depends
|
||||
on global settings or which invokes code which cannot be easily
|
||||
tested such as network access. The ``monkeypatch`` fixture
|
||||
helps you to safely set/delete an attribute, dictionary item or
|
||||
environment variable or to modify ``sys.path`` for importing.
|
||||
environment variable, or to modify ``sys.path`` for importing.
|
||||
|
||||
The ``monkeypatch`` fixture provides these helper methods for safely patching and mocking
|
||||
functionality in tests:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
monkeypatch.setattr(obj, name, value, raising=True)
|
||||
monkeypatch.delattr(obj, name, raising=True)
|
||||
monkeypatch.setitem(mapping, name, value)
|
||||
monkeypatch.delitem(obj, name, raising=True)
|
||||
monkeypatch.setenv(name, value, prepend=False)
|
||||
monkeypatch.delenv(name, raising=True)
|
||||
monkeypatch.syspath_prepend(path)
|
||||
monkeypatch.chdir(path)
|
||||
|
||||
All modifications will be undone after the requesting
|
||||
test function or fixture has finished. The ``raising``
|
||||
parameter determines if a ``KeyError`` or ``AttributeError``
|
||||
will be raised if the target of the set/deletion operation does not exist.
|
||||
|
||||
Consider the following scenarios:
|
||||
|
||||
1. Modifying the behavior of a function or the property of a class for a test e.g.
|
||||
there is an API call or database connection you will not make for a test but you know
|
||||
what the expected output should be. Use :py:meth:`monkeypatch.setattr` to patch the
|
||||
function or property with your desired testing behavior. This can include your own functions.
|
||||
Use :py:meth:`monkeypatch.delattr` to remove the function or property for the test.
|
||||
|
||||
2. Modifying the values of dictionaries e.g. you have a global configuration that
|
||||
you want to modify for certain test cases. Use :py:meth:`monkeypatch.setitem` to patch the
|
||||
dictionary for the test. :py:meth:`monkeypatch.delitem` can be used to remove items.
|
||||
|
||||
3. Modifying environment variables for a test e.g. to test program behavior if an
|
||||
environment variable is missing, or to set multiple values to a known variable.
|
||||
:py:meth:`monkeypatch.setenv` and :py:meth:`monkeypatch.delenv` can be used for
|
||||
these patches.
|
||||
|
||||
4. Use ``monkeypatch.setenv("PATH", value, prepend=os.pathsep)`` to modify ``$PATH``, and
|
||||
:py:meth:`monkeypatch.chdir` to change the context of the current working directory
|
||||
during a test.
|
||||
|
||||
5. Use :py:meth:`monkeypatch.syspath_prepend` to modify ``sys.path`` which will also
|
||||
call :py:meth:`pkg_resources.fixup_namespace_packages` and :py:meth:`importlib.invalidate_caches`.
|
||||
|
||||
See the `monkeypatch blog post`_ for some introduction material
|
||||
and a discussion of its motivation.
|
||||
|
||||
.. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
|
||||
|
||||
|
||||
Simple example: monkeypatching functions
|
||||
---------------------------------------------------
|
||||
----------------------------------------
|
||||
|
||||
If you want to pretend that ``os.expanduser`` returns a certain
|
||||
directory, you can use the :py:meth:`monkeypatch.setattr` method to
|
||||
patch this function before calling into a function which uses it::
|
||||
Consider a scenario where you are working with user directories. In the context of
|
||||
testing, you do not want your test to depend on the running user. ``monkeypatch``
|
||||
can be used to patch functions dependent on the user to always return a
|
||||
specific value.
|
||||
|
||||
# content of test_module.py
|
||||
import os.path
|
||||
def getssh(): # pseudo application code
|
||||
return os.path.join(os.path.expanduser("~admin"), '.ssh')
|
||||
In this example, :py:meth:`monkeypatch.setattr` is used to patch ``Path.home``
|
||||
so that the known testing path ``Path("/abc")`` is always used when the test is run.
|
||||
This removes any dependency on the running user for testing purposes.
|
||||
:py:meth:`monkeypatch.setattr` must be called before the function which will use
|
||||
the patched function is called.
|
||||
After the test function finishes the ``Path.home`` modification will be undone.
|
||||
|
||||
def test_mytest(monkeypatch):
|
||||
def mockreturn(path):
|
||||
return '/abc'
|
||||
monkeypatch.setattr(os.path, 'expanduser', mockreturn)
|
||||
.. code-block:: python
|
||||
|
||||
# contents of test_module.py with source code and the test
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def getssh():
|
||||
"""Simple function to return expanded homedir ssh path."""
|
||||
return Path.home() / ".ssh"
|
||||
|
||||
|
||||
def test_getssh(monkeypatch):
|
||||
# mocked return function to replace Path.home
|
||||
# always return '/abc'
|
||||
def mockreturn():
|
||||
return Path("/abc")
|
||||
|
||||
# Application of the monkeypatch to replace Path.home
|
||||
# with the behavior of mockreturn defined above.
|
||||
monkeypatch.setattr(Path, "home", mockreturn)
|
||||
|
||||
# Calling getssh() will use mockreturn in place of Path.home
|
||||
# for this test with the monkeypatch.
|
||||
x = getssh()
|
||||
assert x == '/abc/.ssh'
|
||||
assert x == Path("/abc/.ssh")
|
||||
|
||||
Here our test function monkeypatches ``os.path.expanduser`` and
|
||||
then calls into a function that calls it. After the test function
|
||||
finishes the ``os.path.expanduser`` modification will be undone.
|
||||
|
||||
example: preventing "requests" from remote operations
|
||||
Monkeypatching returned objects: building mock classes
|
||||
------------------------------------------------------
|
||||
|
||||
If you want to prevent the "requests" library from performing http
|
||||
requests in all your tests, you can do::
|
||||
:py:meth:`monkeypatch.setattr` can be used in conjunction with classes to mock returned
|
||||
objects from functions instead of values.
|
||||
Imagine a simple function to take an API url and return the json response.
|
||||
|
||||
# content of conftest.py
|
||||
.. code-block:: python
|
||||
|
||||
# contents of app.py, a simple API retrieval example
|
||||
import requests
|
||||
|
||||
|
||||
def get_json(url):
|
||||
"""Takes a URL, and returns the JSON."""
|
||||
r = requests.get(url)
|
||||
return r.json()
|
||||
|
||||
We need to mock ``r``, the returned response object for testing purposes.
|
||||
The mock of ``r`` needs a ``.json()`` method which returns a dictionary.
|
||||
This can be done in our test file by defining a class to represent ``r``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# contents of test_app.py, a simple test for our API retrieval
|
||||
# import requests for the purposes of monkeypatching
|
||||
import requests
|
||||
|
||||
# our app.py that includes the get_json() function
|
||||
# this is the previous code block example
|
||||
import app
|
||||
|
||||
# custom class to be the mock return value
|
||||
# will override the requests.Response returned from requests.get
|
||||
class MockResponse:
|
||||
|
||||
# mock json() method always returns a specific testing dictionary
|
||||
@staticmethod
|
||||
def json():
|
||||
return {"mock_key": "mock_response"}
|
||||
|
||||
|
||||
def test_get_json(monkeypatch):
|
||||
|
||||
# Any arguments may be passed and mock_get() will always return our
|
||||
# mocked object, which only has the .json() method.
|
||||
def mock_get(*args, **kwargs):
|
||||
return MockResponse()
|
||||
|
||||
# apply the monkeypatch for requests.get to mock_get
|
||||
monkeypatch.setattr(requests, "get", mock_get)
|
||||
|
||||
# app.get_json, which contains requests.get, uses the monkeypatch
|
||||
result = app.get_json("https://fakeurl")
|
||||
assert result["mock_key"] == "mock_response"
|
||||
|
||||
|
||||
``monkeypatch`` applies the mock for ``requests.get`` with our ``mock_get`` function.
|
||||
The ``mock_get`` function returns an instance of the ``MockResponse`` class, which
|
||||
has a ``json()`` method defined to return a known testing dictionary and does not
|
||||
require any outside API connection.
|
||||
|
||||
You can build the ``MockResponse`` class with the appropriate degree of complexity for
|
||||
the scenario you are testing. For instance, it could include an ``ok`` property that
|
||||
always returns ``True``, or return different values from the ``json()`` mocked method
|
||||
based on input strings.
|
||||
|
||||
This mock can be shared across tests using a ``fixture``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# contents of test_app.py, a simple test for our API retrieval
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
# app.py that includes the get_json() function
|
||||
import app
|
||||
|
||||
# custom class to be the mock return value of requests.get()
|
||||
class MockResponse:
|
||||
@staticmethod
|
||||
def json():
|
||||
return {"mock_key": "mock_response"}
|
||||
|
||||
|
||||
# monkeypatched requests.get moved to a fixture
|
||||
@pytest.fixture
|
||||
def mock_response(monkeypatch):
|
||||
"""Requests.get() mocked to return {'mock_key':'mock_response'}."""
|
||||
|
||||
def mock_get(*args, **kwargs):
|
||||
return MockResponse()
|
||||
|
||||
monkeypatch.setattr(requests, "get", mock_get)
|
||||
|
||||
|
||||
# notice our test uses the custom fixture instead of monkeypatch directly
|
||||
def test_get_json(mock_response):
|
||||
result = app.get_json("https://fakeurl")
|
||||
assert result["mock_key"] == "mock_response"
|
||||
|
||||
|
||||
Furthermore, if the mock was designed to be applied to all tests, the ``fixture`` could
|
||||
be moved to a ``conftest.py`` file and use the with ``autouse=True`` option.
|
||||
|
||||
|
||||
Global patch example: preventing "requests" from remote operations
|
||||
------------------------------------------------------------------
|
||||
|
||||
If you want to prevent the "requests" library from performing http
|
||||
requests in all your tests, you can do:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# contents of conftest.py
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def no_requests(monkeypatch):
|
||||
"""Remove requests.sessions.Session.request for all tests."""
|
||||
monkeypatch.delattr("requests.sessions.Session.request")
|
||||
|
||||
This autouse fixture will be executed for each test function and it
|
||||
@@ -81,6 +253,187 @@ so that any attempts within tests to create http requests will fail.
|
||||
See issue `#3290 <https://github.com/pytest-dev/pytest/issues/3290>`_ for details.
|
||||
|
||||
|
||||
Monkeypatching environment variables
|
||||
------------------------------------
|
||||
|
||||
If you are working with environment variables you often need to safely change the values
|
||||
or delete them from the system for testing purposes. ``monkeypatch`` provides a mechanism
|
||||
to do this using the ``setenv`` and ``delenv`` method. Our example code to test:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# contents of our original code file e.g. code.py
|
||||
import os
|
||||
|
||||
|
||||
def get_os_user_lower():
|
||||
"""Simple retrieval function.
|
||||
Returns lowercase USER or raises EnvironmentError."""
|
||||
username = os.getenv("USER")
|
||||
|
||||
if username is None:
|
||||
raise OSError("USER environment is not set.")
|
||||
|
||||
return username.lower()
|
||||
|
||||
There are two potential paths. First, the ``USER`` environment variable is set to a
|
||||
value. Second, the ``USER`` environment variable does not exist. Using ``monkeypatch``
|
||||
both paths can be safely tested without impacting the running environment:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# contents of our test file e.g. test_code.py
|
||||
import pytest
|
||||
|
||||
|
||||
def test_upper_to_lower(monkeypatch):
|
||||
"""Set the USER env var to assert the behavior."""
|
||||
monkeypatch.setenv("USER", "TestingUser")
|
||||
assert get_os_user_lower() == "testinguser"
|
||||
|
||||
|
||||
def test_raise_exception(monkeypatch):
|
||||
"""Remove the USER env var and assert EnvironmentError is raised."""
|
||||
monkeypatch.delenv("USER", raising=False)
|
||||
|
||||
with pytest.raises(OSError):
|
||||
_ = get_os_user_lower()
|
||||
|
||||
This behavior can be moved into ``fixture`` structures and shared across tests:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# contents of our test file e.g. test_code.py
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_env_user(monkeypatch):
|
||||
monkeypatch.setenv("USER", "TestingUser")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_env_missing(monkeypatch):
|
||||
monkeypatch.delenv("USER", raising=False)
|
||||
|
||||
|
||||
# notice the tests reference the fixtures for mocks
|
||||
def test_upper_to_lower(mock_env_user):
|
||||
assert get_os_user_lower() == "testinguser"
|
||||
|
||||
|
||||
def test_raise_exception(mock_env_missing):
|
||||
with pytest.raises(OSError):
|
||||
_ = get_os_user_lower()
|
||||
|
||||
|
||||
Monkeypatching dictionaries
|
||||
---------------------------
|
||||
|
||||
:py:meth:`monkeypatch.setitem` can be used to safely set the values of dictionaries
|
||||
to specific values during tests. Take this simplified connection string example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# contents of app.py to generate a simple connection string
|
||||
DEFAULT_CONFIG = {"user": "user1", "database": "db1"}
|
||||
|
||||
|
||||
def create_connection_string(config=None):
|
||||
"""Creates a connection string from input or defaults."""
|
||||
config = config or DEFAULT_CONFIG
|
||||
return f"User Id={config['user']}; Location={config['database']};"
|
||||
|
||||
For testing purposes we can patch the ``DEFAULT_CONFIG`` dictionary to specific values.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# contents of test_app.py
|
||||
# app.py with the connection string function (prior code block)
|
||||
import app
|
||||
|
||||
|
||||
def test_connection(monkeypatch):
|
||||
|
||||
# Patch the values of DEFAULT_CONFIG to specific
|
||||
# testing values only for this test.
|
||||
monkeypatch.setitem(app.DEFAULT_CONFIG, "user", "test_user")
|
||||
monkeypatch.setitem(app.DEFAULT_CONFIG, "database", "test_db")
|
||||
|
||||
# expected result based on the mocks
|
||||
expected = "User Id=test_user; Location=test_db;"
|
||||
|
||||
# the test uses the monkeypatched dictionary settings
|
||||
result = app.create_connection_string()
|
||||
assert result == expected
|
||||
|
||||
You can use the :py:meth:`monkeypatch.delitem` to remove values.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# contents of test_app.py
|
||||
import pytest
|
||||
|
||||
# app.py with the connection string function
|
||||
import app
|
||||
|
||||
|
||||
def test_missing_user(monkeypatch):
|
||||
|
||||
# patch the DEFAULT_CONFIG t be missing the 'user' key
|
||||
monkeypatch.delitem(app.DEFAULT_CONFIG, "user", raising=False)
|
||||
|
||||
# Key error expected because a config is not passed, and the
|
||||
# default is now missing the 'user' entry.
|
||||
with pytest.raises(KeyError):
|
||||
_ = app.create_connection_string()
|
||||
|
||||
|
||||
The modularity of fixtures gives you the flexibility to define
|
||||
separate fixtures for each potential mock and reference them in the needed tests.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# contents of test_app.py
|
||||
import pytest
|
||||
|
||||
# app.py with the connection string function
|
||||
import app
|
||||
|
||||
# all of the mocks are moved into separated fixtures
|
||||
@pytest.fixture
|
||||
def mock_test_user(monkeypatch):
|
||||
"""Set the DEFAULT_CONFIG user to test_user."""
|
||||
monkeypatch.setitem(app.DEFAULT_CONFIG, "user", "test_user")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_test_database(monkeypatch):
|
||||
"""Set the DEFAULT_CONFIG database to test_db."""
|
||||
monkeypatch.setitem(app.DEFAULT_CONFIG, "database", "test_db")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_missing_default_user(monkeypatch):
|
||||
"""Remove the user key from DEFAULT_CONFIG"""
|
||||
monkeypatch.delitem(app.DEFAULT_CONFIG, "user", raising=False)
|
||||
|
||||
|
||||
# tests reference only the fixture mocks that are needed
|
||||
def test_connection(mock_test_user, mock_test_database):
|
||||
|
||||
expected = "User Id=test_user; Location=test_db;"
|
||||
|
||||
result = app.create_connection_string()
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_missing_user(mock_missing_default_user):
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
_ = app.create_connection_string()
|
||||
|
||||
|
||||
.. currentmodule:: _pytest.monkeypatch
|
||||
|
||||
API Reference
|
||||
|
||||
@@ -12,7 +12,9 @@ Running tests written for nose
|
||||
Usage
|
||||
-------------
|
||||
|
||||
After :ref:`installation` type::
|
||||
After :ref:`installation` type:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python setup.py develop # make sure tests can import our package
|
||||
pytest # instead of 'nosetests'
|
||||
@@ -44,7 +46,7 @@ Unsupported idioms / known issues
|
||||
<https://github.com/pytest-dev/pytest/issues/377/>`_.
|
||||
|
||||
- nose imports test modules with the same import path (e.g.
|
||||
``tests.test_mod``) but different file system paths
|
||||
``tests.test_mode``) but different file system paths
|
||||
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
|
||||
by extending sys.path/import semantics. pytest does not do that
|
||||
but there is discussion in `#268 <https://github.com/pytest-dev/pytest/issues/268>`_ for adding some support. Note that
|
||||
|
||||
@@ -29,22 +29,22 @@ pytest enables test parametrization at several levels:
|
||||
|
||||
.. regendoc: wipe
|
||||
|
||||
.. versionadded:: 2.2
|
||||
.. versionchanged:: 2.4
|
||||
|
||||
|
||||
Several improvements.
|
||||
|
||||
The builtin :ref:`pytest.mark.parametrize ref` decorator enables
|
||||
parametrization of arguments for a test function. Here is a typical example
|
||||
of a test function that implements checking that a certain input leads
|
||||
to an expected output::
|
||||
to an expected output:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_expectation.py
|
||||
import pytest
|
||||
@pytest.mark.parametrize("test_input,expected", [
|
||||
("3+5", 8),
|
||||
("2+4", 6),
|
||||
("6*9", 42),
|
||||
])
|
||||
|
||||
|
||||
@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
|
||||
def test_eval(test_input, expected):
|
||||
assert eval(test_input) == expected
|
||||
|
||||
@@ -56,8 +56,9 @@ them in turn:
|
||||
|
||||
$ pytest
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 3 items
|
||||
|
||||
test_expectation.py ..F [100%]
|
||||
@@ -67,18 +68,29 @@ them in turn:
|
||||
|
||||
test_input = '6*9', expected = 42
|
||||
|
||||
@pytest.mark.parametrize("test_input,expected", [
|
||||
("3+5", 8),
|
||||
("2+4", 6),
|
||||
("6*9", 42),
|
||||
])
|
||||
@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
|
||||
def test_eval(test_input, expected):
|
||||
> assert eval(test_input) == expected
|
||||
E AssertionError: assert 54 == 42
|
||||
E + where 54 = eval('6*9')
|
||||
|
||||
test_expectation.py:8: AssertionError
|
||||
==================== 1 failed, 2 passed in 0.12 seconds ====================
|
||||
test_expectation.py:6: AssertionError
|
||||
======================= 1 failed, 2 passed in 0.12s ========================
|
||||
|
||||
.. note::
|
||||
|
||||
pytest by default escapes any non-ascii characters used in unicode strings
|
||||
for the parametrization because it has several downsides.
|
||||
If however you would like to use unicode strings in parametrization and see them in the terminal as is (non-escaped), use this option in your ``pytest.ini``:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pytest]
|
||||
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True
|
||||
|
||||
Keep in mind however that this might cause unwanted side effects and
|
||||
even bugs depending on the OS used and plugins currently installed, so use it at your own risk.
|
||||
|
||||
|
||||
As designed in this example, only one pair of input/output values fails
|
||||
the simple test function. And as usual with test function arguments,
|
||||
@@ -88,16 +100,18 @@ Note that you could also use the parametrize marker on a class or a module
|
||||
(see :ref:`mark`) which would invoke several functions with the argument sets.
|
||||
|
||||
It is also possible to mark individual test instances within parametrize,
|
||||
for example with the builtin ``mark.xfail``::
|
||||
for example with the builtin ``mark.xfail``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_expectation.py
|
||||
import pytest
|
||||
@pytest.mark.parametrize("test_input,expected", [
|
||||
("3+5", 8),
|
||||
("2+4", 6),
|
||||
pytest.param("6*9", 42,
|
||||
marks=pytest.mark.xfail),
|
||||
])
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_input,expected",
|
||||
[("3+5", 8), ("2+4", 6), pytest.param("6*9", 42, marks=pytest.mark.xfail)],
|
||||
)
|
||||
def test_eval(test_input, expected):
|
||||
assert eval(test_input) == expected
|
||||
|
||||
@@ -107,13 +121,14 @@ Let's run this:
|
||||
|
||||
$ pytest
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y
|
||||
cachedir: $PYTHON_PREFIX/.pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR
|
||||
collected 3 items
|
||||
|
||||
test_expectation.py ..x [100%]
|
||||
|
||||
=================== 2 passed, 1 xfailed in 0.12 seconds ====================
|
||||
======================= 2 passed, 1 xfailed in 0.12s =======================
|
||||
|
||||
The one parameter set which caused a failure previously now
|
||||
shows up as an "xfailed (expected to fail)" test.
|
||||
@@ -123,9 +138,13 @@ example, if they're dynamically generated by some function - the behaviour of
|
||||
pytest is defined by the :confval:`empty_parameter_set_mark` option.
|
||||
|
||||
To get all combinations of multiple parametrized arguments you can stack
|
||||
``parametrize`` decorators::
|
||||
``parametrize`` decorators:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize("x", [0, 1])
|
||||
@pytest.mark.parametrize("y", [2, 3])
|
||||
def test_foo(x, y):
|
||||
@@ -149,32 +168,44 @@ parametrization.
|
||||
|
||||
For example, let's say we want to run a test taking string inputs which
|
||||
we want to set via a new ``pytest`` command line option. Let's first write
|
||||
a simple test accepting a ``stringinput`` fixture function argument::
|
||||
a simple test accepting a ``stringinput`` fixture function argument:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_strings.py
|
||||
|
||||
|
||||
def test_valid_string(stringinput):
|
||||
assert stringinput.isalpha()
|
||||
|
||||
Now we add a ``conftest.py`` file containing the addition of a
|
||||
command line option and the parametrization of our test function::
|
||||
command line option and the parametrization of our test function:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--stringinput", action="append", default=[],
|
||||
help="list of stringinputs to pass to test functions")
|
||||
parser.addoption(
|
||||
"--stringinput",
|
||||
action="append",
|
||||
default=[],
|
||||
help="list of stringinputs to pass to test functions",
|
||||
)
|
||||
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
if 'stringinput' in metafunc.fixturenames:
|
||||
metafunc.parametrize("stringinput",
|
||||
metafunc.config.getoption('stringinput'))
|
||||
if "stringinput" in metafunc.fixturenames:
|
||||
metafunc.parametrize("stringinput", metafunc.config.getoption("stringinput"))
|
||||
|
||||
If we now pass two stringinput values, our test will run twice::
|
||||
If we now pass two stringinput values, our test will run twice:
|
||||
|
||||
.. code-block:: pytest
|
||||
|
||||
$ pytest -q --stringinput="hello" --stringinput="world" test_strings.py
|
||||
.. [100%]
|
||||
2 passed in 0.12 seconds
|
||||
2 passed in 0.01s
|
||||
|
||||
Let's also run with a stringinput that will lead to a failing test:
|
||||
|
||||
@@ -193,8 +224,8 @@ Let's also run with a stringinput that will lead to a failing test:
|
||||
E + where False = <built-in method isalpha of str object at 0xdeadbeef>()
|
||||
E + where <built-in method isalpha of str object at 0xdeadbeef> = '!'.isalpha
|
||||
|
||||
test_strings.py:3: AssertionError
|
||||
1 failed in 0.12 seconds
|
||||
test_strings.py:4: AssertionError
|
||||
1 failed in 0.02s
|
||||
|
||||
As expected our test function fails.
|
||||
|
||||
@@ -207,8 +238,8 @@ list:
|
||||
$ pytest -q -rs test_strings.py
|
||||
s [100%]
|
||||
========================= short test summary info ==========================
|
||||
SKIP [1] test_strings.py: got empty parameter set ['stringinput'], function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:1
|
||||
1 skipped in 0.12 seconds
|
||||
SKIPPED [1] test_strings.py: got empty parameter set ['stringinput'], function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:2
|
||||
1 skipped in 0.00s
|
||||
|
||||
Note that when calling ``metafunc.parametrize`` multiple times with different parameter sets, all parameter names across
|
||||
those sets cannot be duplicated, otherwise an error will be raised.
|
||||
|
||||
@@ -8,7 +8,9 @@ Installing and Using plugins
|
||||
This section talks about installing and using third party plugins.
|
||||
For writing your own plugins, please refer to :ref:`writing-plugins`.
|
||||
|
||||
Installing a third party plugin can be easily done with ``pip``::
|
||||
Installing a third party plugin can be easily done with ``pip``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install pytest-NAME
|
||||
pip uninstall pytest-NAME
|
||||
@@ -27,7 +29,7 @@ Here is a little annotated list for some popular plugins:
|
||||
for `twisted <http://twistedmatrix.com>`_ apps, starting a reactor and
|
||||
processing deferreds from test functions.
|
||||
|
||||
* `pytest-cov <https://pypi.org/project/pytest-cov/>`_:
|
||||
* `pytest-cov <https://pypi.org/project/pytest-cov/>`__:
|
||||
coverage reporting, compatible with distributed testing
|
||||
|
||||
* `pytest-xdist <https://pypi.org/project/pytest-xdist/>`_:
|
||||
@@ -84,13 +86,20 @@ will be loaded as well.
|
||||
:ref:`full explanation <requiring plugins in non-root conftests>`
|
||||
in the Writing plugins section.
|
||||
|
||||
.. note::
|
||||
The name ``pytest_plugins`` is reserved and should not be used as a
|
||||
name for a custom plugin module.
|
||||
|
||||
|
||||
.. _`findpluginname`:
|
||||
|
||||
Finding out which plugins are active
|
||||
------------------------------------
|
||||
|
||||
If you want to find out which plugins are active in your
|
||||
environment you can type::
|
||||
environment you can type:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest --trace-config
|
||||
|
||||
@@ -103,7 +112,9 @@ and their names. It will also print local plugins aka
|
||||
Deactivating / unregistering a plugin by name
|
||||
---------------------------------------------
|
||||
|
||||
You can prevent plugins from loading or unregister them::
|
||||
You can prevent plugins from loading or unregister them:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest -p no:NAME
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user