doc/book/en/devrepo/testing.rst
branchstable
changeset 8168 707466abe9cc
parent 7856 51a3fb272bf3
child 8483 4ba11607d84a
equal deleted inserted replaced
8167:41ec579e27c4 8168:707466abe9cc
   458 
   458 
   459 CubicWebTC API
   459 CubicWebTC API
   460 ``````````````
   460 ``````````````
   461 .. autoclass:: cubicweb.devtools.testlib.CubicWebTC
   461 .. autoclass:: cubicweb.devtools.testlib.CubicWebTC
   462    :members:
   462    :members:
       
   463 
       
   464 
       
   465 What you need to know about request and session
       
   466 -----------------------------------------------
       
   467 
       
   468 
       
   469 .. image:: ../images/request_session.png
       
   470 
       
   471 First, remember to think that some code run on a client side, some
       
   472 other on the repository side. More precisely:
       
   473 
       
   474 * client side: web interface, raw db-api connection (cubicweb-ctl shell for
       
   475   instance);
       
   476 
       
   477 * repository side: RQL query execution, that may trigger hooks and operation.
       
   478 
       
   479 The client interact with the repository through a db-api connection.
       
   480 
       
   481 
       
   482 A db-api connection is tied to a session in the repository. The connection and
       
   483 request objects are unaccessible from repository code / the session object is
       
   484 unaccessible from client code (theorically at least).
       
   485 
       
   486 The :mod:`cubicweb.dbapi` module provides a base request class. The web interface
       
   487 provides an extended request class.
       
   488 
       
   489 
       
   490 The `request` object provides access to all cubicweb resources, eg:
       
   491 
       
   492 * the registry (which itself provides access to the schema and the
       
   493   configuration);
       
   494 
       
   495 * an underlying db-api connection (when using req.execute, you actually call the
       
   496   db-api);
       
   497 
       
   498 * other specific resources depending on the client type (url generation according
       
   499   to base url, form parameters, etc.).
       
   500 
       
   501 
       
   502 A `session` provides an api similar to a request regarding RQL execution and
       
   503 access to global resources (registry and all), but also have the following
       
   504 responsibilities:
       
   505 
       
   506 * handle transaction data, that will live during the time of a single
       
   507   transaction. This includes the database connections that will be used to
       
   508   execute RQL queries.
       
   509 
       
   510 * handle persistent data that may be used across different (web) requests
       
   511 
       
   512 * security and hooks control (not possible through a request)
       
   513 
       
   514 
       
   515 The `_cw` attribute
       
   516 ```````````````````
       
   517 The `_cw` attribute available on every application object provides access to all
       
   518 cubicweb resources, eg:
       
   519 
       
   520 For code running on the client side (eg web interface view), `_cw` is a request
       
   521 instance.
       
   522 
       
   523 For code running on the repository side (hooks and operation), `_cw` is a session
       
   524 instance.
       
   525 
       
   526 
       
   527 Beware some views may be called with a session (eg notifications) or with a
       
   528 DB-API request. In the later case, see :meth:`use_web_compatible_requests` on
       
   529 :class:`Connection` instances.
       
   530 
       
   531 
       
   532 Request, session and transaction
       
   533 ````````````````````````````````
       
   534 
       
   535 In the web interface, an HTTP request is handle by a single request, which will
       
   536 be thrown way once the response send.
       
   537 
       
   538 The web publisher handle the transaction:
       
   539 
       
   540 * commit / rollback is done automatically
       
   541 * you should not commit / rollback explicitly
       
   542 
       
   543 When using a raw db-api, you're on your own regarding transaction.
       
   544 
       
   545 On the other hand, db-api connection and session live from a user login to its logout.
       
   546 
       
   547 Because session lives for a long time, and database connections is a limited
       
   548 resource, we can't bound a session to its own database connection for all its
       
   549 lifetime. The repository handles a pool of connections (4 by default), and it's
       
   550 responsible to attribute them as needed.
       
   551 
       
   552 Let's detail the process:
       
   553 
       
   554 1. an incoming RQL query comes from a client to the repository
       
   555 
       
   556 2. the repository attributes a database connection to the session
       
   557 
       
   558 3. the repository's querier execute the query
       
   559 
       
   560 4. this query may trigger hooks. Hooks and operation may execute some rql queries
       
   561    through `_cw.execute`. Those queries go directly to the querier, hence don't
       
   562    touch the database connection, they use the one attributed in 2.
       
   563 
       
   564 5. the repository's get the result of the query in 1. If it was a RQL read query,
       
   565    the database connection is released. If it was a write query, the connection
       
   566    is then tied to the session until the transaction is commited or rollbacked.
       
   567 
       
   568 6. results are sent back to the client
       
   569 
       
   570 This implies several things:
       
   571 
       
   572 * when using a request, or code executed in hooks, this database connection
       
   573   handling is totally transparent
       
   574 
       
   575 * however, take care when writing test: you are usually faking / testing both the
       
   576   server and the client side, so you have to decide when to use self.request() /
       
   577   self.session. Ask yourself "where the code I want to test will be running,
       
   578   client or repository side ?". The response is usually : use a request :)
       
   579   However, if you really need using a session:
       
   580 
       
   581   - commit / rollback will free the database connection (unless explicitly told
       
   582     not to do so).
       
   583 
       
   584   - if you issue a query after that without asking for a database connection
       
   585     (`session.get_cnxset()`), you will end up with a 'None type has no attribute
       
   586     source()' error