Source code for robot.model.testcase

#  Copyright 2008-2015 Nokia Networks
#  Copyright 2016-     Robot Framework Foundation
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

from pathlib import Path
from typing import Any, Generic, Sequence, Type, TYPE_CHECKING, TypeVar

from robot.utils import setter

from .body import Body, BodyItem
from .fixture import create_fixture
from .itemlist import ItemList
from .keyword import Keyword
from .modelobject import DataDict, ModelObject
from .tags import Tags

if TYPE_CHECKING:
    from .testsuite import TestSuite
    from .visitor import SuiteVisitor


TC = TypeVar('TC', bound='TestCase')
KW = TypeVar('KW', bound='Keyword', covariant=True)


[docs] class TestCase(ModelObject, Generic[KW]): """Base model for a single test case. Extended by :class:`robot.running.model.TestCase` and :class:`robot.result.model.TestCase`. """ body_class = Body # See model.TestSuite on removing the type ignore directive fixture_class: Type[KW] = Keyword # type: ignore repr_args = ('name',) __slots__ = ['parent', 'name', 'doc', 'timeout', 'lineno', '_setup', '_teardown'] def __init__(self, name: str = '', doc: str = '', tags: 'Tags|Sequence[str]' = (), timeout: 'str|None' = None, lineno: 'int|None' = None, parent: 'TestSuite[KW, TestCase[KW]]|None' = None): self.name = name self.doc = doc self.tags = tags self.timeout = timeout self.lineno = lineno self.parent = parent self.body = [] self._setup: 'KW|None' = None self._teardown: 'KW|None' = None @setter def body(self, body: 'Sequence[BodyItem|DataDict]') -> Body: """Test body as a :class:`~robot.model.body.Body` object.""" return self.body_class(self, body) @setter def tags(self, tags: 'Tags|Sequence[str]') -> Tags: """Test tags as a :class:`~.model.tags.Tags` object.""" return Tags(tags) @property def setup(self) -> KW: """Test setup as a :class:`~.model.keyword.Keyword` object. This attribute is a ``Keyword`` object also when a test has no setup but in that case its truth value is ``False``. Setup can be modified by setting attributes directly:: test.setup.name = 'Example' test.setup.args = ('First', 'Second') Alternatively the :meth:`config` method can be used to set multiple attributes in one call:: test.setup.config(name='Example', args=('First', 'Second')) The easiest way to reset the whole setup is setting it to ``None``. It will automatically recreate the underlying ``Keyword`` object:: test.setup = None New in Robot Framework 4.0. Earlier setup was accessed like ``test.keywords.setup``. """ if self._setup is None: self._setup = create_fixture(self.fixture_class, None, self, Keyword.SETUP) return self._setup @setup.setter def setup(self, setup: 'KW|DataDict|None'): self._setup = create_fixture(self.fixture_class, setup, self, Keyword.SETUP) @property def has_setup(self) -> bool: """Check does a suite have a setup without creating a setup object. A difference between using ``if test.has_setup:`` and ``if test.setup:`` is that accessing the :attr:`setup` attribute creates a :class:`Keyword` object representing the setup even when the test actually does not have one. This typically does not matter, but with bigger suite structures containing a huge about of tests it can have an effect on memory usage. New in Robot Framework 5.0. """ return bool(self._setup) @property def teardown(self) -> KW: """Test teardown as a :class:`~.model.keyword.Keyword` object. See :attr:`setup` for more information. """ if self._teardown is None: self._teardown = create_fixture(self.fixture_class, None, self, Keyword.TEARDOWN) return self._teardown @teardown.setter def teardown(self, teardown: 'KW|DataDict|None'): self._teardown = create_fixture(self.fixture_class, teardown, self, Keyword.TEARDOWN) @property def has_teardown(self) -> bool: """Check does a test have a teardown without creating a teardown object. See :attr:`has_setup` for more information. New in Robot Framework 5.0. """ return bool(self._teardown) @property def id(self) -> str: """Test case id in format like ``s1-t3``. See :attr:`TestSuite.id <robot.model.testsuite.TestSuite.id>` for more information. """ if not self.parent: return 't1' tests = self.parent.tests index = tests.index(self) if self in tests else len(tests) return f'{self.parent.id}-t{index + 1}' @property def full_name(self) -> str: """Test name prefixed with the full name of the parent suite.""" if not self.parent: return self.name return f'{self.parent.full_name}.{self.name}' @property def longname(self) -> str: """Deprecated since Robot Framework 7.0. Use :attr:`full_name` instead.""" return self.full_name @property def source(self) -> 'Path|None': return self.parent.source if self.parent is not None else None
[docs] def visit(self, visitor: 'SuiteVisitor'): """:mod:`Visitor interface <robot.model.visitor>` entry-point.""" visitor.visit_test(self)
[docs] def to_dict(self) -> 'dict[str, Any]': data: 'dict[str, Any]' = {'name': self.name} if self.doc: data['doc'] = self.doc if self.tags: data['tags'] = tuple(self.tags) if self.timeout: data['timeout'] = self.timeout if self.lineno: data['lineno'] = self.lineno if self.has_setup: data['setup'] = self.setup.to_dict() if self.has_teardown: data['teardown'] = self.teardown.to_dict() data['body'] = self.body.to_dicts() return data
[docs] class TestCases(ItemList[TC]): __slots__ = [] def __init__(self, test_class: Type[TC] = TestCase, parent: 'TestSuite|None' = None, tests: 'Sequence[TC|DataDict]' = ()): super().__init__(test_class, {'parent': parent}, tests) def _check_type_and_set_attrs(self, test): test = super()._check_type_and_set_attrs(test) if test.parent: for visitor in test.parent._visitors: test.visit(visitor) return test