~openerp-community/openobject-doc/6.1

« back to all changes in this revision

Viewing changes to i18n/vi/source/developer/5_20_unit_testing/index.rst

  • Committer: TruongSinh Tran
  • Date: 2009-07-19 19:02:35 UTC
  • Revision ID: truongsinh@vipescoserver-20090719190235-fu3bxcrbpvwuk5h7
[FIX] build_i18n.py .. raw:: html

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
 
12
12
Since version 4.2 of OpenERP, the XML api provides several features to test your modules. They allow to
13
13
 
14
 
    .. i18n: * test the properties of your records, your class invariants etc.
15
 
    .. i18n: * test your methods
16
 
    .. i18n: * manipulate your objects to check your workflows and specific methods 
 
14
.. i18n:     * test the properties of your records, your class invariants etc.
 
15
.. i18n:     * test your methods
 
16
.. i18n:     * manipulate your objects to check your workflows and specific methods 
17
17
 
18
18
    * test the properties of your records, your class invariants etc.
19
19
    * test your methods
20
 
    * manipulate your objects to check your workflows and specific methods.. i18n:  
 
20
    * manipulate your objects to check your workflows and specific methods 
21
21
 
22
22
.. i18n: This thus allows you to simulate user interaction and automatically test your modules.
23
23
 
25
25
 
26
26
.. i18n: Generalities
27
27
.. i18n: ============
28
 
 .. i18n: 
 
28
.. i18n:  
29
29
.. i18n: As you will see in the next pages, unit testing through Open ERP's XML can be done using three main tags: <assert>, <workflow> and <function>. All these tags share some common optional attributes:
30
30
 
31
31
Generalities
37
37
 
38
38
:uid:
39
39
 
40
 
        .. i18n: allows to do the tag interpretation through a specific User ID (you must specify the XML id of that user, for example "base.user_demo") 
 
40
.. i18n:        allows to do the tag interpretation through a specific User ID (you must specify the XML id of that user, for example "base.user_demo") 
41
41
 
42
 
        allows to do the tag interpretation through a specific User ID (you must specify the XML id of that user, for example "base.user_demo").. i18n:  
 
42
        allows to do the tag interpretation through a specific User ID (you must specify the XML id of that user, for example "base.user_demo") 
43
43
 
44
44
.. i18n: :context:
45
45
 
46
46
:context:
47
47
 
48
 
        .. i18n: allows to specify a context dictionary (given as a Python expression) to use when applicable (for <function> notice that not all objects methods take a context attribute so it wont be automatically transmitted to them, however it applies on <value>) 
 
48
.. i18n:        allows to specify a context dictionary (given as a Python expression) to use when applicable (for <function> notice that not all objects methods take a context attribute so it wont be automatically transmitted to them, however it applies on <value>) 
49
49
 
50
 
        allows to specify a context dictionary (given as a Python expression) to use when applicable (for <function> notice that not all objects methods take a context attribute so it wont be automatically transmitted to them, however it applies on <value>).. i18n:  
 
50
        allows to specify a context dictionary (given as a Python expression) to use when applicable (for <function> notice that not all objects methods take a context attribute so it wont be automatically transmitted to them, however it applies on <value>) 
51
51
 
52
52
.. i18n: These two attributes might be set on any of those tags (for <functions>, only the root <function> tag may accept it) or on the <data> tag itself. If you set a context attribute on both, they will be merged automatically.
53
53
 
67
67
 
68
68
You can declare unit tests in all your .XML files. We suggest you to name the files like this:
69
69
 
70
 
    .. i18n: * module_name_test.xml 
 
70
.. i18n:     * module_name_test.xml 
71
71
 
72
 
    * module_name_test.xml.. i18n:  
 
72
    * module_name_test.xml 
73
73
 
74
74
.. i18n: If your tests are declared at demo data in the __terp__.py, they will be checked at the installation of the system with demo data. Example of usage, testing the the demo sale order produce a correct amount in the generated invoice.
75
75
 
81
81
 
82
82
.. i18n: If your tests are declared in update sections, the tests are checked at the installation and also at all updates. Use it to tests consistencies, invariants of the module. Example: The sum of the credit must be equals to the sum of the debit for all non draft entries in the accounting module. Putting tests in update sections is very usefull to check consistencies of migrations or new version upgrades. 
83
83
 
