|
| 1 | +# Canopen unit tests |
| 2 | + |
| 3 | +This directory contains the unittests for the canopoen library. canopen use |
| 4 | +`unittest` as the framework for testing. |
| 5 | + |
| 6 | + |
| 7 | +## Testing without async |
| 8 | + |
| 9 | +If writing a test that doesn't require or depend on async features, tests |
| 10 | +can be written as. |
| 11 | + |
| 12 | +```python |
| 13 | +class TestVariable(unittest.TestCase): |
| 14 | + ... |
| 15 | +``` |
| 16 | + |
| 17 | +See [`test_variable.py`](`test_variable.py`) as an example |
| 18 | + |
| 19 | + |
| 20 | +## Testing with async |
| 21 | + |
| 22 | +Since this library supports usage with async support and with regular blocking |
| 23 | +calls, the unit tests must test both cases. This does requre a little bit more |
| 24 | +setup in the testing. |
| 25 | + |
| 26 | +First create a base class that is intended to be run twice, once without async |
| 27 | +and once with async enabled. |
| 28 | + |
| 29 | +```python |
| 30 | +from .async_tests import DualSyncAsyncTestCase |
| 31 | + |
| 32 | +class TestEmcy(DualSyncAsyncTestCase): |
| 33 | + __test__ = False # This is a base class that shall not run directly |
| 34 | + |
| 35 | + # The following attrobutes are available: |
| 36 | + # async_test: bool # Flag if async testing is running |
| 37 | + # loop: Optional[asyncio.AbstractEventLoop] # The loop in async mode, |
| 38 | + # # `None` in regular mode. |
| 39 | + |
| 40 | + def setUp(self): |
| 41 | + super().setUp() # Make sure this is called when overriding `setUp` |
| 42 | + # ... do your setup |
| 43 | + |
| 44 | + # Any tests that doesn't depend on async, can be written as regular |
| 45 | + # test methods |
| 46 | + def test_emcy_error(self): |
| 47 | + self.assertEqual(...) |
| 48 | + |
| 49 | + # Any tests that requre async, use `async def` |
| 50 | + async def test_method(self): |
| 51 | + if self.async_test: |
| 52 | + # This is when async is enabled. |
| 53 | + await some_async_command() |
| 54 | + else: |
| 55 | + # This is when async is not running |
| 56 | + some_regular_command() |
| 57 | +``` |
| 58 | + |
| 59 | +To run this class, two instances of the test class must be created. One with |
| 60 | +async and one without: |
| 61 | + |
| 62 | +```python |
| 63 | +class TestEmcySync(TestEmcy): |
| 64 | + """Run the tests in non-asynchronous mode.""" |
| 65 | + __test__ = True # This is test to run |
| 66 | + async_test = False # Not async mode |
| 67 | + |
| 68 | +class TestEmcyAsync(TestEmcy): |
| 69 | + """Run the tests in asynchronous mode.""" |
| 70 | + __test__ = True # This is tests to run |
| 71 | + async_test = True # In async mode |
| 72 | +``` |
| 73 | + |
| 74 | +Note that there is no internal difference between the `Sync` and `Async` runs |
| 75 | +except for the changes in `self.async_test` and `self.loop`. It is the |
| 76 | +responsibility of the tests to setup the unit test with and without async. |
| 77 | + |
| 78 | + |
| 79 | +### Setting up a Network instance in async |
| 80 | + |
| 81 | +When setting up a `Network()` instance in async, it is important that its async |
| 82 | +context is entered. If `setUp()` contains `self.network = Network(loop=self.loop)` |
| 83 | +then the following can be added: |
| 84 | + |
| 85 | +```python |
| 86 | + async def asyncSetUp(self): |
| 87 | + if self.async_test: |
| 88 | + await self.network.__aenter__() |
| 89 | + |
| 90 | + async def asyncTearDown(self): |
| 91 | + if self.async_test: |
| 92 | + await self.network.__aexit__(None, None, None) |
| 93 | +``` |
| 94 | + |
| 95 | + |
| 96 | +### Excluding async from a test |
| 97 | + |
| 98 | +The easiest way is to do: |
| 99 | + |
| 100 | +```python |
| 101 | +def test_something(self): |
| 102 | + if self.async_test: |
| 103 | + self.skipTest("Async is not supported because ...") |
| 104 | +``` |
0 commit comments