Key Concepts
Fixtures
Each test case would initialize a few fixtures. The most important fixtures are:
msg_queue
, a message queue. There’s a background listener process to read all the messages from this queue, log them to the terminal with an optional timestamp, and record them to the pexpect process.pexpect_proc
, a pexpect process, that could runpexpect.expect()
for testing purposes.app
, the built binarydut
, Device Under Test (DUT)A DUT would contain several daemon processes/threads. The output of each of them would be redirected to the
msg_queue
fixture.
Note
You may redirect any output to the msg_queue
fixture by contextlib.redirect_stdout
.
import contextlib
def test_redirect(dut, msg_queue):
with contextlib.redirect_stdout(msg_queue):
print('will be redirected')
dut.expect_exact('redirected')
Or you may redirect the output from a fixture redirect
def test_redirect(dut, msg_queue, redirect):
with redirect():
print('will also be redirected')
dut.expect_exact('redirected')
You can run pytest --fixtures
to get all the fixtures defined with pytest-embedded
. They are under the section fixtures defined from pytest_embedded.plugin
.
Parametrization
All the CLI options support parametrization via indirect=True
. Parametrization is a feature provided by pytest
, please refer to Parametrizing tests for its documentation.
For example, running shell command pytest
with the test script:
@pytest.mark.parametrize(
'embedded_services, app_path',
[
('idf', app_path_1),
('idf', app_path_2),
],
indirect=True,
)
def test_serial_tcp(dut):
assert dut.app.target == 'esp32'
dut.expect('Restart now')
is equivalent to running two shell commands pytest --embedded-services idf --app-path <app_path_1>
and pytest --embedded-services idf --app-path <app_path_2>
with the test script:
def test_serial_tcp(dut):
assert dut.app.target == 'esp32'
dut.expect('Restart now')
Services
You can activate more services with pytest --embedded-services service[,service]
to enable extra fixtures and functionalities. These services are provided by several optional dependencies. You can install them via pip
as well.
Available services:
serial
: serial port utilities.esp
: auto-detect target/port by esptool.idf
: auto-detect more app info with ESP-IDF specific rules, auto-flash the binary into the target.jtag
: openocd/gdb utilities.qemu
: running test cases on QEMU instead of the real target.arduino
: auto-detect more app info with arduino specific rules, auto-flash the binary into the target.wokwi
: running test cases with Wokwi instead of the real target.
Multi DUTs
Sometimes we need multi DUTs while testing, e.g., master-slave or mesh testing.
Here are a few examples of how to enable this. For detailed information, please refer to the embedded
group of the pytest --help
.
Enable multi DUTs by specifying --count
After you enabled the multi-dut mode, all the fixtures would be a tuple with instances. Each instance inside the tuple would be independent. For parametrization, each configuration will use |
as a separator for each instance.
For example, running shell command:
pytest \
--embedded-services serial|serial \
--count 2 \
--app-path <master_bin>|<slave_bin>
would enable 2 DUTs with serial
service. app
would be a tuple of 2 App
instances, and dut
would be a tuple of 2 Dut
Instances.
You can test with:
def test(dut):
master = dut[0]
slave = dut[1]
master.expect('sent')
slave.expect('received')
Specify once when applying to all DUTs
If all DUTs share the same configuration value, you can specify only once.
pytest \
--embedded-services serial \
--count 2 \
--app-path <master_bin>|<slave_bin>
--embedded-services serial
would apply to all DUTs
Vacant Value if it’s Useless
Sometimes one option is only useful when enabling specific services. You can set a vacant value if this config is only useful for certain DUTs.
pytest \
--embedded-services qemu|serial \
--count 2 \
--app-path <master_bin>|<slave_bin> \
--qemu-cli-args "<args>|" \
--port "|<port>"
--qemu-cli-args
would apply to the first DUT with qemu
service and --port
would apply to the second DUT with serial
service.
Logging
pytest-embedded
print all the DUT output with the timestamp. If you want to remove the timestamp, please run pytest with pytest --with-timestamp n
to disable this feature.
By default, pytest
would swallow the stdout. If you want to check the live output, please run pytest with pytest -s
.