Example #1
0
    def setUp(self):

        # Call superclass implementation to create the workspace
        super(SuiteRunnerFactoryTest, self).setUp()

        # Create mock instances to be returned by the constructors
        # of each mock class.
        self.mock_desc = mock.MagicMock(SuiteDescription)
        self.mock_renderer = mock.MagicMock(SuiteRenderer)
        self.mock_server = mock.MagicMock(SuitePageServer)
        self.mock_console_result = mock.MagicMock(ConsoleResultReporter)
        self.mock_xunit_result = mock.MagicMock(XUnitResultReporter)
        self.mock_html_coverage = mock.MagicMock(HtmlCoverageReporter)
        self.mock_xml_coverage = mock.MagicMock(XmlCoverageReporter)
        self.mock_browser = mock.MagicMock(Browser)
        self.mock_runner = mock.MagicMock(SuiteRunner)

        # Create mocks for each class that the factory will instantiate
        self.mock_desc_class = mock.MagicMock(return_value=self.mock_desc)
        self.mock_renderer_class = mock.MagicMock(return_value=self.mock_renderer)
        self.mock_server_class = mock.MagicMock(return_value=self.mock_server)
        self.mock_console_result_class = mock.MagicMock(return_value=self.mock_console_result)
        self.mock_xunit_result_class = mock.MagicMock(return_value=self.mock_xunit_result)
        self.mock_html_coverage_class = mock.MagicMock(return_value=self.mock_html_coverage)
        self.mock_xml_coverage_class = mock.MagicMock(return_value=self.mock_xml_coverage)
        self.mock_browser_class = mock.MagicMock(return_value=self.mock_browser)

        # Create the factory
        self.factory = SuiteRunnerFactory(
            desc_class=self.mock_desc_class,
            renderer_class=self.mock_renderer_class,
            server_class=self.mock_server_class,
            console_result_class=self.mock_console_result_class,
            xunit_result_class=self.mock_xunit_result_class,
            html_coverage_class=self.mock_html_coverage_class,
            xml_coverage_class=self.mock_xml_coverage_class,
            browser_class=self.mock_browser_class
        )