84
 
If your tests are declared in update sections, the tests are checked at the installation and also at all updates. Use it to tests consistencies, invariants of the module. Example: The sum of the credit must be equals to the sum of the debit for all non draft entries in the accounting module. Putting tests in update sections is very usefull to check consistencies of migrations or new version upgrades... i18n:  
 
84
If your tests are declared in update sections, the tests are checked at the installation and also at all updates. Use it to tests consistencies, invariants of the module. Example: The sum of the credit must be equals to the sum of the debit for all non draft entries in the accounting module. Putting tests in update sections is very usefull to check consistencies of migrations or new version upgrades. 
85
85
 
86
86
.. i18n: Assert Tag
87
87
.. i18n: ==========
94
94
The assert tag allows to define some assertion that have to be checked at boot time. Example :
95
95
 
96
96
.. i18n: .. code-block:: xml
97
 
        .. i18n: 
98
 
        .. i18n: <assert model="res.company" id="main_company" string="The main company name is Tiny SPRL">
99
 
            .. i18n: <test expr="name">Tiny sprl</test>
100
 
        .. i18n: </assert>
 
97
.. i18n:        
 
98
.. i18n:        <assert model="res.company" id="main_company" string="The main company name is Tiny SPRL">
 
99
.. i18n:            <test expr="name">Tiny sprl</test>
 
100
.. i18n:        </assert>
101
101
 
102
102
.. code-block:: xml
103
103
        
114
114
For more complex tests it is not always sufficient to compare a result to a string. To do that you may instead omit the tag's content and just put an expression that must evaluate to True:
115
115
 
116
116
.. i18n: .. code-block:: xml
117
 
        .. i18n: 
118
 
        .. i18n: <assert model="res.company" 
119
 
                .. i18n: id="main_company" 
120
 
                .. i18n: string="The main company's currency is €" : severity="warning">
121
 
            .. i18n: <test expr="currency_id.code == 'eur'.upper()"/>
122
 
        .. i18n: </assert>
 
117
.. i18n:        
 
118
.. i18n:        <assert model="res.company" 
 
119
.. i18n:                 id="main_company" 
 
120
.. i18n:                 string="The main company's currency is €" : severity="warning">
 
121
.. i18n:            <test expr="currency_id.code == 'eur'.upper()"/>
 
122
.. i18n:        </assert>
123
123
 
124
124
.. code-block:: xml
125
125
        
138
138
As sometimes you do not know the id a priori, that attribute can be substituted by a search. So we can define another example, which will be always true :
139
139
 
140
140
.. i18n: .. code-block:: xml
141
 
        .. i18n: 
142
 
        .. i18n: <assert model="res.partner" 
143
 
                .. i18n: search="[('name','=','Agrolait')]" 
144
 
                .. i18n: string="The name of Agrolait is :Agrolait">
145
 
            .. i18n: <test expr="name">Agrolait</test>
146
 
        .. i18n: </assert>
 
141
.. i18n:        
 
142
.. i18n:        <assert model="res.partner" 
 
143
.. i18n:                 search="[('name','=','Agrolait')]" 
 
144
.. i18n:                 string="The name of Agrolait is :Agrolait">
 
145
.. i18n:            <test expr="name">Agrolait</test>
 
146
.. i18n:        </assert>
147
147
 
148
148
.. code-block:: xml
149
149
        
158
158
When you use the search, each resulting record is tested but the assertion is counted only once. Thus if an assertion fails, the remaining records wont be tested. In addition, if the search finds no record, nothing will be tested so the assertion will be considered successful. If you want to make sure that there are a certain number of results, you might use the count parameter:
159
159
 
160
160
.. i18n: .. code-block:: xml
161
 
        .. i18n: 
162
 
        .. i18n: <assert model="res.partner" 
163
 
                .. i18n: search="[('name','=','Agrolait')]" 
164
 
                .. i18n: string="The name of Agrolait is :Agrolait" count="1">
165
 
            .. i18n: <test expr="name">Agrolait</test>
166
 
        .. i18n: </assert>
 
161
.. i18n:        
 
162
.. i18n:        <assert model="res.partner" 
 
163
.. i18n:                 search="[('name','=','Agrolait')]" 
 
164
.. i18n:                 string="The name of Agrolait is :Agrolait" count="1">
 
