Skip to content

Commit d86d5c6

Browse files
committed
Add 'failed' property to result model.
Also enhance docs and tests of 'passed' and 'skipped'.
1 parent 4239a33 commit d86d5c6

3 files changed

Lines changed: 94 additions & 24 deletions

File tree

src/robot/model/stats.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def __init__(self, name):
3636
self.passed = 0
3737
#: Number of failed tests.
3838
self.failed = 0
39+
#: Number of skipped tests.
3940
self.skipped = 0
4041
#: Number of milliseconds it took to execute.
4142
self.elapsed = 0

src/robot/result/model.py

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,28 @@ def name(self):
106106

107107
@property
108108
def passed(self):
109-
"""``True`` or ``False`` depending on the :attr:`status`."""
109+
"""``True`` when :attr:`status` is 'PASS', ``False`` otherwise."""
110110
return self.status == 'PASS'
111111

112112
@passed.setter
113113
def passed(self, passed):
114114
self.status = 'PASS' if passed else 'FAIL'
115115

116+
@property
117+
def failed(self):
118+
"""``True`` when :attr:`status` is 'FAIL', ``False`` otherwise."""
119+
return self.status == 'FAIL'
120+
121+
@failed.setter
122+
def failed(self, failed):
123+
self.status = 'FAIL' if failed else 'PASS'
124+
116125
@property
117126
def skipped(self):
127+
"""``True`` when :attr:`status` is 'SKIP', ``False`` otherwise.
128+
129+
Setting to ``False`` value is ambiguous and raises an exception.
130+
"""
118131
return self.status == 'SKIP'
119132

120133
@skipped.setter
@@ -153,15 +166,28 @@ def elapsedtime(self):
153166

154167
@property
155168
def passed(self):
156-
"""``True/False`` depending on the :attr:`status`."""
169+
"""``True`` when :attr:`status` is 'PASS', ``False`` otherwise."""
157170
return self.status == 'PASS'
158171

159172
@passed.setter
160173
def passed(self, passed):
161174
self.status = 'PASS' if passed else 'FAIL'
162175

176+
@property
177+
def failed(self):
178+
"""``True`` when :attr:`status` is 'FAIL', ``False`` otherwise."""
179+
return self.status == 'FAIL'
180+
181+
@failed.setter
182+
def failed(self, failed):
183+
self.status = 'FAIL' if failed else 'PASS'
184+
163185
@property
164186
def skipped(self):
187+
"""``True`` when :attr:`status` is 'SKIP', ``False`` otherwise.
188+
189+
Setting to ``False`` value is ambiguous and raises an exception.
190+
"""
165191
return self.status == 'SKIP'
166192

