mirror of
https://github.com/evennia/evennia.git
synced 2026-03-18 13:56:30 +01:00
600 lines
No EOL
44 KiB
HTML
600 lines
No EOL
44 KiB
HTML
|
||
<!DOCTYPE html>
|
||
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>Unit Testing — Evennia 1.0-dev documentation</title>
|
||
<link rel="stylesheet" href="../_static/nature.css" type="text/css" />
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
||
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
|
||
<script src="../_static/jquery.js"></script>
|
||
<script src="../_static/underscore.js"></script>
|
||
<script src="../_static/doctools.js"></script>
|
||
<script src="../_static/language_data.js"></script>
|
||
<link rel="shortcut icon" href="../_static/favicon.ico"/>
|
||
<link rel="index" title="Index" href="../genindex.html" />
|
||
<link rel="search" title="Search" href="../search.html" />
|
||
</head><body>
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="General Index"
|
||
accesskey="I">index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia 1.0-dev</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">Unit Testing</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="document">
|
||
<div class="documentwrapper">
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<div class="section" id="unit-testing">
|
||
<h1>Unit Testing<a class="headerlink" href="#unit-testing" title="Permalink to this headline">¶</a></h1>
|
||
<p><em>Unit testing</em> means testing components of a program in isolation from each other to make sure every
|
||
part works on its own before using it with others. Extensive testing helps avoid new updates causing
|
||
unexpected side effects as well as alleviates general code rot (a more comprehensive wikipedia
|
||
article on unit testing can be found <a class="reference external" href="http://en.wikipedia.org/wiki/Unit_test">here</a>).</p>
|
||
<p>A typical unit test set calls some function or method with a given input, looks at the result and
|
||
makes sure that this result looks as expected. Rather than having lots of stand-alone test programs,
|
||
Evennia makes use of a central <em>test runner</em>. This is a program that gathers all available tests all
|
||
over the Evennia source code (called <em>test suites</em>) and runs them all in one go. Errors and
|
||
tracebacks are reported.</p>
|
||
<p>By default Evennia only tests itself. But you can also add your own tests to your game code and have
|
||
Evennia run those for you.</p>
|
||
<div class="section" id="running-the-evennia-test-suite">
|
||
<h2>Running the Evennia test suite<a class="headerlink" href="#running-the-evennia-test-suite" title="Permalink to this headline">¶</a></h2>
|
||
<p>To run the full Evennia test suite, go to your game folder and issue the command</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">evennia</span> <span class="n">test</span> <span class="n">evennia</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This will run all the evennia tests using the default settings. You could also run only a subset of
|
||
all tests by specifying a subpackage of the library:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">evennia</span> <span class="n">test</span> <span class="n">evennia</span><span class="o">.</span><span class="n">commands</span><span class="o">.</span><span class="n">default</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>A temporary database will be instantiated to manage the tests. If everything works out you will see
|
||
how many tests were run and how long it took. If something went wrong you will get error messages.
|
||
If you contribute to Evennia, this is a useful sanity check to see you haven’t introduced an
|
||
unexpected bug.</p>
|
||
</div>
|
||
<div class="section" id="running-tests-with-custom-settings-file">
|
||
<h2>Running tests with custom settings file<a class="headerlink" href="#running-tests-with-custom-settings-file" title="Permalink to this headline">¶</a></h2>
|
||
<p>If you have implemented your own tests for your game (see below) you can run them from your game dir
|
||
with</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">evennia</span> <span class="n">test</span> <span class="o">.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The period (<code class="docutils literal notranslate"><span class="pre">.</span></code>) means to run all tests found in the current directory and all subdirectories. You
|
||
could also specify, say, <code class="docutils literal notranslate"><span class="pre">typeclasses</span></code> or <code class="docutils literal notranslate"><span class="pre">world</span></code> if you wanted to just run tests in those subdirs.</p>
|
||
<p>Those tests will all be run using the default settings. To run the tests with your own settings file
|
||
you must use the <code class="docutils literal notranslate"><span class="pre">--settings</span></code> option:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">evennia</span> <span class="n">test</span> <span class="o">--</span><span class="n">settings</span> <span class="n">settings</span><span class="o">.</span><span class="n">py</span> <span class="o">.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">--settings</span></code> option of Evennia takes a file name in the <code class="docutils literal notranslate"><span class="pre">mygame/server/conf</span></code> folder. It is
|
||
normally used to swap settings files for testing and development. In combination with <code class="docutils literal notranslate"><span class="pre">test</span></code>, it
|
||
forces Evennia to use this settings file over the default one.</p>
|
||
</div>
|
||
<div class="section" id="writing-new-tests">
|
||
<h2>Writing new tests<a class="headerlink" href="#writing-new-tests" title="Permalink to this headline">¶</a></h2>
|
||
<p>Evennia’s test suite makes use of Django unit test system, which in turn relies on Python’s
|
||
<em>unittest</em> module.</p>
|
||
<blockquote>
|
||
<div><p>If you want to help out writing unittests for Evennia, take a look at Evennia’s <a class="reference external" href="https://coveralls.io/github/evennia/evennia">coveralls.io
|
||
page</a>. There you see which modules have any form of
|
||
test coverage and which does not.</p>
|
||
</div></blockquote>
|
||
<p>To make the test runner find the tests, they must be put in a module named <code class="docutils literal notranslate"><span class="pre">test*.py</span></code> (so <code class="docutils literal notranslate"><span class="pre">test.py</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">tests.py</span></code> etc). Such a test module will be found wherever it is in the package. It can be a good
|
||
idea to look at some of Evennia’s <code class="docutils literal notranslate"><span class="pre">tests.py</span></code> modules to see how they look.</p>
|
||
<p>Inside a testing file, a <code class="docutils literal notranslate"><span class="pre">unittest.TestCase</span></code> class is used to test a single aspect or component in
|
||
various ways. Each test case contains one or more <em>test methods</em> - these define the actual tests to
|
||
run. You can name the test methods anything you want as long as the name starts with “<code class="docutils literal notranslate"><span class="pre">test_</span></code>”.
|
||
Your <code class="docutils literal notranslate"><span class="pre">TestCase</span></code> class can also have a method <code class="docutils literal notranslate"><span class="pre">setUp()</span></code>. This is run before each test, setting up and
|
||
storing whatever preparations the test methods need. Conversely, a <code class="docutils literal notranslate"><span class="pre">tearDown()</span></code> method can
|
||
optionally do cleanup after each test.</p>
|
||
<p>To test the results, you use special methods of the <code class="docutils literal notranslate"><span class="pre">TestCase</span></code> class. Many of those start with
|
||
“<code class="docutils literal notranslate"><span class="pre">assert</span></code>”, such as <code class="docutils literal notranslate"><span class="pre">assertEqual</span></code> or <code class="docutils literal notranslate"><span class="pre">assertTrue</span></code>.</p>
|
||
<p>Example of a <code class="docutils literal notranslate"><span class="pre">TestCase</span></code> class:</p>
|
||
<div class="highlight-python notranslate"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11
|
||
12
|
||
13
|
||
14
|
||
15
|
||
16
|
||
17
|
||
18
|
||
19
|
||
20</pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="kn">import</span> <span class="nn">unittest</span>
|
||
|
||
<span class="c1"># the function we want to test</span>
|
||
<span class="kn">from</span> <span class="nn">mypath</span> <span class="kn">import</span> <span class="n">myfunc</span>
|
||
|
||
<span class="k">class</span> <span class="nc">TestObj</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
|
||
<span class="s2">"This tests a function myfunc."</span>
|
||
|
||
<span class="k">def</span> <span class="nf">test_return_value</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="s2">"test method. Makes sure return value is as expected."</span>
|
||
<span class="n">expected_return</span> <span class="o">=</span> <span class="s2">"This is me being nice."</span>
|
||
<span class="n">actual_return</span> <span class="o">=</span> <span class="n">myfunc</span><span class="p">()</span>
|
||
<span class="c1"># test </span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">expected_return</span><span class="p">,</span> <span class="n">actual_return</span><span class="p">)</span>
|
||
<span class="k">def</span> <span class="nf">test_alternative_call</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="s2">"test method. Calls with a keyword argument."</span>
|
||
<span class="n">expected_return</span> <span class="o">=</span> <span class="s2">"This is me being baaaad."</span>
|
||
<span class="n">actual_return</span> <span class="o">=</span> <span class="n">myfunc</span><span class="p">(</span><span class="n">bad</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
|
||
<span class="c1"># test</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="n">expected_return</span><span class="p">,</span> <span class="n">actual_return</span><span class="p">)</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>You might also want to read the <a class="reference external" href="http://docs.python.org/library/unittest.html">documentation for the unittest
|
||
module</a>.</p>
|
||
<div class="section" id="using-the-evenniatest-class">
|
||
<h3>Using the EvenniaTest class<a class="headerlink" href="#using-the-evenniatest-class" title="Permalink to this headline">¶</a></h3>
|
||
<p>Evennia offers a custom TestCase, the <code class="docutils literal notranslate"><span class="pre">evennia.utils.test_resources.EvenniaTest</span></code> class. This class
|
||
initiates a range of useful properties on themselves for testing Evennia systems. Examples are
|
||
<code class="docutils literal notranslate"><span class="pre">.account</span></code> and <code class="docutils literal notranslate"><span class="pre">.session</span></code> representing a mock connected Account and its Session and <code class="docutils literal notranslate"><span class="pre">.char1</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">char2</span></code> representing Characters complete with a location in the test database. These are all useful
|
||
when testing Evennia system requiring any of the default Evennia typeclasses as inputs. See the full
|
||
definition of the <code class="docutils literal notranslate"><span class="pre">EvenniaTest</span></code> class in
|
||
<a class="reference external" href="https://github.com/evennia/evennia/blob/master/evennia/utils/test_resources.py">evennia/utils/test_resources.py</a>.</p>
|
||
<div class="highlight-python notranslate"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="c1"># in a test module</span>
|
||
|
||
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTest</span>
|
||
|
||
<span class="k">class</span> <span class="nc">TestObject</span><span class="p">(</span><span class="n">EvenniaTest</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">test_object_search</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="c1"># char1 and char2 are both created in room1</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">char1</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">char2</span><span class="o">.</span><span class="n">key</span><span class="p">),</span> <span class="bp">self</span><span class="o">.</span><span class="n">char2</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">char1</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">char1</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">key</span><span class="p">),</span> <span class="bp">self</span><span class="o">.</span><span class="n">char1</span><span class="o">.</span><span class="n">location</span><span class="p">)</span>
|
||
<span class="c1"># ...</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
</div>
|
||
<div class="section" id="testing-in-game-commands">
|
||
<h3>Testing in-game Commands<a class="headerlink" href="#testing-in-game-commands" title="Permalink to this headline">¶</a></h3>
|
||
<p>In-game Commands are a special case. Tests for the default commands are put in
|
||
<code class="docutils literal notranslate"><span class="pre">evennia/commands/default/tests.py</span></code>. This uses a custom <code class="docutils literal notranslate"><span class="pre">CommandTest</span></code> class that inherits from
|
||
<code class="docutils literal notranslate"><span class="pre">evennia.utils.test_resources.EvenniaTest</span></code> described above. <code class="docutils literal notranslate"><span class="pre">CommandTest</span></code> supplies extra convenience
|
||
functions for executing commands and check that their return values (calls of <code class="docutils literal notranslate"><span class="pre">msg()</span></code> returns
|
||
expected values. It uses Characters and Sessions generated on the <code class="docutils literal notranslate"><span class="pre">EvenniaTest</span></code> class to call each
|
||
class).</p>
|
||
<p>Each command tested should have its own <code class="docutils literal notranslate"><span class="pre">TestCase</span></code> class. Inherit this class from the <code class="docutils literal notranslate"><span class="pre">CommandTest</span></code>
|
||
class in the same module to get access to the command-specific utilities mentioned.</p>
|
||
<div class="highlight-python notranslate"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11</pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="kn">from</span> <span class="nn">evennia.commands.default.tests</span> <span class="kn">import</span> <span class="n">CommandTest</span>
|
||
<span class="kn">from</span> <span class="nn">evennia.commands.default</span> <span class="kn">import</span> <span class="n">general</span>
|
||
<span class="k">class</span> <span class="nc">TestSet</span><span class="p">(</span><span class="n">CommandTest</span><span class="p">):</span>
|
||
<span class="s2">"tests the look command by simple call, using Char2 as a target"</span>
|
||
<span class="k">def</span> <span class="nf">test_mycmd_char</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">general</span><span class="o">.</span><span class="n">CmdLook</span><span class="p">(),</span> <span class="s2">"Char2"</span><span class="p">,</span> <span class="s2">"Char2(#7)"</span><span class="p">)</span>
|
||
<span class="s2">"tests the look command by simple call, with target as room"</span>
|
||
<span class="k">def</span> <span class="nf">test_mycmd_room</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">general</span><span class="o">.</span><span class="n">CmdLook</span><span class="p">(),</span> <span class="s2">"Room"</span><span class="p">,</span>
|
||
<span class="s2">"Room(#1)</span><span class="se">\n</span><span class="s2">room_desc</span><span class="se">\n</span><span class="s2">Exits: out(#3)</span><span class="se">\n</span><span class="s2">"</span>
|
||
<span class="s2">"You see: Obj(#4), Obj2(#5), Char2(#7)"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
</div>
|
||
<div class="section" id="unit-testing-contribs-with-custom-models">
|
||
<h3>Unit testing contribs with custom models<a class="headerlink" href="#unit-testing-contribs-with-custom-models" title="Permalink to this headline">¶</a></h3>
|
||
<p>A special case is if you were to create a contribution to go to the <code class="docutils literal notranslate"><span class="pre">evennia/contrib</span></code> folder that
|
||
uses its <a class="reference internal" href="../Concepts/New-Models.html"><span class="doc">own database models</span></a>. The problem with this is that Evennia (and Django) will
|
||
only recognize models in <code class="docutils literal notranslate"><span class="pre">settings.INSTALLED_APPS</span></code>. If a user wants to use your contrib, they will
|
||
be required to add your models to their settings file. But since contribs are optional you cannot
|
||
add the model to Evennia’s central <code class="docutils literal notranslate"><span class="pre">settings_default.py</span></code> file - this would always create your
|
||
optional models regardless of if the user wants them. But at the same time a contribution is a part
|
||
of the Evennia distribution and its unit tests should be run with all other Evennia tests using
|
||
<code class="docutils literal notranslate"><span class="pre">evennia</span> <span class="pre">test</span> <span class="pre">evennia</span></code>.</p>
|
||
<p>The way to do this is to only temporarily add your models to the <code class="docutils literal notranslate"><span class="pre">INSTALLED_APPS</span></code> directory when the
|
||
test runs. here is an example of how to do it.</p>
|
||
<blockquote>
|
||
<div><p>Note that this solution, derived from this [stackexchange
|
||
answer](http://stackoverflow.com/questions/502916/django-how-to-create-a-model-dynamically-just-for-
|
||
testing#503435) is currently untested! Please report your findings.</p>
|
||
</div></blockquote>
|
||
<div class="highlight-python notranslate"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11
|
||
12
|
||
13
|
||
14
|
||
15
|
||
16
|
||
17
|
||
18
|
||
19
|
||
20
|
||
21
|
||
22
|
||
23
|
||
24
|
||
25
|
||
26
|
||
27
|
||
28
|
||
29
|
||
30
|
||
31
|
||
32
|
||
33
|
||
34
|
||
35
|
||
36
|
||
37
|
||
38
|
||
39
|
||
40
|
||
41
|
||
42
|
||
43
|
||
44</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="c1"># a file contrib/mycontrib/tests.py</span>
|
||
|
||
<span class="kn">from</span> <span class="nn">django.conf</span> <span class="kn">import</span> <span class="n">settings</span>
|
||
<span class="kn">import</span> <span class="nn">django</span>
|
||
<span class="kn">from</span> <span class="nn">evennia.utils.test_resources</span> <span class="kn">import</span> <span class="n">EvenniaTest</span>
|
||
|
||
<span class="n">OLD_DEFAULT_SETTINGS</span> <span class="o">=</span> <span class="n">settings</span><span class="o">.</span><span class="n">INSTALLED_APPS</span>
|
||
<span class="n">DEFAULT_SETTINGS</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span>
|
||
<span class="n">INSTALLED_APPS</span><span class="o">=</span><span class="p">(</span>
|
||
<span class="s1">'contrib.mycontrib.tests'</span><span class="p">,</span>
|
||
<span class="p">),</span>
|
||
<span class="n">DATABASES</span><span class="o">=</span><span class="p">{</span>
|
||
<span class="s2">"default"</span><span class="p">:</span> <span class="p">{</span>
|
||
<span class="s2">"ENGINE"</span><span class="p">:</span> <span class="s2">"django.db.backends.sqlite3"</span>
|
||
<span class="p">}</span>
|
||
<span class="p">},</span>
|
||
<span class="n">SILENCED_SYSTEM_CHECKS</span><span class="o">=</span><span class="p">[</span><span class="s2">"1_7.W001"</span><span class="p">],</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="k">class</span> <span class="nc">TestMyModel</span><span class="p">(</span><span class="n">EvenniaTest</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">settings</span><span class="o">.</span><span class="n">configured</span><span class="p">:</span>
|
||
<span class="n">settings</span><span class="o">.</span><span class="n">configure</span><span class="p">(</span><span class="o">**</span><span class="n">DEFAULT_SETTINGS</span><span class="p">)</span>
|
||
<span class="n">django</span><span class="o">.</span><span class="n">setup</span><span class="p">()</span>
|
||
|
||
<span class="kn">from</span> <span class="nn">django.core.management</span> <span class="kn">import</span> <span class="n">call_command</span>
|
||
<span class="kn">from</span> <span class="nn">django.db.models</span> <span class="kn">import</span> <span class="n">loading</span>
|
||
<span class="n">loading</span><span class="o">.</span><span class="n">cache</span><span class="o">.</span><span class="n">loaded</span> <span class="o">=</span> <span class="bp">False</span>
|
||
<span class="n">call_command</span><span class="p">(</span><span class="s1">'syncdb'</span><span class="p">,</span> <span class="n">verbosity</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">tearDown</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="n">settings</span><span class="o">.</span><span class="n">configure</span><span class="p">(</span><span class="o">**</span><span class="n">OLD_DEFAULT_SETTINGS</span><span class="p">)</span>
|
||
<span class="n">django</span><span class="o">.</span><span class="n">setup</span><span class="p">()</span>
|
||
|
||
<span class="kn">from</span> <span class="nn">django.core.management</span> <span class="kn">import</span> <span class="n">call_command</span>
|
||
<span class="kn">from</span> <span class="nn">django.db.models</span> <span class="kn">import</span> <span class="n">loading</span>
|
||
<span class="n">loading</span><span class="o">.</span><span class="n">cache</span><span class="o">.</span><span class="n">loaded</span> <span class="o">=</span> <span class="bp">False</span>
|
||
<span class="n">call_command</span><span class="p">(</span><span class="s1">'syncdb'</span><span class="p">,</span> <span class="n">verbosity</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
|
||
|
||
<span class="c1"># test cases below ...</span>
|
||
|
||
<span class="k">def</span> <span class="nf">test_case</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="c1"># test case here</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
</div>
|
||
<div class="section" id="a-note-on-adding-new-tests">
|
||
<h3>A note on adding new tests<a class="headerlink" href="#a-note-on-adding-new-tests" title="Permalink to this headline">¶</a></h3>
|
||
<p>Having an extensive tests suite is very important for avoiding code degradation as Evennia is
|
||
developed. Only a small fraction of the Evennia codebase is covered by test suites at this point.
|
||
Writing new tests is not hard, it’s more a matter of finding the time to do so. So adding new tests
|
||
is really an area where everyone can contribute, also with only limited Python skills.</p>
|
||
</div>
|
||
<div class="section" id="a-note-on-making-the-test-runner-faster">
|
||
<h3>A note on making the test runner faster<a class="headerlink" href="#a-note-on-making-the-test-runner-faster" title="Permalink to this headline">¶</a></h3>
|
||
<p>If you have custom models with a large number of migrations, creating the test database can take a
|
||
very long time. If you don’t require migrations to run for your tests, you can disable them with the
|
||
django-test-without-migrations package. To install it, simply:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ pip install django-test-without-migrations
|
||
</pre></div>
|
||
</div>
|
||
<p>Then add it to your <code class="docutils literal notranslate"><span class="pre">INSTALLED_APPS</span></code> in your <code class="docutils literal notranslate"><span class="pre">server.conf.settings.py</span></code>:</p>
|
||
<div class="highlight-python notranslate"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
|
||
2
|
||
3
|
||
4</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">INSTALLED_APPS</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="c1"># ...</span>
|
||
<span class="s1">'test_without_migrations'</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>After doing so, you can then run tests without migrations by adding the <code class="docutils literal notranslate"><span class="pre">--nomigrations</span></code> argument:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">evennia</span> <span class="n">test</span> <span class="o">--</span><span class="n">settings</span> <span class="n">settings</span><span class="o">.</span><span class="n">py</span> <span class="o">--</span><span class="n">nomigrations</span> <span class="o">.</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="testing-for-game-development-mini-tutorial">
|
||
<h2>Testing for Game development (mini-tutorial)<a class="headerlink" href="#testing-for-game-development-mini-tutorial" title="Permalink to this headline">¶</a></h2>
|
||
<p>Unit testing can be of paramount importance to game developers. When starting with a new game, it is
|
||
recommended to look into unit testing as soon as possible; an already huge game is much harder to
|
||
write tests for. The benefits of testing a game aren’t different from the ones regarding library
|
||
testing. For example it is easy to introduce bugs that affect previously working code. Testing is
|
||
there to ensure your project behaves the way it should and continue to do so.</p>
|
||
<p>If you have never used unit testing (with Python or another language), you might want to check the
|
||
<a class="reference external" href="https://docs.python.org/2/library/unittest.html">official Python documentation about unit testing</a>,
|
||
particularly the first section dedicated to a basic example.</p>
|
||
<div class="section" id="basic-testing-using-evennia">
|
||
<h3>Basic testing using Evennia<a class="headerlink" href="#basic-testing-using-evennia" title="Permalink to this headline">¶</a></h3>
|
||
<p>Evennia’s test runner can be used to launch tests in your game directory (let’s call it ‘mygame’).
|
||
Evennia’s test runner does a few useful things beyond the normal Python unittest module:</p>
|
||
<ul class="simple">
|
||
<li><p>It creates and sets up an empty database, with some useful objects (accounts, characters and
|
||
rooms, among others).</p></li>
|
||
<li><p>It provides simple ways to test commands, which can be somewhat tricky at times, if not tested
|
||
properly.</p></li>
|
||
</ul>
|
||
<p>Therefore, you should use the command-line to execute the test runner, while specifying your own
|
||
game directories (not the one containing evennia). Go to your game directory (referred as ‘mygame’
|
||
in this section) and execute the test runner:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">evennia</span> <span class="o">--</span><span class="n">settings</span> <span class="n">settings</span><span class="o">.</span><span class="n">py</span> <span class="n">test</span> <span class="n">commands</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This command will execute Evennia’s test runner using your own settings file. It will set up a dummy
|
||
database of your choice and look into the ‘commands’ package defined in your game directory
|
||
(<code class="docutils literal notranslate"><span class="pre">mygame/commands</span></code> in this example) to find tests. The test module’s name should begin with ‘test’
|
||
and contain one or more <code class="docutils literal notranslate"><span class="pre">TestCase</span></code>. A full example can be found below.</p>
|
||
</div>
|
||
<div class="section" id="a-simple-example">
|
||
<h3>A simple example<a class="headerlink" href="#a-simple-example" title="Permalink to this headline">¶</a></h3>
|
||
<p>In your game directory, go to <code class="docutils literal notranslate"><span class="pre">commands</span></code> and create a new file <code class="docutils literal notranslate"><span class="pre">tests.py</span></code> inside (it could be named
|
||
anything starting with <code class="docutils literal notranslate"><span class="pre">test</span></code>). We will start by making a test that has nothing to do with Commands,
|
||
just to show how unit testing works:</p>
|
||
<div class="highlight-python notranslate"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11</pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="c1"># mygame/commands/tests.py</span>
|
||
|
||
<span class="kn">import</span> <span class="nn">unittest</span>
|
||
|
||
<span class="k">class</span> <span class="nc">TestString</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
|
||
|
||
<span class="sd">"""Unittest for strings (just a basic example)."""</span>
|
||
|
||
<span class="k">def</span> <span class="nf">test_upper</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="sd">"""Test the upper() str method."""</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="s1">'foo'</span><span class="o">.</span><span class="n">upper</span><span class="p">(),</span> <span class="s1">'FOO'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>This example, inspired from the Python documentation, is used to test the ‘upper()’ method of the
|
||
‘str’ class. Not very useful, but it should give you a basic idea of how tests are used.</p>
|
||
<p>Let’s execute that test to see if it works.</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">></span> <span class="n">evennia</span> <span class="o">--</span><span class="n">settings</span> <span class="n">settings</span><span class="o">.</span><span class="n">py</span> <span class="n">test</span> <span class="n">commands</span>
|
||
|
||
<span class="n">TESTING</span><span class="p">:</span> <span class="n">Using</span> <span class="n">specified</span> <span class="n">settings</span> <span class="n">file</span> <span class="s1">'server.conf.settings'</span><span class="o">.</span>
|
||
|
||
<span class="p">(</span><span class="n">Obs</span><span class="p">:</span> <span class="n">Evennia</span><span class="s1">'s full test suite may not pass if the settings are very</span>
|
||
<span class="n">different</span> <span class="kn">from</span> <span class="nn">the</span> <span class="n">default</span><span class="o">.</span> <span class="n">Use</span> <span class="s1">'test .'</span> <span class="k">as</span> <span class="n">arguments</span> <span class="n">to</span> <span class="n">run</span> <span class="n">only</span> <span class="n">tests</span>
|
||
<span class="n">on</span> <span class="n">the</span> <span class="n">game</span> <span class="nb">dir</span><span class="o">.</span><span class="p">)</span>
|
||
|
||
<span class="n">Creating</span> <span class="n">test</span> <span class="n">database</span> <span class="k">for</span> <span class="n">alias</span> <span class="s1">'default'</span><span class="o">...</span>
|
||
<span class="o">.</span>
|
||
<span class="o">----------------------------------------------------------------------</span>
|
||
<span class="n">Ran</span> <span class="mi">1</span> <span class="n">test</span> <span class="ow">in</span> <span class="mf">0.001</span><span class="n">s</span>
|
||
|
||
<span class="n">OK</span>
|
||
<span class="n">Destroying</span> <span class="n">test</span> <span class="n">database</span> <span class="k">for</span> <span class="n">alias</span> <span class="s1">'default'</span><span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We specified the <code class="docutils literal notranslate"><span class="pre">commands</span></code> package to the evennia test command since that’s where we put our test
|
||
file. In this case we could just as well just said <code class="docutils literal notranslate"><span class="pre">.</span></code> to search all of <code class="docutils literal notranslate"><span class="pre">mygame</span></code> for testing files.
|
||
If we have a lot of tests it may be useful to test only a single set at a time though. We get an
|
||
information text telling us we are using our custom settings file (instead of Evennia’s default
|
||
file) and then the test runs. The test passes! Change the “FOO” string to something else in the test
|
||
to see how it looks when it fails.</p>
|
||
</div>
|
||
<div class="section" id="testing-commands">
|
||
<h3>Testing commands<a class="headerlink" href="#testing-commands" title="Permalink to this headline">¶</a></h3>
|
||
<div class="admonition warning">
|
||
<p class="admonition-title">Warning</p>
|
||
<p>This is not correct anymore.</p>
|
||
</div>
|
||
<p>This section will test the proper execution of the ‘abilities’ command, as described in the DELETED
|
||
tutorial to create the ‘abilities’ command, we will need it to test it.</p>
|
||
<p>Testing commands in Evennia is a bit more complex than the simple testing example we have seen.
|
||
Luckily, Evennia supplies a special test class to do just that … we just need to inherit from it
|
||
and use it properly. This class is called ‘CommandTest’ and is defined in the
|
||
‘evennia.commands.default.tests’ package. To create a test for our ‘abilities’ command, we just
|
||
need to create a class that inherits from ‘CommandTest’ and add methods.</p>
|
||
<p>We could create a new test file for this but for now we just append to the <code class="docutils literal notranslate"><span class="pre">tests.py</span></code> file we
|
||
already have in <code class="docutils literal notranslate"><span class="pre">commands</span></code> from before.</p>
|
||
<div class="highlight-python notranslate"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11
|
||
12
|
||
13</pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="c1"># bottom of mygame/commands/tests.py</span>
|
||
|
||
<span class="kn">from</span> <span class="nn">evennia.commands.default.tests</span> <span class="kn">import</span> <span class="n">CommandTest</span>
|
||
|
||
<span class="kn">from</span> <span class="nn">commands.command</span> <span class="kn">import</span> <span class="n">CmdAbilities</span>
|
||
<span class="kn">from</span> <span class="nn">typeclasses.characters</span> <span class="kn">import</span> <span class="n">Character</span>
|
||
|
||
<span class="k">class</span> <span class="nc">TestAbilities</span><span class="p">(</span><span class="n">CommandTest</span><span class="p">):</span>
|
||
|
||
<span class="n">character_typeclass</span> <span class="o">=</span> <span class="n">Character</span>
|
||
|
||
<span class="k">def</span> <span class="nf">test_simple</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">CmdAbilities</span><span class="p">(),</span> <span class="s2">""</span><span class="p">,</span> <span class="s2">"STR: 5, AGI: 4, MAG: 2"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<ul class="simple">
|
||
<li><p>Line 1-4: we do some importing. ‘CommandTest’ is going to be our base class for our test, so we
|
||
need it. We also import our command (‘CmdAbilities’ in this case). Finally we import the
|
||
‘Character’ typeclass. We need it, since ‘CommandTest’ doesn’t use ‘Character’, but
|
||
‘DefaultCharacter’, which means the character calling the command won’t have the abilities we have
|
||
written in the ‘Character’ typeclass.</p></li>
|
||
<li><p>Line 6-8: that’s the body of our test. Here, a single command is tested in an entire class.
|
||
Default commands are usually grouped by category in a single class. There is no rule, as long as
|
||
you know where you put your tests. Note that we set the ‘character_typeclass’ class attribute to
|
||
Character. As explained above, if you didn’t do that, the system would create a ‘DefaultCharacter’
|
||
object, not a ‘Character’. You can try to remove line 4 and 8 to see what happens when running the
|
||
test.</p></li>
|
||
<li><p>Line 10-11: our unique testing method. Note its name: it should begin by ‘test_’. Apart from
|
||
that, the method is quite simple: it’s an instance method (so it takes the ‘self’ argument) but no
|
||
other arguments are needed. Line 11 uses the ‘call’ method, which is defined in ‘CommandTest’.
|
||
It’s a useful method that compares a command against an expected result. It would be like comparing
|
||
two strings with ‘assertEqual’, but the ‘call’ method does more things, including testing the
|
||
command in a realistic way (calling its hooks in the right order, so you don’t have to worry about
|
||
that).</p></li>
|
||
</ul>
|
||
<p>Line 11 can be understood as: test the ‘abilities’ command (first parameter), with no argument
|
||
(second parameter), and check that the character using it receives his/her abilities (third
|
||
parameter).</p>
|
||
<p>Let’s run our new test:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">></span> <span class="n">evennia</span> <span class="o">--</span><span class="n">settings</span> <span class="n">settings</span><span class="o">.</span><span class="n">py</span> <span class="n">test</span> <span class="n">commands</span>
|
||
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
|
||
<span class="n">Creating</span> <span class="n">test</span> <span class="n">database</span> <span class="k">for</span> <span class="n">alias</span> <span class="s1">'default'</span><span class="o">...</span>
|
||
<span class="o">..</span>
|
||
<span class="o">----------------------------------------------------------------------</span>
|
||
<span class="n">Ran</span> <span class="mi">2</span> <span class="n">tests</span> <span class="ow">in</span> <span class="mf">0.156</span><span class="n">s</span>
|
||
|
||
<span class="n">OK</span>
|
||
<span class="n">Destroying</span> <span class="n">test</span> <span class="n">database</span> <span class="k">for</span> <span class="n">alias</span> <span class="s1">'default'</span><span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Two tests were executed, since we have kept ‘TestString’ from last time. In case of failure, you
|
||
will get much more information to help you fix the bug.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="clearer"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||
<div class="sphinxsidebarwrapper">
|
||
<p class="logo"><a href="../index.html">
|
||
<img class="logo" src="../_static/evennia_logo.png" alt="Logo"/>
|
||
</a></p>
|
||
<div id="searchbox" style="display: none" role="search">
|
||
<h3 id="searchlabel">Quick search</h3>
|
||
<div class="searchformwrapper">
|
||
<form class="search" action="../search.html" method="get">
|
||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||
<input type="submit" value="Go" />
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<script>$('#searchbox').show(0);</script>
|
||
<p><h3><a href="../index.html">Table of Contents</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">Unit Testing</a><ul>
|
||
<li><a class="reference internal" href="#running-the-evennia-test-suite">Running the Evennia test suite</a></li>
|
||
<li><a class="reference internal" href="#running-tests-with-custom-settings-file">Running tests with custom settings file</a></li>
|
||
<li><a class="reference internal" href="#writing-new-tests">Writing new tests</a><ul>
|
||
<li><a class="reference internal" href="#using-the-evenniatest-class">Using the EvenniaTest class</a></li>
|
||
<li><a class="reference internal" href="#testing-in-game-commands">Testing in-game Commands</a></li>
|
||
<li><a class="reference internal" href="#unit-testing-contribs-with-custom-models">Unit testing contribs with custom models</a></li>
|
||
<li><a class="reference internal" href="#a-note-on-adding-new-tests">A note on adding new tests</a></li>
|
||
<li><a class="reference internal" href="#a-note-on-making-the-test-runner-faster">A note on making the test runner faster</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#testing-for-game-development-mini-tutorial">Testing for Game development (mini-tutorial)</a><ul>
|
||
<li><a class="reference internal" href="#basic-testing-using-evennia">Basic testing using Evennia</a></li>
|
||
<li><a class="reference internal" href="#a-simple-example">A simple example</a></li>
|
||
<li><a class="reference internal" href="#testing-commands">Testing commands</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<div role="note" aria-label="source link">
|
||
<!--h3>This Page</h3-->
|
||
<ul class="this-page-menu">
|
||
<li><a href="../_sources/Coding/Unit-Testing.md.txt"
|
||
rel="nofollow">Show Page Source</a></li>
|
||
</ul>
|
||
</div>
|
||
<h3>Versions</h3>
|
||
<ul>
|
||
<li><a href="Unit-Testing.html">1.0-dev (develop branch)</a></li>
|
||
<li><a href="../../0.9.5/index.html">0.9.5 (master branch)</a></li>
|
||
</ul>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="clearer"></div>
|
||
</div>
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="General Index"
|
||
>index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="nav-item nav-item-0"><a href="../index.html">Evennia 1.0-dev</a> »</li>
|
||
<li class="nav-item nav-item-this"><a href="">Unit Testing</a></li>
|
||
</ul>
|
||
</div>
|
||
<div class="footer" role="contentinfo">
|
||
© Copyright 2020, The Evennia developer community.
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
||
</div>
|
||
</body>
|
||
</html> |