--- a/devtools/testlib.py Tue Apr 13 19:18:06 2010 +0200
+++ b/devtools/testlib.py Tue Apr 13 19:19:37 2010 +0200
@@ -130,18 +130,17 @@
class CubicWebTC(TestCase):
"""abstract class for test using an apptest environment
+ attributes:
- attributes:
- `vreg`, the vregistry
- `schema`, self.vreg.schema
- `config`, cubicweb configuration
- `cnx`, dbapi connection to the repository using an admin user
- `session`, server side session associated to `cnx`
- `app`, the cubicweb publisher (for web testing)
- `repo`, the repository object
-
- `admlogin`, login of the admin user
- `admpassword`, password of the admin user
+ * `vreg`, the vregistry
+ * `schema`, self.vreg.schema
+ * `config`, cubicweb configuration
+ * `cnx`, dbapi connection to the repository using an admin user
+ * `session`, server side session associated to `cnx`
+ * `app`, the cubicweb publisher (for web testing)
+ * `repo`, the repository object
+ * `admlogin`, login of the admin user
+ * `admpassword`, password of the admin user
"""
appid = 'data'
--- a/doc/book/en/development/testing.rst Tue Apr 13 19:18:06 2010 +0200
+++ b/doc/book/en/development/testing.rst Tue Apr 13 19:19:37 2010 +0200
@@ -6,27 +6,90 @@
.. toctree::
:maxdepth: 1
-
Unit tests
----------
-*CubicWeb* framework provides essentially two Python test classes in the
-module `cubicweb.devtools.apptest`:
+The *CubicWeb* framework provides the `CubicWebTC` test base class in
+the module `cubicweb.devtools.testlib`.
+
+Tests shall be put into the mycube/test directory. Additional test
+data shall go into mycube/test/data.
+
+It is much advised to write tests concerning entities methods, hooks
+and operations, security. The CubicWebTC base class has convenience
+methods to help test all of this.
+
+.. note::
+
+ In the realm of views, there is not much to do but check that the
+ views are valid XHTML. See :ref:`automatic_views_tests` for
+ details. Integration of CubicWeb tests with UI testing tools such as
+ `selenium`_ are currently under invesitgation.
+
+.. _selenium: http://seleniumhq.org/projects/ide/
-* `EnvBasedTC`, to simulate a complete environment (web + repository)
-* `RepositoryBasedTC`, to simulate a repository environment only
+Most unit tests need a live database to work against. This is achieved
+by CubicWeb using automatically sqlite (bundled with Python, see
+http://docs.python.org/library/sqlite3.html) as a backend.
+
+The database is stored in the mycube/test/tmpdb,
+mycube/test/tmpdb-template files. If it does not (yet) exists, it will
+be built automatically when the test suit starts.
+
+.. warning::
-Those two classes almost have the same interface and offer numerous
-methods to write tests rapidly and efficiently.
+ Whenever the schema changes (new entities, attributes, relations)
+ one must delete these two files. Changes concerned only with entity
+ or relation type properties (constraints, cardinalities,
+ permissions) and generally dealt with using the
+ `sync_schema_props_perms()` fonction of the migration environment
+ need not a database regeneration step.
+
+Unit test by example
+````````````````````
-XXX FILLME describe API
+We start with an example extracted from the keyword cube (available
+from http://www.cubicweb.org/project/cubicweb-keyword).
+
+.. sourcecode:: python
+
+ from cubicweb.devtools.testlib import CubicWebTC
+ from cubicweb import ValidationError
+
+ class ClassificationHooksTC(CubicWebTC):
+
+ def setup_database(self):
+ req = self.request()
+ group_etype = req.execute('Any X WHERE X name "CWGroup"').get_entity(0,0)
+ c1 = req.create_entity('Classification', name=u'classif1',
+ classifies=group_etype)
+ user_etype = req.execute('Any X WHERE X name "CWUser"').get_entity(0,0)
+ c2 = req.create_entity('Classification', name=u'classif2',
+ classifies=user_etype)
+ self.kw1 = req.create_entity('Keyword', name=u'kwgroup', included_in=c1)
+ self.kw2 = req.create_entity('Keyword', name=u'kwuser', included_in=c2)
-In most of the cases, you will inherit `EnvBasedTC` to write Unittest or
-functional tests for your entities, views, hooks, etc...
+ def test_cannot_create_cycles(self):
+ # direct obvious cycle
+ self.assertRaises(ValidationError, self.kw1.set_relations,
+ subkeyword_of=self.kw1)
+ # testing indirect cycles
+ kw3 = self.execute('INSERT Keyword SK: SK name "kwgroup2", SK included_in C, '
+ 'SK subkeyword_of K WHERE C name "classif1", K eid %s'
+ % self.kw1.eid).get_entity(0,0)
+ self.kw1.set_relations(subkeyword_of=kw3)
+ self.assertRaises(ValidationError, self.commit)
-XXX pytestconf.py & options (e.g --source to use a different db
-backend than sqlite)
+The test class defines a `setup_database` method which populates the
+database with initial data. Each test of the class runs with this
+pre-populated database.
+The test case itself checks that an Operation does it job of
+preventing cycles amongst Keyword entities.
+
+
+XXX ref to advanced use case
+XXX apycot plug
Managing connections or users
+++++++++++++++++++++++++++++
@@ -62,23 +125,78 @@
# the default admin connection and one may be tempted to close it
self.restore_connection()
-Do not use the references kept to the entities created with a connection from another.
+.. warning::
+ Do not use the references kept to the entities created with a
+ connection from another !
+
+XXX the new context manager ?
Email notifications tests
-------------------------
-When running tests potentially generated e-mails are not really
-sent but is found in the list `MAILBOX` of module `cubicweb.devtools.apptest`.
-This list is reset at each test *setUp* (by the setUp of classes `EnvBasedTC`
-and `RepositoryBasedTC`).
+When running tests potentially generated e-mails are not really sent
+but is found in the list `MAILBOX` of module
+`cubicweb.devtools.testlib`.
You can test your notifications by analyzing the contents of this list, which
contains objects with two attributes:
+
* `recipients`, the list of recipients
* `msg`, object email.Message
+Let us look at simple example from the ``blog`` cube.
-Automatic testing
------------------
-XXXFILLME
+.. sourcecode:: python
+
+ from cubicweb.devtools.testlib import CubicWebTC, MAILBOX
+
+ class BlogTestsCubicWebTC(CubicWebTC):
+ """test blog specific behaviours"""
+
+ def test_notifications(self):
+ req = self.request()
+ cubicweb_blog = req.create_entity('Blog', title=u'cubicweb',
+ description=u'cubicweb is beautiful')
+ blog_entry_1 = req.create_entity('BlogEntry', title=u'hop',
+ content=u'cubicweb hop')
+ blog_entry_1.set_relations(entry_of=cubicweb_blog)
+ blog_entry_2 = req.create_entity('BlogEntry', title=u'yes',
+ content=u'cubicweb yes')
+ blog_entry_2.set_relations(entry_of=cubicweb_blog)
+ self.assertEquals(len(MAILBOX), 0)
+ self.commit()
+ self.assertEquals(len(MAILBOX), 2)
+ mail = MAILBOX[0]
+ self.assertEquals(mail.subject, '[data] hop')
+ mail = MAILBOX[1]
+ self.assertEquals(mail.subject, '[data] yes')
+
+.. _automatic_views_tests:
+
+Automatic views testing
+-----------------------
+
+This is done automatically with the AutomaticWebTest class. At cube
+creation time, the mycube/test/test_mycube.py file contains such a
+test. The code here has to be uncommented to be usable, without
+further modification.
+
+XXX more to come
+
+
+Using Pytest
+````````````
+
+.. automodule:: logilab.common.testlib
+.. autoclass:: logilab.common.testlib.TestCase
+ :members:
+
+XXX pytestconf.py & options (e.g --source to use a different db
+backend than sqlite)
+
+CubicWebTC API
+``````````````
+.. autoclass:: cubicweb.devtools.testlib.CubicWebTC
+ :members:
+