165
.. i18n:            <test expr="name">Agrolait</test>
 
166
.. i18n:        </assert>
167
167
 
168
168
.. code-block:: xml
169
169
        
182
182
Require the version of a module.
183
183
 
184
184
.. i18n: .. code-block:: xml
185
 
        .. i18n: 
186
 
        .. i18n: <!-- modules requirement -->
187
 
        .. i18n: <assert model="ir.module.module" 
188
 
                .. i18n: search="[('name','=','common')]" 
189
 
                .. i18n: severity="critical" count="1">
190
 
            .. i18n: <test expr="state == 'installed'" />
191
 
            .. i18n: <!-- only check module version -->
192
 
            .. i18n: <test expr="'.'.join(installed_version.split('.')[3:]) >= '2.4'" />
193
 
        .. i18n: </assert>
194
 
        .. i18n: 
195
 
        .. i18n: 
 
185
.. i18n:        
 
186
.. i18n:        <!-- modules requirement -->
 
187
.. i18n:        <assert model="ir.module.module" 
 
188
.. i18n:                 search="[('name','=','common')]" 
 
189
.. i18n:                 severity="critical" count="1">
 
190
.. i18n:            <test expr="state == 'installed'" />
 
191
.. i18n:            <!-- only check module version -->
 
192
.. i18n:            <test expr="'.'.join(installed_version.split('.')[3:]) >= '2.4'" />
 
193
.. i18n:        </assert>
 
194
.. i18n:        
 
195
.. i18n:        
196
196
.. i18n: Workflow Tag
197
197
.. i18n: =============
198
198
 
216
216
The workflow tag allows you to call for a transition in a workflow by sending a signal to it. It is generally used to simulate an interaction with a user (clicking on a button…) for test purposes:
217
217
 
218
218
.. i18n: .. code-block:: xml
219
 
        .. i18n: 
220
 
        .. i18n: <workflow model="sale.order" ref="test_order_1" action="order_confirm" />
 
219
.. i18n:        
 
220
.. i18n:        <workflow model="sale.order" ref="test_order_1" action="order_confirm" />
221
221
 
222
222
.. code-block:: xml
223
223
        
