The rotest shell is an extension of an IPython environment meant to work with resources and tests.
It creates a resources client, starts a log-to-screen pipe, automatically imports resources, and provides basic functions to run tests.
Using the shell:
$ rotest shell Creating client Done! You can now lock resources and run tests, e.g. resource1 = ResourceClass.lock(skip_init=True, name='resource_name') resource2 = ResourceClass.lock(name='resource_name', config='config.json') shared_data['resource'] = resource1 run_test(ResourceBlock, parameter=5) run_test(ResourceBlock.params(parameter=6), resource=resource2) run_test(SomeTestCase, debug=True) Python 2.7.15 (default, Jun 27 2018, 13:05:28) Type "copyright", "credits" or "license" for more information. IPython 5.5.0 -- An enhanced Interactive Python. ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. help -> Python's own help system. object? -> Details about 'object', use 'object??' for extra details. Importing resources: Calculator In : calc = Calculator.lock() 06:08:34 : Requesting resources from resource manager 06:08:34 : Locked resources [Calculator(CalculatorData('calc'))] 06:08:34 : Setting up the locked resources 06:08:34 : Resource 'shell_resource' work dir was created under '~/.rotest' 06:08:34 : Connecting resource 'calc' 06:08:34 : Initializing resource 'calc' 06:08:34 : Resource 'calc' validation failed 06:08:34 : Initializing resource 'calc' 06:08:34 : Resource 'calc' was initialized In : print calc.calculate("1 + 1") 2
All BaseResources have a lock method that can be used in the shell and in scripts, which requests and initializes resources, returning a resource that’s ready for work.
You can add more startup commands to the rotest shell via the entry-point shell_startup_commands. For more information, see Configurations.
Writing a Resource-Based Test¶
In this section, we are going to add our resource to our existing test.
The first thing we need to do, is setting up our resource named
need to run the RPyC server of the calculator, using the following command:
$ rpyc_classic.py --port 1357 INFO:SLAVE/1357:server started on [0.0.0.0]:1357
This way, we have a way to communicate to our resource, which is running on our local computer (or may run on other computer, assuming you’ve set the corresponding IP address in the Django admin).
Now, let’s change the previously written module
test_math.py with the
from rotest.core import TestCase from resources_app.resources import Calculator class AddTest(TestCase): calc = Calculator.request() def test_add(self): result = self.calc.calculate("1 + 1") self.assertEqual(result, 2)
We can request resources in the test’s scope in two different ways.
As shown in the example, write a request of the format:
<request_name> = <resource_class>.request(<request_filters or service_parameters>)
request filters(in case of a resource that has data) are of the same syntax as the options passed to Django models
<Model>.objects.filter()method, and can help you make the resource request of the test more specific, e.g.
calc = Calculator.request(name='calc')
If the resource doesn’t point to
DATA_CLASS(is None) then the resource is a service, and
request_filtersbecome initialization parameters.
[Deprecated] Overriding the
resourcesfield and using
resources = [<request1>, <request2>, ...]
where each request is of the format
request(<request_name>, <resource_class>, <request_filters or service_parameters>)
where the parameters mean the same as in the previous requesting method.
Dynamic requests (during the test-run)
In the test method, you can call
self.request_resources([<request1>, <request2>, ...])
The requests are instances of
rotest.core.request, as in the previous method.
The method for declaring test resource and sub-resources has changed since version 6.0.0.
The previous method didn’t use the request classmethod, and instead used the constructor, e.g. calc = Calculator().
That form is no longer supported!
Now, let’s run the test:
$ rotest test_math.py AnonymousSuite AddTest.test_add ... OK Ran 1 test in 0.160s OK
Test event methods¶
Test result events you can use in Rotest:
self.fail(<message>), self.skip(<message>) as in
All failure events using assert<X>, as in
expect<X> methods (a new concept) - for cases where you want to fail the test but don’t want the action to break the test flow.
expectonly registers the failures (if there are any) but stays in the same scope, allowing for more testing actions in the same single test. E.g.
from rotest.core import TestCase from resources_app.resources import Calculator class AddTest(TestCase): calc = Calculator() def test_add(self): self.expectEqual(self.calc.calculate("1 + 1"), 2) self.expectEqual(self.calc.calculate("1 + 2"), 2) self.expectEqual(self.calc.calculate("1 + 3"), 2)
In the above example
AddTestwill have 2 failures to the same run (3!=2 and 4!=2).
It is recommended to use
expectto test different side-effects of the same scenario, like different side effects of the same action, but you can use it any way you please.
There is an
expectmethod equivalent for every
Success events (a new concept) - When you want to register information about the test, like numeric results of actions or time measurement of actions.
The success information will be registered into the test’s metadata, like any other failure, error, or skip message, and will be visible in the DB, excel, etc.
from rotest.core import TestCase from resources_app.resources import Calculator class AddTest(TestCase): calc = Calculator() def test_add(self): self.success("One way to register success") # Or self.addSuccess("Another way to register success") value = self.calc.calculate("1 + 1") self.expectEqual(value, 3, msg="Expected value 3, got %r" % value, success_msg="Value is %r, as expected" % value) # Or self.assertEqual(value, 3, msg="Expected value 3, got %r" % value, success_msg="Value is %r, as expected" % value)