diff --git a/docs/source/resultyaml.rst b/docs/source/resultyaml.rst
--- a/docs/source/resultyaml.rst
+++ b/docs/source/resultyaml.rst
@@ -4,7 +4,8 @@
 Task result format
 ==================
 
-The required output format of the checks in Taskotron is YAML.
+The required output format for tasks in Taskotron is YAML with particular values, which we call
+*ResultYAML*.
 
 We recommend using the provided :py:class:`libtaskotron.check.CheckDetail` and the
 helper method :py:meth:`libtaskotron.check.export_YAML` to create the YAML output.
diff --git a/libtaskotron.spec b/libtaskotron.spec
--- a/libtaskotron.spec
+++ b/libtaskotron.spec
@@ -36,7 +36,7 @@
 BuildRequires:  python2-devel
 BuildRequires:  python-configparser >= 3.5.0b2
 BuildRequires:  python-dingus >= 0.3.4
-BuildRequires:  python-mock >= 1.0.1
+BuildRequires:  python-mock >= 2.0.0
 BuildRequires:  python-progressbar >= 2.3
 BuildRequires:  python-resultsdb_api >= 1.2.2
 BuildRequires:  python-requests >= 2.7.0
diff --git a/libtaskotron/directives/koji_directive.py b/libtaskotron/directives/koji_directive.py
--- a/libtaskotron/directives/koji_directive.py
+++ b/libtaskotron/directives/koji_directive.py
@@ -87,6 +87,8 @@
   * `log_errors`: (list of str) a list of architectures for which the ``build.log`` could not be
     downloaded
 raises: |
+  * :class:`.TaskotronDirectiveError`: if mandatory parameters are missing or incorrect parameter
+    values were provided
   * :class:`.TaskotronRemoteError`: if downloading failed
 version_added: 0.4
 """
diff --git a/libtaskotron/directives/xunit_directive.py b/libtaskotron/directives/xunit_directive.py
--- a/libtaskotron/directives/xunit_directive.py
+++ b/libtaskotron/directives/xunit_directive.py
@@ -15,67 +15,70 @@
 module: xunit_directive
 short_description: parse XML xUnit file
 description: |
-  Parse test ouput in XML xUnit format and generate ResultYAML for further use (i.e. in resultsdb)
+  Parse test ouput in XML xUnit format and generate :doc:`ResultYAML </resultyaml>` for further use
+  (i.e. directly usable in :doc:`resultsdb directive <resultsdb_directive_module>`).
+
+  .. note::
+    The input xUnit file is set as an artifact in the output ResultYAML result, therefore it's
+    recommended that you place it inside ``${artifactsdir}``.
 parameters:
   file:
     required: true
-    description: xml file to be parsed, relative path is to the task formula file
+    description: xUnit XML file path to be parsed, either absolute or relative to task directory
     type: str
   aggregation:
     required: false
-    description: type of result aggregation, ``none`` means no aggregation will be use and all
-      testcases will be reporter as separate results. ``allpass`` will agregate testcases into
-      one results with outcome ``PASSED`` if all testcases passed otherwise ``FAILED`` outcome is
-      set.
-      If not provided ``allpass`` is default value.
+    description: |
+      type of result aggregation: ``allpass`` will aggregate testcases into a single result with
+      outcome ``PASSED`` if all testcases passed, otherwise ``FAILED``. ``none`` means no
+      aggregation and all testcases will be reported as separate results.
     type: str
+    default: allpass
+    choices: [allpass, none]
 returns: |
-  Returns output of the check in the ResultYAML format directly usable in
-  :ref:`resultsdb directive <resultsdb_directive>`
-
+  check result(s) in the ResultYAML format
 raises: |
-  * :class:`.TaskotronDirectiveError`: if ``file`` was not provided or aggregation
-    was set to unknown type.
+  * :class:`.TaskotronDirectiveError`: if mandatory parameters are missing or incorrect parameter
+    values were provided
 version_added: 0.4.14
 """
 
 EXAMPLES = """
-This example parses XML file located in ``artifactsdir`` generates ResultYAML with no aggregation
-and exports it to variable which is finally passed to ``resultsdb``::
+This example parses xUnit XML file located in ``${artifactsdir}``, generates ResultYAML from it and
+exports it to a variable which is then passed to ``resultsdb`` directive::
 
-  - name: parse test output
+  - name: parse xunit output
     xunit:
-        file: ${artifactsdir}/output.xml
-        aggregation: none
-    export: test_output
+        file: ${artifactsdir}/xunit_output.xml
+    export: resultyaml
 
   - name: report results to resultsdb
     resultsdb:
-        results: ${test_output}
+        results: ${resultyaml}
 """
 
