|
<html> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<link rel="stylesheet" href="reveal.js/css/reveal.css"> |
|
<link rel="stylesheet" href="reveal.js/css/theme/black.css"> |
|
<link rel="stylesheet" href="reveal.js/lib/css/zenburn.css"> |
|
<style type="text/css"> |
|
.reveal table { |
|
color: white; |
|
} |
|
.text-right { |
|
text-align: right !important; |
|
} |
|
.reveal .slides ul { |
|
display: block; |
|
padding-left: 30px; |
|
} |
|
.reveal .slides ul li { |
|
list-style: none; |
|
font-size: 28px; |
|
text-indent: -24px; |
|
padding-top: 10px; |
|
} |
|
.reveal .slides ul li:before { |
|
content: "·"; |
|
font-size: 80px; |
|
vertical-align: middle; |
|
line-height: 20px; |
|
padding-right: 4px; |
|
} |
|
.smaller code { |
|
font-size: 18px; |
|
} |
|
.reveal .mono { |
|
font-family: monospace; |
|
} |
|
.reveal .code { |
|
font-family: monospace; |
|
background: #3f3f3f; |
|
color: #dcdcdc; |
|
padding: 2px 8px; |
|
font-size: 24px; |
|
} |
|
.reveal pre.inline { |
|
display: inline; |
|
position: static; |
|
padding: 26px 8px; |
|
border: none; |
|
box-shadow: none; |
|
} |
|
.reveal pre.inline code { |
|
display: inline; |
|
position: static; |
|
font-size: 24px; |
|
background: transparent; |
|
line-height: 36px; |
|
} |
|
.reveal pre { |
|
box-shadow: none; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="reveal"> |
|
<div class="slides"> |
|
|
|
<section> |
|
<pre><code class="python" style="text-align:center;font-size:42px;padding:42px;">import pytest</code></pre> |
|
<p>Mantas Zimnickas</p> |
|
<p> |
|
<small> |
|
<img src="pyconltlogo.png" style="border:none;vertical-align:middle;background:none;" alt="pyconlt logo"> |
|
PyConLT 2016 |
|
</small> |
|
</p> |
|
</section> |
|
|
|
<section> |
|
<h2>Demo</h2> |
|
</section> |
|
|
|
<section> |
|
<h2>unittest</h2> |
|
<pre><code class="python" data-trim data-noescape> |
|
import unittest |
|
|
|
class TestLen(unittest.TestCase): |
|
|
|
def test_len(self): |
|
self.assertEqual(len('foo'), 3) |
|
</code></pre> |
|
<ul> |
|
<li>Sukurtas pagal <strong>Kent Beck</strong> <em>„Simple Smalltalk Testing: With Patterns“</em> |
|
straipsnį.</li> |
|
<li>Straipsnis parašytas 1989 metais (prieš 27 metus).</li> |
|
<li>Pagal tą patį straipsnį sukurta nemažai <em>xUnit</em> stiliaus karkasų <em>SUnit</em>, |
|
<em>JUnit</em>, <em>RUnit</em>, <em>xUnit.net</em>, ...).</li> |
|
</ul> |
|
</section> |
|
|
|
<section> |
|
<h2>doctest</h2> |
|
<pre><code class="python" data-trim data-noescape> |
|
>>> len('foo') |
|
3 |
|
</code></pre> |
|
<ul> |
|
<li>Tinka paprastiems atvejams testuoti.</li> |
|
<li>Nėra padoraus testų pradinės būsenos (<em>angl. fixtures</em>) valdymo.</li> |
|
<li>Reikia atkartoti <em>Python REPL</em>, kas nėra labai patogu.</li> |
|
</ul> |
|
</section> |
|
|
|
<section> |
|
<h2>py.test</h2> |
|
<pre><code class="python" data-trim data-noescape> |
|
def test_len(): |
|
assert len('foo') == 3 |
|
</code></pre> |
|
<ul> |
|
<li>Geriausios dalys iš <em>unittest</em> ir <em>doctest</em> sudėtos į vieną.</li> |
|
</ul> |
|
</section> |
|
|
|
<section> |
|
<h2>Geriausias API, jokio API</h2> |
|
<table> |
|
<tbody class="mono"> |
|
<tr> |
|
<td><pre class="inline"><code class="python" data-trim data-noescape>assertEqual(a, b)</code></pre></td> |
|
<td><pre class="inline"><code class="python" data-trim data-noescape>assert a == b</code></pre></td> |
|
</tr> |
|
<tr> |
|
<td><pre class="inline"><code class="python" data-trim data-noescape>assertNotEqual(a, b)</code></pre></td> |
|
<td><pre class="inline"><code class="python" data-trim data-noescape>assert a != b</code></pre></td> |
|
</tr> |
|
<tr> |
|
<td><pre class="inline"><code class="python" data-trim data-noescape>assertTrue(a)</code></pre></td> |
|
<td><pre class="inline"><code class="python" data-trim data-noescape>assert a</code></pre></td> |
|
</tr> |
|
<tr> |
|
<td><pre class="inline"><code class="python" data-trim data-noescape>assertFalse(a)</code></pre></td> |
|
<td><pre class="inline"><code class="python" data-trim data-noescape>assert not a</code></pre></td> |
|
</tr> |
|
<tr> |
|
<td><pre class="inline"><code class="python" data-trim data-noescape>assertIn(a, b)</code></pre></td> |
|
<td><pre class="inline"><code class="python" data-trim data-noescape>assert a in b</code></pre></td> |
|
</tr> |
|
</tbody> |
|
</table> |
|
<ul> |
|
<li>Python <em>unittest</em> modulis turi <strong>33</strong> <span class="code">assert*</span> metodus.</li> |
|
</ul> |
|
</section> |
|
|
|
<section> |
|
<h2>Pranešimai apie klaidas</h2> |
|
<pre>$ nosetests test_pytest.py</pre> |
|
<pre class="smaller"><code data-trim data-noescape> |
|
Traceback (most recent call last): |
|
File "lib/python3.4/site-packages/nose/case.py", line 198, in runTest |
|
self.test(*self.arg) |
|
File "test_pytest.py", line 9, in test_len |
|
assert len('foo') == 2 |
|
AssertionError |
|
</code></pre> |
|
<pre>$ py.test --tb=native test_pytest.py</pre> |
|
<pre class="smaller"><code data-trim data-noescape> |
|
Traceback (most recent call last): |
|
File "test_pytest.py", line 9, in test_len |
|
assert len('foo') == 2 |
|
AssertionError: assert 3 == 2 |
|
+ where 3 = len('foo') |
|
</code></pre> |
|
</section> |
|
|
|
<section> |
|
<h2>Testų leidėjai</h2> |
|
<table> |
|
<thead> |
|
<tr> |
|
<th></th> |
|
<th>Atsisiuntimų sk.</th> |
|
<th>Atnaujinta</th> |
|
</tr> |
|
</thead> |
|
<tbody> |
|
<tr> |
|
<td>py.test</td> |
|
<td class="text-right">7,162,417</td> |
|
<td class="text-right">2016-03-18</td> |
|
</tr> |
|
<tr> |
|
<td>nose</td> |
|
<td class="text-right">26,967,204</td> |
|
<td class="text-right">2015-06-02</td> |
|
</tr> |
|
<tr> |
|
<td>nose2</td> |
|
<td class="text-right">268,291</td> |
|
<td class="text-right">2016-03-15</td> |
|
</tr> |
|
<tr> |
|
<td>zope.testrunner</td> |
|
<td class="text-right">194,450</td> |
|
<td class="text-right">2016-05-02</td> |
|
</tr> |
|
</tbody> |
|
</table> |
|
</section> |
|
|
|
<section> |
|
<h2>Testų leidimas</h2> |
|
<pre>$ py.test</pre> |
|
<pre>$ py.test tests</pre> |
|
<pre>$ py.test tests/test_foobar.py</pre> |
|
<pre>$ py.test tests/test_foobar.py::test_case</pre> |
|
<pre>$ py.test tests/test_foobar.py::TestClass.test_method</pre> |
|
</section> |
|
|
|
<section data-transition="none"> |
|
<h2>Testų žymėjimas</h2> |
|
<pre class="smaller"><code class="python" data-trim data-noescape> |
|
@pytest.mark.slow |
|
def test_len(): |
|
assert len('foo') == 3 |
|
</code></pre> |
|
<pre>$ py.test -m slow</pre> |
|
<pre>$ py.test -m 'not slow'</pre> |
|
</section> |
|
|
|
<section data-transition="none"> |
|
<h2>Pradinė būsena</h2> |
|
<pre class="smaller"><code class="python" data-trim data-noescape> |
|
@pytest.fixture |
|
def browser(): |
|
return selenium.webdriver.Firefox() |
|
|
|
def test_pytest(<mark>browser</mark>): |
|
browser.get('http://pytest.org/') |
|
assert 'pytest' in browser.title |
|
|
|
def test_pycon(<mark>browser</mark>): |
|
browser.get('http://pycon.lt/') |
|
assert 'PyCon' in browser.title |
|
</code></pre> |
|
</section> |
|
|
|
<section data-transition="none"> |
|
<h2>Pradinės būsenos apimtis</h2> |
|
<pre class="smaller"><code class="python" data-trim data-noescape> |
|
@pytest.fixture(<mark>scope='session'</mark>) |
|
def browser(): |
|
return selenium.webdriver.Firefox() |
|
|
|
def test_pytest(browser): |
|
browser.get('http://pytest.org/') |
|
assert 'pytest' in browser.title |
|
|
|
def test_pycon(browser): |
|
browser.get('http://pycon.lt/') |
|
assert 'PyCon' in browser.title |
|
</code></pre> |
|
</section> |
|
|
|
<section data-transition="none"> |
|
<h2>Pradinės būsenos atstatymas</h2> |
|
<pre class="smaller"><code class="python" data-trim data-noescape> |
|
@pytest.fixture(scope='session') |
|
def browser(<mark>request</mark>): |
|
driver = selenium.webdriver.Firefox() |
|
<mark>request.addfinalizer(driver.quit)</mark> |
|
return driver |
|
|
|
def test_pytest(browser): |
|
browser.get('http://pytest.org/') |
|
assert 'pytest' in browser.title |
|
|
|
def test_pycon(browser): |
|
browser.get('http://pycon.lt/') |
|
assert 'PyCon' in browser.title |
|
</code></pre> |
|
</section> |
|
|
|
<section data-transition="none"> |
|
<h2>Parametrizuota pradinė būsena</h2> |
|
<pre class="smaller"><code class="python" data-trim data-noescape> |
|
@pytest.fixture(scope='session', <mark>params=(Firefox, Chrome)</mark>) |
|
def browser(request): |
|
driver = <mark>request.param()</mark> |
|
request.addfinalizer(driver.quit) |
|
return driver |
|
|
|
def test_pytest(browser): |
|
browser.get('http://pytest.org/') |
|
assert 'pytest' in browser.title |
|
|
|
def test_pycon(browser): |
|
browser.get('http://pycon.lt/') |
|
assert 'PyCon' in browser.title |
|
</code></pre> |
|
</section> |
|
|
|
<section> |
|
<h2>Perrašoma pradinė būsena</h2> |
|
<pre class="smaller"><code class="python" data-trim data-noescape> |
|
tests/ |
|
conftest.py # aprašytas app projekto lygyje |
|
subfolder/ |
|
conftest.py # perrašytas app paketo lygyje |
|
test_foo.py # perrašytas app modulio lygyje |
|
def test_bar(app): # naudojamas app iš test_foo.py |
|
pass |
|
</code></pre> |
|
<ul> |
|
<li><span class="code">py.test --fixtures</span> išveda dokumentuotą pradinių būsenų sąrašą.</li> |
|
</ul> |
|
</section> |
|
|
|
<section> |
|
<h2>Kaip dėl mock dekoratorių?</h2> |
|
<ul> |
|
<li>Įprastinis <em>mock</em> naudojimas:</li> |
|
</ul> |
|
<pre class="smaller"><code class="python" data-trim data-noescape> |
|
@mock.patch('os.chmod') |
|
@mock.patch('os.remove') |
|
@mock.patch('os.listdir') |
|
def test_unix_fs(mocked_listdir, mocked_remove, mocked_chmod): |
|
pass |
|
</code></pre> |
|
<ul> |
|
<li><em>mocker</em> pradinės būsenos (<em>angl. fixture</em>) naudojimas:</li> |
|
</ul> |
|
<pre class="smaller"><code class="python" data-trim data-noescape> |
|
def test_unix_fs(mocker): |
|
mocker.patch('os.chmod') |
|
mocker.patch('os.remove') |
|
mocked_listdir = mocker.patch('os.listdir') |
|
</code></pre> |
|
<ul> |
|
<li><span class="code">pip install pytest-mock</span> ir visą tai veiks.</li> |
|
</ul> |
|
</section> |
|
|
|
<section> |
|
<h2>Žurnalai ir išvestys</h2> |
|
<ul> |
|
<li><span class="code">pip install pytest-catchlog</span></li> |
|
</ul> |
|
<pre class="smaller"><code class="python" data-trim data-noescape> |
|
def test_foo(caplog): |
|
caplog.set_level(logging.ERROR, logger='my.logger') |
|
assert caplog.text == 'foo bar' |
|
</code></pre> |
|
<pre class="smaller"><code class="txt" data-trim data-noescape> |
|
----------------------- Captured stdlog call ---------------------- |
|
test_reporting.py 26 INFO text going to logger |
|
----------------------- Captured stdout call ---------------------- |
|
text going to stdout |
|
----------------------- Captured stderr call ---------------------- |
|
text going to stderr |
|
==================== 2 failed in 0.02 seconds ===================== |
|
</code></pre> |
|
<ul> |
|
<li><span class="code">py.test -s</span> - neužlaikyti išvesties.</li> |
|
</ul> |
|
</section> |
|
|
|
<section> |
|
<h2>Įskiepiai</h2> |
|
<pre class="smaller"><code class="python" data-trim data-noescape> |
|
setup( |
|
entry_points = { |
|
'pytest11': ['name_of_plugin = myproject.pluginmodule'] |
|
}, |
|
) |
|
</code></pre> |
|
|
|
<pre class="smaller"><code class="python" data-trim data-noescape> |
|
@pytest.hookimpl(hookwrapper=True) |
|
def pytest_runtest_call(item): |
|
print('set up') |
|
outcome = yield |
|
print('tear down') |
|
</code></pre> |
|
</section> |
|
</div> |
|
</div> |
|
|
|
<script src="reveal.js/lib/js/head.min.js"></script> |
|
<script src="reveal.js/js/reveal.js"></script> |
|
|
|
<script> |
|
|
|
// Full list of configuration options available at: |
|
// https://github.com/hakimel/reveal.js#configuration |
|
Reveal.initialize({ |
|
controls: false, |
|
progress: true, |
|
history: true, |
|
center: true, |
|
|
|
transition: 'slide', // none/fade/slide/convex/concave/zoom |
|
|
|
// Optional reveal.js plugins |
|
dependencies: [ |
|
{ src: 'reveal.js/lib/js/classList.js', condition: function() { return !document.body.classList; } }, |
|
{ src: 'reveal.js/plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, |
|
{ src: 'reveal.js/plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, |
|
{ src: 'reveal.js/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }, |
|
{ src: 'reveal.js/plugin/zoom-js/zoom.js', async: true }, |
|
{ src: 'reveal.js/plugin/notes/notes.js', async: true } |
|
] |
|
}); |
|
|
|
</script> |
|
</body> |
|
</html> |