232
232
Notice that workflow tags (as all other tags) are interpreted as root which might be a problem if the signals handling needs to use some particular property of the user (typically the user's company, while root does not belong to one). In that case you might specify a user to switch to before handling the signal, through the uid property:
233
233
 
234
234
.. i18n: .. code-block:: xml
235
 
        .. i18n: 
236
 
        .. i18n: <workflow model="sale.order" ref="test_order_1" action="manual_invoice" uid="base.user_admin" />
 
235
.. i18n:        
 
236
.. i18n:        <workflow model="sale.order" ref="test_order_1" action="manual_invoice" uid="base.user_admin" />
237
237
 
238
238
.. code-block:: xml
239
239
        
248
248
In some particular cases, you do not know a priori the id of the object to manipulate through the workflow. It is thus allowed to replace the ref attribute by a value child tag:
249
249
 
250
250
.. i18n: .. code-block:: xml
251
 
        .. i18n: 
252
 
        .. i18n: <workflow model="account.invoice" action="invoice_open">
253
 
            .. i18n: <value model="sale.order" eval="obj(ref('test_order_1')).invoice_ids[0].id" />
254
 
        .. i18n: </workflow>
 
251
.. i18n:        
 
252
.. i18n:        <workflow model="account.invoice" action="invoice_open">
 
253
.. i18n:            <value model="sale.order" eval="obj(ref('test_order_1')).invoice_ids[0].id" />
 
254
.. i18n:        </workflow>
255
255
 
256
256
.. code-block:: xml
257
257
        
261
261
 
262
262
.. i18n: (notice that it the eval part must evaluate to a valid database id) 
263
263
 
264
 
(notice that it the eval part must evaluate to a valid database id).. i18n:  
 
264
(notice that it the eval part must evaluate to a valid database id) 
265
265
 
266
266
.. i18n: Function Tag
267
267
.. i18n: ============
281
281
 
282
282
Where
283
283
 
284
 
    .. i18n: * cr is the database cursor
285
 
    .. i18n: * uid is the user id 
 
284
.. i18n:     * cr is the database cursor
 
285
.. i18n:     * uid is the user id 
286
286
 
287
287
    * cr is the database cursor
288
 
    * uid is the user id.. i18n:  
 
288
    * uid is the user id 
289
289
 
290
290
.. i18n: Most of the methods defined in Tiny respect that signature as cr and uid are required for a lot of operations, including database access.
291
291
 
296
296
The function tag can then be used to call that method:
297
297
 
298
298
.. i18n: .. code-block:: xml
299
 
        .. i18n: 
300
 
        .. i18n: <function model="mypackage.myclass" name="mymethod" />
 
299
.. i18n:        
 
300
.. i18n:        <function model="mypackage.myclass" name="mymethod" />
301
301
 
302
302
.. code-block:: xml
303
303
        
315
315
 
316
316
There are two ways to call that method:
317
317
 
318
 
    .. i18n: * either by using the eval attribute, which must be a python expression evaluating to the list of additional arguments: 
 
318
.. i18n:     * either by using the eval attribute, which must be a python expression evaluating to the list of additional arguments: 
319
319
 
320
 
    * either by using the eval attribute, which must be a python expression evaluating to the list of additional arguments:.. i18n:  
 
320
    * either by using the eval attribute, which must be a python expression evaluating to the list of additional arguments: 
321
321
 
322
322
.. i18n: .. code-block:: xml
323
 
        .. i18n: 
324
 
        .. i18n: <function model="mypackage.myclass" name="mymethod" eval="[42]" />
 
323
.. i18n:        
 
324
.. i18n:        <function model="mypackage.myclass" name="mymethod" eval="[42]" />
325
325
 
326
326
.. code-block:: xml
327
327
        
331
331
 
332
332
In that case you have access to all native python functions an to a function ref which takes as argument an XML id and returns the corresponding id.
333
333
 
334
 
    .. i18n: * or by putting a child node inside the function tag: 
 
334
.. i18n:     * or by putting a child node inside the function tag: 
335
335
 
336
 
    * or by putting a child node inside the function tag:.. i18n:  
 
336
    * or by putting a child node inside the function tag: 
337
337
 
338
338
.. i18n: .. code-block:: xml
339
 
        .. i18n: 
340
 
        .. i18n: <function model="mypackage.myclass" name="mymethod">
341
 
             .. i18n: <value eval="42" />
342
 
        .. i18n: </function>
 
339
.. i18n:        
 
340
.. i18n:        <function model="mypackage.myclass" name="mymethod">
 
341
.. i18n:             <value eval="42" />
 
342
.. i18n:        </function>
343
343
 
344
344
.. code-block:: xml
345
345
        
349
349
 
350
350
.. i18n: Only value and function tags have a meaning as function child nodes (using other tags will give unspecified results). This means that you can use the returned result of a method call as an argument of another call. You can put as many child nodes as you want, each one being an argument of the method call (keeping them in order). You can also mix child nodes and the eval attribute. In that case it will be evaluated first and child nodes will be appended to the resulting list. 
351
351
 
352
 
Only value and function tags have a meaning as function child nodes (using other tags will give unspecified results). This means that you can use the returned result of a method call as an argument of another call. You can put as many child nodes as you want, each one being an argument of the method call (keeping them in order). You can also mix child nodes and the eval attribute. In that case it will be evaluated first and child nodes will be appended to the resulting list... i18n:  
 
352
Only value and function tags have a meaning as function child nodes (using other tags will give unspecified results). This means that you can use the returned result of a method call as an argument of another call. You can put as many child nodes as you want, each one being an argument of the method call (keeping them in order). You can also mix child nodes and the eval attribute. In that case it will be evaluated first and child nodes will be appended to the resulting list. 
353
353
 
354
354
.. i18n: ==================
355
355
.. i18n: Acceptance testing
369
369
Integrity tests on migrations
370
370
=============================
371
371
 
372
 
            .. i18n: * Sum credit = Sum debit
373
 
            .. i18n: * Balanced account chart 
 
372
.. i18n:             * Sum credit = Sum debit
 
373
.. i18n:             * Balanced account chart 
374
374
 
375
375
            * Sum credit = Sum debit
376
 
            * Balanced account chart.. i18n:  
 
376
            * Balanced account chart 
377
377
 
378
378
.. i18n: ... Describe all integrity tests here
379
379