Updated HTML docs

This commit is contained in:
Griatch 2022-01-21 00:21:02 +01:00
parent 7c06220c24
commit 3aaf366163
89 changed files with 1540 additions and 1583 deletions

View file

@ -69,118 +69,195 @@ how many tests were run and how long it took. If something went wrong you will g
If you contribute to Evennia, this is a useful sanity check to see you havent introduced an
unexpected bug.</p>
</section>
<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
<section id="running-tests-for-your-game-dir">
<h2>Running tests for your game dir<a class="headerlink" href="#running-tests-for-your-game-dir" title="Permalink to this headline"></a></h2>
<p>If you have implemented your own tests for your game you can run them from your game dir
with</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test .
</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>
<p>An important thing to note is that those tests will all be run using the <em>default Evennia settings</em>.
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-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings settings.py .
</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>
<p>You can also test specific things by giving their path</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings settings.py .world.tests.YourTest
</pre></div>
</div>
</section>
<section id="writing-new-tests">
<h2>Writing new tests<a class="headerlink" href="#writing-new-tests" title="Permalink to this headline"></a></h2>
<p>Evennias test suite makes use of Django unit test system, which in turn relies on Pythons
<em>unittest</em> module.</p>
<blockquote>
<div><p>If you want to help out writing unittests for Evennia, take a look at Evennias <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 Evennias <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"><div class="highlight"><pre><span></span> <span class="kn">import</span> <span class="nn">unittest</span>
<p>Inside the module you need to put a class inheriting (at any distance) from <code class="docutils literal notranslate"><span class="pre">unittest.TestCase</span></code>. Each
method on that class that starts with <code class="docutils literal notranslate"><span class="pre">test_</span></code> will be run separately as a unit test. There
are two special, optional methods <code class="docutils literal notranslate"><span class="pre">setUp</span></code> and <code class="docutils literal notranslate"><span class="pre">tearDown</span></code> that will (if you define them) run before
<em>every</em> test. This can be useful for setting up and deleting things.</p>
<p>To actually test things, you use special <code class="docutils literal notranslate"><span class="pre">assert...</span></code> methods on the class. Most common on is
<code class="docutils literal notranslate"><span class="pre">assertEqual</span></code>, which makes sure a result is what you expect it to be.</p>
<p>Heres an example of the principle. Lets assume you put this in <code class="docutils literal notranslate"><span class="pre">mygame/world/tests.py</span></code>
and want to test a function in <code class="docutils literal notranslate"><span class="pre">mygame/world/myfunctions.py</span></code></p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="c1"># in a module tests.py somewhere i your game dir</span>
<span class="kn">import</span> <span class="nn">unittest</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">create_object</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="kn">from</span> <span class="nn">.myfunctions</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">&quot;This tests a function myfunc.&quot;</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="sd">&quot;&quot;&quot;done before every of the test_ * methods below&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="n">create_object</span><span class="p">(</span><span class="s2">&quot;mytestobject&quot;</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="sd">&quot;&quot;&quot;done after every test_* method below &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="o">.</span><span class="n">delete</span><span class="p">()</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">&quot;test method. Makes sure return value is as expected.&quot;</span>
<span class="n">expected_return</span> <span class="o">=</span> <span class="s2">&quot;This is me being nice.&quot;</span>
<span class="n">actual_return</span> <span class="o">=</span> <span class="n">myfunc</span><span class="p">()</span>
<span class="sd">&quot;&quot;&quot;test method. Makes sure return value is as expected.&quot;&quot;&quot;</span>
<span class="n">actual_return</span> <span class="o">=</span> <span class="n">myfunc</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">)</span>
<span class="n">expected_return</span> <span class="o">=</span> <span class="s2">&quot;This is the good object &#39;mytestobject&#39;.&quot;</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">&quot;test method. Calls with a keyword argument.&quot;</span>
<span class="n">expected_return</span> <span class="o">=</span> <span class="s2">&quot;This is me being baaaad.&quot;</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="kc">True</span><span class="p">)</span>
<span class="sd">&quot;&quot;&quot;test method. Calls with a keyword argument.&quot;&quot;&quot;</span>
<span class="n">actual_return</span> <span class="o">=</span> <span class="n">myfunc</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">obj</span><span class="p">,</span> <span class="n">bad</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">expected_return</span> <span class="o">=</span> <span class="s2">&quot;This is the baaad object &#39;mytestobject&#39;.&quot;</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>
</div>
<p>You might also want to read the <a class="reference external" href="https://docs.python.org/library/unittest.html">documentation for the unittest
module</a>.</p>
<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>
<p>To test this, run</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings settings.py .
</pre></div>
</div>
<p>to run the entire test module</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings setings.py .world.tests
</pre></div>
</div>
<p>or a specific class:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings settings.py .world.tests.TestObj
</pre></div>
</div>
<p>You can also run a specific test:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>evennia test --settings settings.py .world.tests.TestObj.test_alternative_call
</pre></div>
</div>
<p>You might also want to read the <a class="reference external" href="https://docs.python.org/library/unittest.html">Python documentation for the unittest module</a>.</p>
</section>
<section id="using-the-evennia-testing-classes">
<h2>Using the Evennia testing classes<a class="headerlink" href="#using-the-evennia-testing-classes" title="Permalink to this headline"></a></h2>
<p>Evennia offers many custom testing classes that helps with testing Evennia features.
They are all found in <a class="reference internal" href="../api/evennia.utils.test_resources.html#evennia-utils-test-resources"><span class="std std-ref">evennia.utils.test_resources</span></a>. Note that
these classes implement the <code class="docutils literal notranslate"><span class="pre">setUp</span></code> and <code class="docutils literal notranslate"><span class="pre">tearDown</span></code> already, so if you want to add stuff in them
yourself you should remember to use e.g. <code class="docutils literal notranslate"><span class="pre">super().setUp()</span></code> in your code.</p>
<section id="classes-for-testing-your-game-dir">
<h3>Classes for testing your game dir<a class="headerlink" href="#classes-for-testing-your-game-dir" title="Permalink to this headline"></a></h3>
<p>These all use whatever setting you pass to them and works well for testing code in your game dir.</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">EvenniaTest</span></code> - this sets up a full object environment for your test. All the created entities
can be accesses as properties on the class:</p>
<ul>
<li><p><code class="docutils literal notranslate"><span class="pre">.account</span></code> - A fake <a class="reference internal" href="../api/evennia.accounts.accounts.html#evennia.accounts.accounts.DefaultAccount" title="evennia.accounts.accounts.DefaultAccount"><span class="xref myst py py-class">Account</span></a> named “TestAccount”.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.account2</span></code> - Another account named “TestAccount2”</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">char1</span></code> - A <a class="reference internal" href="../api/evennia.objects.objects.html#evennia.objects.objects.DefaultCharacter" title="evennia.objects.objects.DefaultCharacter"><span class="xref myst py py-class">Character</span></a> linked to <code class="docutils literal notranslate"><span class="pre">.account</span></code>, named <code class="docutils literal notranslate"><span class="pre">Char</span></code>.
This has Developer permissions but is not a superuser.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.char2</span></code> - Another character linked to <code class="docutils literal notranslate"><span class="pre">account</span></code>, named <code class="docutils literal notranslate"><span class="pre">Char2</span></code>. This has base permissions (player).</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.obj1</span></code> - A regular <a class="reference internal" href="../api/evennia.objects.objects.html#evennia.objects.objects.DefaultObject" title="evennia.objects.objects.DefaultObject"><span class="xref myst py py-class">Object</span></a> named “Obj”.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.obj2</span></code> - Another object named “Obj2”.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.room1</span></code> - A <a class="reference internal" href="../api/evennia.objects.objects.html#evennia.objects.objects.DefaultRoom" title="evennia.objects.objects.DefaultRoom"><span class="xref myst py py-class">Room</span></a> named “Room”. Both characters and both
objects are located inside this room. It has a description of “room_desc”.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.room2</span></code> - Another room named “Room2”. It is empty and has no set description.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.exit</span></code> - An exit named “out” that leads from <code class="docutils literal notranslate"><span class="pre">.room1</span></code> to <code class="docutils literal notranslate"><span class="pre">.room2</span></code>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.script</span></code> - A <a class="reference internal" href="../api/evennia.scripts.scripts.html#evennia.scripts.scripts.DefaultScript" title="evennia.scripts.scripts.DefaultScript"><span class="xref myst py py-class">Script</span></a> named “Script”. Its an inert script
without a timing component.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.session</span></code> - A fake <a class="reference internal" href="../api/evennia.server.serversession.html#evennia.server.serversession.ServerSession" title="evennia.server.serversession.ServerSession"><span class="xref myst py py-class">Session</span></a> that mimics a player
connecting to the game. It is used by <code class="docutils literal notranslate"><span class="pre">.account1</span></code> and has a sessid of 1.</p></li>
</ul>
</li>
<li><p><code class="docutils literal notranslate"><span class="pre">EvenniaCommandTest</span></code> - has the same environment like <code class="docutils literal notranslate"><span class="pre">EvenniaTest</span></code> but also adds a special
<a class="reference internal" href="../api/evennia.utils.test_resources.html#evennia.utils.test_resources.EvenniaCommandTestMixin.call" title="evennia.utils.test_resources.EvenniaCommandTestMixin.call"><span class="xref myst py py-meth">.call()</span></a> method specifically for
testing Evennia <a class="reference internal" href="../Components/Commands.html"><span class="doc std std-doc">Commands</span></a>. It allows you to compare what the command <em>actually</em>
returns to the player with what you expect. Read the <code class="docutils literal notranslate"><span class="pre">call</span></code> api doc for more info.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">EvenniaTestCase</span></code> - This is identical to the regular Python <code class="docutils literal notranslate"><span class="pre">TestCase</span></code> class, its
just there for naming symmetry with <code class="docutils literal notranslate"><span class="pre">BaseEvenniaTestCase</span></code> below.</p></li>
</ul>
<p>Heres an example of using <code class="docutils literal notranslate"><span class="pre">EvenniaTest</span></code></p>
<div class="highlight-python notranslate"><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">BaseEvenniaTest</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">BaseEvenniaTest</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="k">class</span> <span class="nc">TestObject</span><span class="p">(</span><span class="n">EvenniaTest</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Remember that the testing class creates char1 and char2 inside room1 ...&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="nf">test_object_search_character</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Check that char1 can search for char2 by name&quot;&quot;&quot;</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="k">def</span> <span class="nf">test_location_search</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Check so that char1 can find the current location by name&quot;&quot;&quot;</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>
</div>
</section>
<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"><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">&quot;tests the look command by simple call, using Char2 as a target&quot;</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">&quot;Char2&quot;</span><span class="p">,</span> <span class="s2">&quot;Char2(#7)&quot;</span><span class="p">)</span>
<p>This example tests a custom command.</p>
<div class="highlight-python notranslate"><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">EvenniaCommandTest</span>
<span class="kn">from</span> <span class="nn">commands</span> <span class="kn">import</span> <span class="n">command</span> <span class="k">as</span> <span class="n">mycommand</span>
<span class="k">class</span> <span class="nc">TestSet</span><span class="p">(</span><span class="n">EvenniaCommandTest</span><span class="p">):</span>
<span class="s2">&quot;tests the look command by simple call, using Char2 as a target&quot;</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">mycommand</span><span class="o">.</span><span class="n">CmdMyLook</span><span class="p">(),</span> <span class="s2">&quot;Char2&quot;</span><span class="p">,</span> <span class="s2">&quot;Char2(#7)&quot;</span><span class="p">)</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="s2">&quot;tests the look command by simple call, with target as room&quot;</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">&quot;Room&quot;</span><span class="p">,</span>
<span class="s2">&quot;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">&quot;</span>
<span class="s2">&quot;You see: Obj(#4), Obj2(#5), Char2(#7)&quot;</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">mycommand</span><span class="o">.</span><span class="n">CmdMyLook</span><span class="p">(),</span> <span class="s2">&quot;Room&quot;</span><span class="p">,</span>
<span class="s2">&quot;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">&quot;</span>
<span class="s2">&quot;You see: Obj(#4), Obj2(#5), Char2(#7)&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>When using <code class="docutils literal notranslate"><span class="pre">.call</span></code>, you dont need to specify the entire string; you can just give the beginning
of it and if it matches, thats enough. Use <code class="docutils literal notranslate"><span class="pre">\n</span></code> to denote line breaks and (this is a special for
the <code class="docutils literal notranslate"><span class="pre">.call</span></code> helper), <code class="docutils literal notranslate"><span class="pre">||</span></code> to indicate multiple uses of <code class="docutils literal notranslate"><span class="pre">.msg()</span></code> in the Command. The <code class="docutils literal notranslate"><span class="pre">.call</span></code> helper
has a lot of arguments for mimicing different ways of calling a Command, so make sure to
<a class="reference internal" href="../api/evennia.utils.test_resources.html#evennia.utils.test_resources.EvenniaCommandTestMixin.call" title="evennia.utils.test_resources.EvenniaCommandTestMixin.call"><span class="xref myst py py-meth">read the API docs for .call()</span></a>.</p>
</section>
<section id="classes-for-testing-evennia-core">
<h3>Classes for testing Evennia core<a class="headerlink" href="#classes-for-testing-evennia-core" title="Permalink to this headline"></a></h3>
<p>These are used for testing Evennia itself. They provide the same resources as the classes
above but enforce Evennias default settings found in <code class="docutils literal notranslate"><span class="pre">evennia/settings_default.py</span></code>, ignoring
any settings changes in your game dir.</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">BaseEvenniaTest</span></code> - all the default objects above but with enforced default settings</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">BaseEvenniaCommandTest</span></code> - for testing Commands, but with enforced default settings</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">BaseEvenniaTestCase</span></code> - no default objects, only enforced default settings</p></li>
</ul>
<p>There are also two special mixin classes. These are uses in the classes above, but may also
be useful if you want to mix your own testing classes:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">EvenniaTestMixin</span></code> - A class mixin that creates all test environment objects.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">EvenniaCommandMixin</span></code> - A class mixin that adds the <code class="docutils literal notranslate"><span class="pre">.call()</span></code> Command-tester helper.</p></li>
</ul>
<p>If you want to help out writing unittests for Evennia, take a look at Evennias <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. All help is appreciated!</p>
</section>
</section>
<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>
<h2>Unit testing contribs with custom models<a class="headerlink" href="#unit-testing-contribs-with-custom-models" title="Permalink to this headline"></a></h2>
<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 std std-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
@ -243,15 +320,8 @@ testing#503435) is currently untested! Please report your findings.</p>
</pre></div>
</div>
</section>
<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, its 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>
</section>
<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>
<h2>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></h2>
<p>If you have custom models with a large number of migrations, creating the test database can take a
very long time. If you dont require migrations to run for your tests, you can disable them with the
django-test-without-migrations package. To install it, simply:</p>
@ -270,152 +340,6 @@ django-test-without-migrations package. To install it, simply:</p>
</pre></div>
</div>
</section>
</section>
<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 arent 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>
<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>Evennias test runner can be used to launch tests in your game directory (lets call it mygame).
Evennias 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-none notranslate"><div class="highlight"><pre><span></span>evennia --settings settings.py test commands
</pre></div>
</div>
<p>This command will execute Evennias 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 modules 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>
</section>
<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"><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">&quot;&quot;&quot;Unittest for strings (just a basic example).&quot;&quot;&quot;</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">&quot;&quot;&quot;Test the upper() str method.&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="s1">&#39;foo&#39;</span><span class="o">.</span><span class="n">upper</span><span class="p">(),</span> <span class="s1">&#39;FOO&#39;</span><span class="p">)</span>
</pre></div>
</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>Lets execute that test to see if it works.</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; evennia --settings settings.py test commands
TESTING: Using specified settings file &#39;server.conf.settings&#39;.
(Obs: Evennia&#39;s full test suite may not pass if the settings are very
different from the default. Use &#39;test .&#39; as arguments to run only tests
on the game dir.)
Creating test database for alias &#39;default&#39;...
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Destroying test database for alias &#39;default&#39;...
</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 thats 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 Evennias 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>
</section>
<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"><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">&quot;&quot;</span><span class="p">,</span> <span class="s2">&quot;STR: 5, AGI: 4, MAG: 2&quot;</span><span class="p">)</span>
</pre></div>
</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 doesnt use Character, but
DefaultCharacter, which means the character calling the command wont have the abilities we have
written in the Character typeclass.</p></li>
<li><p>Line 6-8: thats 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 didnt 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: its 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.
Its 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 dont 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>Lets run our new test:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; evennia --settings settings.py test commands
[...]
Creating test database for alias &#39;default&#39;...
..
----------------------------------------------------------------------
Ran 2 tests in 0.156s
OK
Destroying test database for alias &#39;default&#39;...
</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>
</section>
</section>
</section>
@ -442,23 +366,17 @@ will get much more information to help you fix the bug.</p>
<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="#running-tests-for-your-game-dir">Running tests for your game dir</a></li>
<li><a class="reference internal" href="#writing-new-tests">Writing new tests</a></li>
<li><a class="reference internal" href="#using-the-evennia-testing-classes">Using the Evennia testing classes</a><ul>
<li><a class="reference internal" href="#classes-for-testing-your-game-dir">Classes for testing your game dir</a></li>
<li><a class="reference internal" href="#classes-for-testing-evennia-core">Classes for testing Evennia core</a></li>
</ul>
</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">