diff --git a/doc/extensions/index.rst b/doc/extensions/index.rst
index f5413f46c46..0522cf24e16 100644
--- a/doc/extensions/index.rst
+++ b/doc/extensions/index.rst
@@ -26,6 +26,8 @@ extensions.
:maxdepth: 2
tutorial
+ testing-extensions
+ publishing-extensions
best-practices
plugin-interfaces
plugins-toolkit
diff --git a/doc/extensions/publishing-extensions.rst b/doc/extensions/publishing-extensions.rst
new file mode 100644
index 00000000000..97d114ca96d
--- /dev/null
+++ b/doc/extensions/publishing-extensions.rst
@@ -0,0 +1,35 @@
+Publishing extensions
+=====================
+
+CKAN extensions are just Python packages like any other Python package,
+they can be published, downloaded and installed using the usual methods.
+For example, why not get a free `github.com `_ account,
+create a new git repo and push your extension code to it?
+See `help.github.com `_ for documentation on using
+git and GitHub.
+
+.. note::
+
+ There are a few files in the ``ckanext-iauthfunctions`` directory that you
+ shouldn't publish or commit to your git repository or other version control
+ repo. Don't commit:
+
+ * the ``ckanext_iauthfunctions.egg-info`` directory, or
+ * any of the ``*.pyc`` files.
+
+ You should create a `.gitignore file
+ `_ to tell git to ignore
+ these files, and commit the ``.gitignore`` file to your git repo.
+
+ *Do* commit the ``setup.py`` file, the ``test.ini`` file and all the
+ ``__init__.py`` files.
+
+Once it's been published to GitHub, users can then install your extension by
+activating their CKAN virtual environment then running a command like this::
+
+ pip install -e git+https://github.com/{USER}/ckanext-iauthfunctions.git#egg=ckanext-iauthfunctions
+
+(replacing ``{USER}`` with the name of the GitHub account that the extension
+was published to). They can then simply add your plugin to the ``ckan.plugins``
+setting in the config file, restart CKAN, and your plugin should be running.
+
diff --git a/doc/extensions/testing-extensions.rst b/doc/extensions/testing-extensions.rst
new file mode 100644
index 00000000000..0b05e891f91
--- /dev/null
+++ b/doc/extensions/testing-extensions.rst
@@ -0,0 +1,68 @@
+Testing extensions
+==================
+
+CKAN extensions can have their own tests that are run using ``nosetests``
+in much the same way as running CKAN's own tests (see :doc:`/test`).
+
+Continuing with our :doc:`example_iauthfunctions example extension `,
+first we need a CKAN config file to be used when running our tests.
+Create the file ``ckanext-iauthfunctions/test.ini`` with the following
+contents::
+
+ [app:main]
+ use = config:../ckan/test-core.ini
+
+The ``use`` line declares that this config file inherits the settings from the
+config file used to run CKAN's own tests (``../ckan`` should be the path to
+your CKAN source directory, relative to your ``test.ini`` file).
+
+The ``test.ini`` file is a CKAN config file just like your |development.ini|
+and |production.ini| files, and it can contain any
+:doc:`CKAN config file settings ` that you want CKAN to use
+when running your tests, for example::
+
+ [app:main]
+ use = config:../ckan/test-core.ini
+ ckan.site_title = My Test CKAN Site
+ ckan.site_description = A test site for testing my CKAN extension
+
+Next, make the directory that will contain our test modules::
+
+ mkdir ckanext-iauthfunctions/ckanext/iauthfunctions/tests/
+
+Finally, create the file
+``ckanext-iauthfunctions/ckanext/iauthfunctions/tests/test_iauthfunctions.py``
+with the following contents:
+
+.. literalinclude:: ../../ckanext/example_iauthfunctions/tests/test_example_iauthfunctions.py
+ :end-before: class TestExampleIAuthFunctionsPluginV3
+
+To run these extension tests, ``cd`` into the ``ckanext-iauthfunctions``
+directory and run this command::
+
+ nosetests --ckan --with-pylons=test.ini ckanext/iauthfunctions/tests
+
+Some notes on how these tests work:
+
+* Nose has lots of useful functions for testing, see the
+ `nose documentation `_.
+
+* We're using a :class:`paste.fixture.TestApp` object to simulate sending HTTP
+ requests to the CKAN API or frontend.
+ See `Testing Applications with Paste `_
+ for some documentation of this.
+
+* We're calling :func:`ckan.tests.call_action_api` to post (simulated) HTTP
+ requests to the CKAN API. This is a convenience function that CKAN provides
+ for its own tests.
+
+* You might also find it useful to read the
+ `Pylons testing documentation `_.
+
+* The Pylons book also has a `chapter on testing `_.
+
+.. todo::
+
+ Link to CKAN guidelines for *how* to write tests, once those guidelines have
+ been written. Also add any more extension-specific testing details here.
+
diff --git a/doc/extensions/tutorial.rst b/doc/extensions/tutorial.rst
index 2f8959bf3cc..8330002be7f 100644
--- a/doc/extensions/tutorial.rst
+++ b/doc/extensions/tutorial.rst
@@ -537,124 +537,3 @@ If you get a ``TypeError`` like this one::
it means that one of your plugin methods has the wrong number of parameters.
A plugin has to implement each method in a plugin interface with the same
parameters as in the interface.
-
-
-.. _publishing extensions:
-
-Publishing extensions
-=====================
-
-CKAN extensions are just Python packages like any other Python package,
-they can be published, downloaded and installed using the usual methods.
-For example, why not get a free `github.com `_ account,
-create a new git repo and push your extension code to it?
-See `help.github.com `_ for documentation on using
-git and GitHub.
-
-.. note::
-
- There are a few files in the ``ckanext-iauthfunctions`` directory that you
- shouldn't publish or commit to your git repository or other version control
- repo. Don't commit:
-
- * the ``ckanext_iauthfunctions.egg-info`` directory, or
- * any of the ``*.pyc`` files.
-
- You should create a `.gitignore file
- `_ to tell git to ignore
- these files, and commit the ``.gitignore`` file to your git repo.
-
- *Do* commit the ``setup.py`` file, the ``test.ini`` file and all the
- ``__init__.py`` files.
-
-Once it's been published to GitHub, users can then install your extension by
-activating their CKAN virtual environment then running a command like this::
-
- pip install -e git+https://github.com/{USER}/ckanext-iauthfunctions.git#egg=ckanext-iauthfunctions
-
-(replacing ``{USER}`` with the name of the GitHub account that the extension
-was published to). They can then simply add your plugin to the ``ckan.plugins``
-setting in the config file, restart CKAN, and your plugin should be running.
-
-
-.. _testing extensions:
-
-Testing extensions
-==================
-
-CKAN extensions can have their own tests that are run using ``nosetests``
-in much the same way as running CKAN's own tests (see :doc:`/test`).
-
-First, we need a CKAN config file to be used when running our tests.
-Create the file ``ckanext-iauthfunctions/test.ini`` with the following
-contents::
-
- [app:main]
- use = config:../ckan/test-core.ini
-
-The ``use`` line declares that this config file inherits the settings from the
-config file used to run CKAN's own tests (``../ckan`` should be the path to
-your CKAN source directory, relative to your ``test.ini`` file).
-
-The ``test.ini`` file is a CKAN config file just like your |development.ini|
-and |production.ini| files, and it can contain any
-:doc:`CKAN config file settings ` that you want CKAN to use
-when running your tests, for example::
-
- [app:main]
- use = config:../ckan/test-core.ini
- ckan.site_title = My Test CKAN Site
- ckan.site_description = A test site for testing my CKAN extension
-
-Next, make the directory that will contain our test modules::
-
- mkdir ckanext-iauthfunctions/ckanext/iauthfunctions/tests/
-
-Finally, create the file
-``ckanext-iauthfunctions/ckanext/iauthfunctions/tests/test_iauthfunctions.py``
-with the following contents:
-
-.. literalinclude:: ../../ckanext/example_iauthfunctions/tests/test_example_iauthfunctions.py
- :end-before: class TestExampleIAuthFunctionsPluginV3
-
-To run these extension tests, ``cd`` into the ``ckanext-iauthfunctions``
-directory and run this command::
-
- nosetests --ckan --with-pylons=test.ini ckanext/iauthfunctions/tests
-
-Some notes on how these tests work:
-
-* Nose has lots of useful functions for testing, see the
- `nose documentation `_.
-
-* We're using a :class:`paste.fixture.TestApp` object to simulate sending HTTP
- requests to the CKAN API or frontend.
- See `Testing Applications with Paste `_
- for some documentation of this.
-
-* We're calling :func:`ckan.tests.call_action_api` to post (simulated) HTTP
- requests to the CKAN API. This is a convenience function that CKAN provides
- for its own tests.
-
-* You might also find it useful to read the
- `Pylons testing documentation `_.
-
-* The Pylons book also has a `chapter on testing `_.
-
-.. todo::
-
- Link to CKAN guidelines for *how* to write tests, once those guidelines have
- been written. Also add any more extension-specific testing details here.
-
-
-.. _localizing extensions:
-
-Internationalizing and localizing extensions
-============================================
-
-.. todo::
-
- Show how to internationalize and localize the example extension.
-
-
-