Example #2
0
class SuiteRunnerFactoryTest(TempWorkspaceTestCase):

    def setUp(self):

        # Call superclass implementation to create the workspace
        super(SuiteRunnerFactoryTest, self).setUp()

        # Create mock instances to be returned by the constructors
        # of each mock class.
        self.mock_desc = mock.MagicMock(SuiteDescription)
        self.mock_renderer = mock.MagicMock(SuiteRenderer)
        self.mock_server = mock.MagicMock(SuitePageServer)
        self.mock_console_result = mock.MagicMock(ConsoleResultReporter)
        self.mock_xunit_result = mock.MagicMock(XUnitResultReporter)
        self.mock_html_coverage = mock.MagicMock(HtmlCoverageReporter)
        self.mock_xml_coverage = mock.MagicMock(XmlCoverageReporter)
        self.mock_browser = mock.MagicMock(Browser)
        self.mock_runner = mock.MagicMock(SuiteRunner)

        # Create mocks for each class that the factory will instantiate
        self.mock_desc_class = mock.MagicMock(return_value=self.mock_desc)
        self.mock_renderer_class = mock.MagicMock(return_value=self.mock_renderer)
        self.mock_server_class = mock.MagicMock(return_value=self.mock_server)
        self.mock_console_result_class = mock.MagicMock(return_value=self.mock_console_result)
        self.mock_xunit_result_class = mock.MagicMock(return_value=self.mock_xunit_result)
        self.mock_html_coverage_class = mock.MagicMock(return_value=self.mock_html_coverage)
        self.mock_xml_coverage_class = mock.MagicMock(return_value=self.mock_xml_coverage)
        self.mock_browser_class = mock.MagicMock(return_value=self.mock_browser)

        # Create the factory
        self.factory = SuiteRunnerFactory(
            desc_class=self.mock_desc_class,
            renderer_class=self.mock_renderer_class,
            server_class=self.mock_server_class,
            console_result_class=self.mock_console_result_class,
            xunit_result_class=self.mock_xunit_result_class,
            html_coverage_class=self.mock_html_coverage_class,
            xml_coverage_class=self.mock_xml_coverage_class,
            browser_class=self.mock_browser_class
        )

    def test_configure_browsers(self):

        # Build a runner and configure it to test using these browsers
        browser_names = ['chrome', 'firefox', 'phantomjs']
        _, browsers = self._build_runner(1, browser_names=browser_names,
                                         coverage_xml_path='coverage.xml',
                                         coverage_html_path='coverage.html',
                                         timeout_sec=5)

        # Expect that the browsers were created using the provided timeout
        call_args = self.mock_browser_class.call_args_list
        for _, kwargs in call_args:
            timeout_sec = kwargs.get('timeout_sec')
            self.assertEqual(timeout_sec, 5)

        # Expect that the suite runner was configured with the correct browsers
        expected_browsers = [self.mock_browser] * len(browser_names)
        self.assertEqual(browsers, expected_browsers)

    def test_configure_server(self):

        # Build the runner
        num_suites = 5
        self._build_runner(num_suites)

        # Expect that the suite page server is correctly configured
        # Because of the way we configure the mocks, each suite description
        # should be identical.  Check that we get the right number.
        suite_desc_list = [self.mock_desc for _ in range(num_suites)]
        self.mock_server_class.assert_called_with(suite_desc_list,
                                                  self.mock_renderer,
                                                  jscover_path=None)

    def test_configure_suite_desc(self):

        # Build the runner
        # Ignore the return value because we are checking for calls the
        # factory makes to our mocks.
        num_suites = 5
        self._build_runner(num_suites)

        # Retrieve all the file paths passed to SuiteDescription constructors
        all_paths = []
        all_root_dirs = []
        for (args, _) in self.mock_desc_class.call_args_list:
            file_handle = args[0]
            all_paths.append(file_handle.name)
            all_root_dirs.append(args[1])

        # Expect that all the paths we passed to the factory were used
        # to instantiate SuiteDescription instances
        for suite_path in self._suite_paths(num_suites):
            self.assertIn(suite_path, all_paths)

        # Expect that all the root dirs are the temp directory
        # (where we created the description file)
        for root_dir in all_root_dirs:
            self.assertEqual(
                os.path.realpath(root_dir),
                os.path.realpath(self.temp_dir)
            )

    def test_configure_coverage(self):

        # Build the runner
        # Ignore the return value because we are checking for calls the
        # factory makes to our mocks.
        html_path = 'coverage.html'
        xml_path = 'coverage.xml'
        runner, _ = self._build_runner(
            1, coverage_html_path=html_path,
            coverage_xml_path=xml_path
        )

        # Expect that the coverage reporters were configured correctly
        self.mock_html_coverage_class.assert_called_with(html_path)
        self.mock_xml_coverage_class.assert_called_with(xml_path)
        self.assertEqual(
            runner.coverage_reporters(),
            [self.mock_xml_coverage, self.mock_html_coverage]
        )

    def test_configure_jscover(self):

        # Set the environment variable to configure
        # JSCover (used by the server to instrument
        # the JavaScript sources for coverage)
        with mock.patch.dict('os.environ', JSCOVER_JAR='jscover.jar'):
            runner, _ = self._build_runner(1, coverage_xml_path='coverage.xml')

        # Expect that the server was configured with the JSCover JAR path
        _, kwargs = self.mock_server_class.call_args
        self.assertEqual(kwargs.get('jscover_path'), 'jscover.jar')
        self.assertEqual(
            runner.coverage_reporters(),
            [self.mock_xml_coverage]
        )

    def test_configure_coverage_but_no_report(self):

        # Build a runner with no coverage report
        # But DO configure the JSCover environment variable
        with mock.patch.dict('os.environ', JSCOVER_JAR='jscover.jar'):
            runner, _ = self._build_runner(
                1, coverage_xml_path=None,
                coverage_html_path=None
            )

        # Expect that the server was NOT configured to use coverage
        _, kwargs = self.mock_server_class.call_args
        self.assertEqual(kwargs.get('jscover_path'), None)
        self.assertEqual(runner.coverage_reporters(), [])

    def test_configure_console_result(self):
        runner, _ = self._build_runner(1)

        # By default, only a console reporter is configured
        self.assertEqual(
            runner.result_reporters(),
            [self.mock_console_result]
        )

        # The console reporter should be configured
        # to send results to stdout
        self.mock_console_result_class.assert_called_with(sys.stdout)

    def test_configure_xunit_result(self):
        runner, _ = self._build_runner(1, xunit_path='foo.txt')

        # Should configure the runner to generate both
        # console and xunit reports
        self.assertEqual(
            runner.result_reporters(),
            [self.mock_console_result, self.mock_xunit_result]
        )

        # XUnit reporter should be configured with the right path
        args, _ = self.mock_xunit_result_class.call_args
        self.assertTrue(isinstance(args[0], file))
        self.assertEqual(args[0].name, 'foo.txt')

    def test_invalid_browser_names(self):

        with self.assertRaises(UnknownBrowserError):
            self._build_runner(1, browser_names=['chrome', 'invalid'])

    def test_empty_browser_name_list(self):

        with self.assertRaises(ValueError):
            self._build_runner(1, browser_names=[])

    @staticmethod
    def _suite_paths(num_suites):
        """
        Return a list of unique suite file paths of length `num_suites`.
        """
        return ['suite_{}.yaml'.format(num) for num in range(num_suites)]

    def _build_runner(self, num_suites,
                      xunit_path=None,
                      coverage_xml_path=None,
                      coverage_html_path=None,
                      browser_names=None,
                      timeout_sec=None):
        """
        Build a configured `SuiteRunner` instance
        using the `SuiteRunnerFactory`.

        `num_suites` is the number of suite descriptions to use.

        `xunit_path` is the path to the XUnit (XML) test result file.

        `coverage_xml_path` and `coverage_html_path` are the paths
        to the coverage reports to be generated.

        `browser_names` is a list of browser names to use in the
        suite descriptions.

        `timeout_sec` is the number of seconds to wait for a page to load
        before timing out

        Because we are using mock dependencies that always return the same
        values, each suite runner will be identical,
        and they will all use the same browser dependencies.

        Returns a tuple `(suite_runner, browsers)`.  See
        `SuiteRunnerFactory.build_runner()` for details.
        """

        # Supply default browser names
        if browser_names is None:
            browser_names = ['chrome']

        # Create fake suite description files
        suite_path_list = self._suite_paths(num_suites)

        for path in suite_path_list:
            with open(path, 'w') as file_handle:
                file_handle.write('test file')

        # Build the suite runner instances
        return self.factory.build_runner(
            suite_path_list, browser_names,
            xunit_path, coverage_xml_path,
            coverage_html_path, timeout_sec
        )