-aggregations = ['none', 'allpass']
-
 directive_class = 'XunitDirective'
 
 
 class XunitDirective(BaseDirective):
+
+    aggregations = ['none', 'allpass']
+
     def process(self, params, arg_data):
-        try:
-            artifact = params['file']
-        except KeyError:
+        if 'file' not in params:
             raise TaskotronDirectiveError('No file provided.')
+        xunitfile = params['file']
 
         aggregation = params.get('aggregation', 'allpass')
 
         if not arg_data['item'] or not arg_data['type']:
             raise TaskotronDirectiveError('Item and its type has to be provided.')
 
-        if aggregation not in aggregations:
+        if aggregation not in self.aggregations:
             raise TaskotronDirectiveError(
-                    'Aggregation must be one of %s.' % ', '.join(aggregations))
+                "Aggregation '%s' is not one of: %s" % (aggregation, ', '.join(self.aggregations)))
 
-        with open(artifact) as xmlfile:
+        with open(xunitfile) as xmlfile:
             testsuite, testresult = xunitparser.parse(xmlfile)
 
         if aggregation == 'none':
@@ -86,27 +89,21 @@
                                            checkname="%s.%s" % (arg_data['checkname'],
                                                                 tc.methodname),
                                            report_type=arg_data['type'],
-                                           outcome=outcome))
-
+                                           outcome=outcome,
+                                           artifact=xunitfile))
             return export_YAML(details)
+
         elif aggregation == 'allpass':
-            summary = "%d PASSED, %d FAILED"
-            passed = 0
-            failed = 0
-            final_outcome = 'PASSED'
-            for tc in testsuite:
-                if tc.good:
-                    passed += 1
-                else:
-                    failed += 1
-                    final_outcome = 'FAILED'
+            passed = len([tc for tc in testsuite if tc.good])
+            failed = len([tc for tc in testsuite if not tc.good])
+            final_outcome = 'PASSED' if failed == 0 else 'FAILED'
+            note = CheckDetail.create_multi_item_summary(['PASSED'] * passed + ['FAILED'] * failed)
 
-            summary = summary % (passed, failed)
             return export_YAML(CheckDetail(item=arg_data['item'],
-                                           note=summary,
+                                           note=note,
                                            report_type=arg_data['type'],
                                            outcome=final_outcome,
-                                           artifact=artifact))
+                                           artifact=xunitfile))
 
         else:
-            assert False, "This should never happen"
+            assert False, "This should never happen, aggregation is %r" % aggregation
diff --git a/requirements.txt b/requirements.txt
--- a/requirements.txt
+++ b/requirements.txt
@@ -14,7 +14,7 @@
 dingus == 0.3.4
 Jinja2 == 2.8
 munch == 2.0.2
-mock == 1.0.1
+mock == 2.0.0
 paramiko == 1.15.1
 pytest == 2.7.3
 pytest-cov == 2.2.1
diff --git a/testing/test_xunit_directive.py b/testing/test_xunit_directive.py
--- a/testing/test_xunit_directive.py
+++ b/testing/test_xunit_directive.py
@@ -10,9 +10,8 @@
 import mock
 import pytest
 import xunitparser