167193
@skipped.setter
@@ -201,7 +227,12 @@ def __init__(self, name='', doc='', metadata=None, source=None,
201227
@property
202228
def passed(self):
203229
"""``True`` if no test has failed, ``False`` otherwise."""
204-
return not self.statistics.all.failed
230+
return self.status == 'PASS'
231+
232+
@property
233+
def failed(self):
234+
"""``True`` if any test has failed, ``False`` otherwise."""
235+
return self.status == 'FAIL'
205236

206237
@property
207238
def skipped(self):
@@ -210,11 +241,18 @@ def skipped(self):
210241

211242
@property
212243
def status(self):
213-
"""``'FAIL'`` if any test has failed, ``'SKIP'`` if all tests have
214-
been skipped, ``'PASS'`` otherwise."""
215-
if self.statistics.all.failed:
244+
"""'PASS', 'FAIL' or 'SKIP' depending on test statuses.
245+
246+
- If any test has failed, status is 'FAIL'.
247+
- If no test has failed but some has passed, status is 'PASS'.
248+
- If all tests have been skipped, status is 'SKIP'.
249+
"""
250+
stats = self.statistics.all # Local variable avoids recreating stats.
251+
if stats.failed:
216252
return 'FAIL'
217-
if self.statistics.all.total == 0 or self.statistics.all.passed:
253+
# TODO: Would 'SKIP' be better status if there are no executed tests?
254+
# Update also docstring above accordingly.
255+
if stats.total == 0 or stats.passed:
218256
return 'PASS'
219257
return 'SKIP'
220258

utest/result/test_resultmodel.py

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,29 @@ def test_suite_status_is_fail_if_failed_subsuite(self):
8383
suite.suites.create().tests.create(status='FAIL')
8484
assert_equal(suite.status, 'FAIL')
8585

86-
def test_passed(self):
86+
def test_passed_failed_skipped_propertys(self):
8787
suite = TestSuite()
8888
assert_true(suite.passed)
89+
assert_false(suite.failed)
90+
assert_false(suite.skipped)
91+
suite.tests.create(status='SKIP')
92+
assert_false(suite.passed)
93+
assert_false(suite.failed)
94+
assert_true(suite.skipped)
8995
suite.tests.create(status='PASS')
9096
assert_true(suite.passed)
91-
suite.tests.create(status='FAIL', tags='tag')
97+
assert_false(suite.failed)
98+
assert_false(suite.skipped)
99+
suite.tests.create(status='FAIL')
92100
assert_false(suite.passed)
101+
assert_true(suite.failed)
102+
assert_false(suite.skipped)
103+
104+
def test_suite_status_cannot_be_set_directly(self):
105+
suite = TestSuite()
106+
for attr in 'status', 'passed', 'failed', 'skipped':
107+
assert_true(hasattr(suite, attr))
108+
assert_raises(AttributeError, setattr, suite, attr, True)
93109

94110

95111
class TestElapsedTime(unittest.TestCase):
@@ -143,32 +159,47 @@ def test_keyword_name(self):
143159
def test_keyword_name_cannot_be_set_directly(self):
144160
assert_raises(AttributeError, setattr, Keyword(), 'name', 'value')
145161

146-
def test_test_passed(self):
147-
self._test_passed(TestCase())
162+
def test_test_passed_failed_skipped_propertys(self):
163+
self._verify_passed_failed_skipped(TestCase())
148164

149-
def test_keyword_passed(self):
150-
self._test_passed(Keyword())
165+
def test_keyword_passed_failed_skipped_propertys(self):
166+
self._verify_passed_failed_skipped(Keyword())
151167

152168
def test_keyword_passed_after_dry_run(self):
153-
self._test_passed(Keyword(status='NOT_RUN'),
154-
initial_status='NOT_RUN')
169+
self._verify_passed_failed_skipped(Keyword(status='NOT_RUN'),
170+
initial_status='NOT_RUN')
155171

156-
def _test_passed(self, item, initial_status='FAIL'):
157-
assert_equal(item.passed, False)
172+
def _verify_passed_failed_skipped(self, item, initial_status='FAIL'):
158173
assert_equal(item.status, initial_status)
174+
assert_equal(item.passed, False)
175+
assert_equal(item.failed, initial_status == 'FAIL')
176+
assert_equal(item.skipped, False)
159177
item.passed = True
160178
assert_equal(item.passed, True)
179+
assert_equal(item.failed, False)
180+
assert_equal(item.skipped, False)
161181
assert_equal(item.status, 'PASS')
162182
item.passed = False
163183
assert_equal(item.passed, False)
184+
assert_equal(item.failed, True)
185+
assert_equal(item.skipped, False)
164186
assert_equal(item.status, 'FAIL')
165-
166-
def test_suite_passed(self):
167-
suite = TestSuite()
168-
assert_equal(suite.passed, True)
169-
suite.tests.create(status='FAIL')
170-
assert_equal(suite.passed, False)
171-
assert_raises(AttributeError, setattr, TestSuite(), 'passed', True)
187+
item.failed = True
188+
assert_equal(item.passed, False)
189+
assert_equal(item.failed, True)
190+
assert_equal(item.skipped, False)
191+
assert_equal(item.status, 'FAIL')
192+
item.failed = False
193+
assert_equal(item.passed, True)
194+
assert_equal(item.failed, False)
195+
assert_equal(item.skipped, False)
196+
assert_equal(item.status, 'PASS')
197+
item.skipped = True
198+
assert_equal(item.passed, False)
199+
assert_equal(item.failed, False)
200+
assert_equal(item.skipped, True)
201+
assert_equal(item.status, 'SKIP')
202+
assert_raises(ValueError, setattr, item, 'skipped', False)
172203

173204

174205
if __name__ == '__main__':

0 commit comments

Comments
 (0)