-import io
 
-OUTPUT = 'Kilo kabanosu naprudko'
+OUTPUT = 'I shall not use Czech in source code'
 
 XML = """<?xml version="1.0"?>
 <testsuite errors="0" failures="0" name="pytest" skips="0" tests="3" time="17.131">
@@ -25,113 +24,73 @@
 
 
 class TestCheckParams(object):
-    def test_file_present(self, monkeypatch):
-        test_params = {'file': 'json.xml'}
-        test_arg_data = {'item': 'starobrno', 'type': 'skopek'}
 
-        test_directive = xunit_directive.XunitDirective()
+    def setup(self):
+        self.params = {'file': 'json.xml'}
+        self.arg_data = {'item': 'some_item', 'type': 'some_type'}
+        self.directive = xunit_directive.XunitDirective()
 
+    @pytest.fixture
+    def mock_parse(self, monkeypatch):
         stub_parse = mock.MagicMock(return_value=([], True))
         monkeypatch.setattr(xunitparser, 'parse', stub_parse)
 
-        with mock.patch('__builtin__.open', mock.mock_open(), create=True):
-            with mock.patch('libtaskotron.directives.xunit_directive.export_YAML',
-                            mock.MagicMock(return_value=OUTPUT)):
-                output = test_directive.process(test_params, test_arg_data)
+    @pytest.fixture
+    def mock_open(self, monkeypatch, read_data=None):
+        monkeypatch.setattr(xunit_directive, 'open', mock.mock_open(read_data=read_data),
+                            raising=False)
 
-        assert output == OUTPUT
-
-    def test_file_not_present(self, monkeypatch):
-        test_params = {'fila': 'json.xml'}
-        test_arg_data = {'item': 'starobrno', 'type': 'skopek'}
-        test_directive = xunit_directive.XunitDirective()
-
-        stub_parse = mock.MagicMock(return_value=([], True))
-        monkeypatch.setattr(xunitparser, 'parse', stub_parse)
-
-        with mock.patch('__builtin__.open', mock.mock_open(), create=True):
-            with pytest.raises(TaskotronDirectiveError):
-                test_directive.process(test_params, test_arg_data)
+    def test_file_present(self, monkeypatch, mock_parse, mock_open):
+        monkeypatch.setattr(xunit_directive, 'export_YAML', mock.Mock(return_value=OUTPUT))
 
-    def test_item_not_present(self, monkeypatch):
-        test_params = {'file': 'json.xml'}
-        test_arg_data = {'item': None, 'type': 'skopek'}
-        test_directive = xunit_directive.XunitDirective()
+        output = self.directive.process(self.params, self.arg_data)
+        assert output == OUTPUT
 
-        stub_parse = mock.MagicMock(return_value=([], True))
-        monkeypatch.setattr(xunitparser, 'parse', stub_parse)
+    def test_file_not_present(self, mock_parse, mock_open):
+        self.params = {'fila': 'json.xml'}
 
-        with mock.patch('__builtin__.open', mock.mock_open(), create=True):
-            with pytest.raises(TaskotronDirectiveError):
-                test_directive.process(test_params, test_arg_data)
+        with pytest.raises(TaskotronDirectiveError):
+            self.directive.process(self.params, self.arg_data)
 
-    def test_type_not_present(self, monkeypatch):
-        test_params = {'file': 'json.xml'}
-        test_arg_data = {'item': 'starobrno', 'type': None}
-        test_directive = xunit_directive.XunitDirective()
+    def test_item_not_present(self, mock_parse, mock_open):
+        self.arg_data['item'] = None
 
-        stub_parse = mock.MagicMock(return_value=([], True))
-        monkeypatch.setattr(xunitparser, 'parse', stub_parse)
-
-        with mock.patch('__builtin__.open', mock.mock_open(), create=True):
-            with pytest.raises(TaskotronDirectiveError):
-                test_directive.process(test_params, test_arg_data)
+        with pytest.raises(TaskotronDirectiveError):
+            self.directive.process(self.params, self.arg_data)
 
-    def test_file_does_not_exist(self, monkeypatch):
-        test_params = {'file': 'json.xml'}
-        test_arg_data = {'item': 'starobrno', 'type': 'skopek'}
-        test_directive = xunit_directive.XunitDirective()
+    def test_type_not_present(self, mock_parse, mock_open):
+        self.arg_data['type'] = None
 
-        stub_parse = mock.MagicMock(return_value=([], True))
-        monkeypatch.setattr(xunitparser, 'parse', stub_parse)
+        with pytest.raises(TaskotronDirectiveError):
+            self.directive.process(self.params, self.arg_data)
 
+    def test_file_does_not_exist(self, mock_parse):
         with pytest.raises(IOError):
-            test_directive.process(test_params, test_arg_data)
+            self.directive.process(self.params, self.arg_data)
 
-    def test_aggregation_unknown(self, monkeypatch):
-        test_params = {'file': 'json.xml', 'aggregation': 'allfail'}
-        test_arg_data = {'item': 'starobrno', 'type': 'skopek'}
-        test_directive = xunit_directive.XunitDirective()
-
-        stub_parse = mock.MagicMock(return_value=([], True))
-        monkeypatch.setattr(xunitparser, 'parse', stub_parse)
+    def test_aggregation_unknown(self, mock_parse):
+        self.params['aggregation'] = 'allfail'
 
         with pytest.raises(TaskotronDirectiveError):
-            test_directive.process(test_params, test_arg_data)
+            self.directive.process(self.params, self.arg_data)
 
     def test_aggregation_none(self, monkeypatch):
-        test_params = {'file': 'json.xml', 'aggregation': 'none'}
-        test_arg_data = {'item': 'starobrno', 'type': 'skopek', 'checkname': 'ochutnavka'}
-        test_directive = xunit_directive.XunitDirective()
-
-        # https://bugzilla.redhat.com/show_bug.cgi?id=1244145#c18
-        # with mock.patch('__builtin__.open', mock.mock_open(read_data=XML), create=True):
-        with mock.patch('__builtin__.open', mock.MagicMock(return_value=io.BytesIO(XML)),
-                        create=True):
-            output = test_directive.process(test_params, test_arg_data)
+        self.params['aggregation'] = 'none'
+        self.arg_data['checkname'] = 'mycheck'
+        self.mock_open(monkeypatch, read_data=XML)
 
+        output = self.directive.process(self.params, self.arg_data)
         assert len(import_YAML(output)) == 3
 
     def test_aggregation_allpass(self, monkeypatch):
-        test_params = {'file': 'json.xml', 'aggregation': 'allpass'}
-        test_arg_data = {'item': 'starobrno', 'type': 'skopek'}
-        test_directive = xunit_directive.XunitDirective()
-
-        # ditto
-        with mock.patch('__builtin__.open', mock.MagicMock(return_value=io.BytesIO(XML)),
-                        create=True):
-            output = test_directive.process(test_params, test_arg_data)
+        self.params['aggregation'] = 'allpass'
+        self.mock_open(monkeypatch, read_data=XML)
 
+        output = self.directive.process(self.params, self.arg_data)
         assert len(import_YAML(output)) == 1
 
     def test_aggregation_allpass_default(self, monkeypatch):
-        test_params = {'file': 'json.xml'}
-        test_arg_data = {'item': 'starobrno', 'type': 'skopek'}
-        test_directive = xunit_directive.XunitDirective()
-
-        # ditto
-        with mock.patch('__builtin__.open', mock.MagicMock(return_value=io.BytesIO(XML)),
-                        create=True):
-            output = test_directive.process(test_params, test_arg_data)
+        self.mock_open(monkeypatch, read_data=XML)
 
+        output = self.directive.process(self.params, self.arg_data)
         assert len(import_YAML(output)) == 1

