Repository: PyCQA/docformatter
Pull request #110 (2022-08-19 16:12:24)

refactor: move functions to classes
This PR moves related functions into the Formator class and updates associated tests.

coveralls: ## Pull Request Test Coverage Report for [Build 2890723008](https://coveralls.io/builds/51801189)

* **0** of **0**   changed or added relevant lines in **0** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage remained the same at **0.0%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/51801189/badge)](https://coveralls.io/builds/51801189) |
| :-- | --: |
| Change from base [Build 2890652053](https://coveralls.io/builds/51800866): |  0.0% |
| Covered Lines: | 0 |
| Relevant Lines: | 0 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/.github/workflows/do-update-authors.yml b/.github/workflows/do-update-authors.yml
index 274d090..d20c970 100644
--- a/.github/workflows/do-update-authors.yml
+++ b/.github/workflows/do-update-authors.yml
@@ -40,6 +40,7 @@ jobs:
               "pre-commit-ci",
               "github-action",
               "GitHub Actions",
+              "Sourcery AI",
           )
           authors = [
               author
@@ -51,9 +52,7 @@ jobs:
           file_head = (
               ".. This file is automatically generated/updated by a github actions workflow.\n"
               ".. Every manual change will be overwritten on push to main.\n"
-              ".. You can find it here: ``.github/workflows/update-authors.yaml``\n"
-              ".. For more information see "
-              "`https://github.com/rstcheck/rstcheck/graphs/contributors`\n\n"
+              ".. You can find it here: ``.github/workflows/do-update-authors.yaml``\n\n"
               "Author\n"
               "------\n"
               "Steven Myint <git@stevenmyint.com>\n\n"
diff --git a/AUTHORS.rst b/AUTHORS.rst
index 05273ba..0c5b2fd 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -27,7 +27,7 @@ Additional contributions by (sorted by name)
 - Paul Angerer <48882462+etimoz@users.noreply.github.com>
 - Peter Boothe <pboothe@pboothe2.nyc.corp.google.com>
 - Sho Iwamoto <sho.iwamoto@pd.infn.it>
-- Sourcery AI <>
 - Swen Kooij <swenkooij@gmail.com>
 - happlebao <c.x.bao@student.ucc.ie>
 - serhiy-yevtushenko <syevtushenko@gmail.com>
+
diff --git a/docformatter.py b/docformatter.py
index 2e7ee98..0fcfffc 100755
--- a/docformatter.py
+++ b/docformatter.py
@@ -46,7 +46,7 @@ import sysconfig
 import textwrap
 import tokenize
 from configparser import ConfigParser
-from typing import List, Tuple, Union
+from typing import List, TextIO, Tuple, Union
 
 # Third Party Imports
 import untokenize
@@ -92,7 +92,7 @@ QUOTE_TYPES = STR_QUOTE_TYPES + RAW_QUOTE_TYPES + UCODE_QUOTE_TYPES
 _PYTHON_LIBS = set(sysconfig.get_paths().values())
 
 
-class FormatResult(object):
+class FormatResult:
     """Possible exit codes."""
 
     ok = 0
@@ -201,13 +201,13 @@ class Configurator:
             "result in a mess (default: %(default)s)",
         )
         self.parser.add_argument(
-            "--tab_width",
+            "--tab-width",
             type=int,
             dest="tab_width",
             metavar="width",
             default=int(self.flargs_dct.get("tab-width", 1)),
             help="tabs in indentation are this many characters when "
-                 "wrapping lines (default: %(default)s)",
+            "wrapping lines (default: %(default)s)",
         )
         self.parser.add_argument(
             "--blank",
@@ -351,6 +351,368 @@ class Configurator:
                 }
 
 
+class Formator:
+    """Format docstrings."""
+
+    parser = None
+    """Parser object."""
+
+    args: argparse.Namespace = None
+
+    def __init__(
+        self,
+            args: argparse.Namespace,
+            stderror: TextIO,
+            stdin: TextIO,
+            stdout: TextIO,
+    ) -> None:
+        """Initialize a Formattor instance.
+
+        Parameters
+        ----------
+        args : argparse.Namespace
+            Any command line arguments passed during invocation or
+            configuration file options.
+        stderror : TextIO
+            The standard error device.  Typically, the screen.
+        stdin :  TextIO
+            The standard input device.  Typically, the keyboard.
+        stdout : TextIO
+            The standard output device.  Typically, the screen.
+
+        Returns
+        -------
+        object
+        """
+        self.args = args
+        self.stderror: TextIO = stderror
+        self.stdin: TextIO = stdin
+        self.stdout: TextIO = stdout
+
+    def do_format_standard_in(self, parser: argparse.ArgumentParser):
+        """Print formatted text to standard out.
+
+        Parameters
+        ----------
+        parser: argparse.ArgumentParser
+            The argument parser containing the formatting options.
+        """
+        if len(self.args.files) > 1:
+            parser.error("cannot mix standard in and regular files")
+
+        if self.args.in_place:
+            parser.error("--in-place cannot be used with standard input")
+
+        if self.args.recursive:
+            parser.error("--recursive cannot be used with standard input")
+
+        encoding = None
+        source = self.stdin.read()
+        if not isinstance(source, unicode):
+            encoding = self.stdin.encoding or _get_encoding()
+            source = source.decode(encoding)
+
+        formatted_source = self._do_format_code(source)
+
+        if encoding:
+            formatted_source = formatted_source.encode(encoding)
+
+        self.stdout.write(formatted_source)
+
+    def do_format_files(self):
+        """Format multiple files.
+
+        Return
+        ------
+        code: int
+            One of the FormatResult codes.
+        """
+        outcomes = collections.Counter()
+        for filename in find_py_files(
+            set(self.args.files), self.args.recursive, self.args.exclude
+        ):
+            try:
+                result = self._do_format_file(filename)
+                outcomes[result] += 1
+                if result == FormatResult.check_failed:
+                    print(unicode(filename), file=self.stderror)
+            except IOError as exception:
+                outcomes[FormatResult.error] += 1
+                print(unicode(exception), file=self.stderror)
+
+        return_codes = [  # in order of preference
+            FormatResult.error,
+            FormatResult.check_failed,
+            FormatResult.ok,
+        ]
+
+        for code in return_codes:
+            if outcomes[code]:
+                return code
+
+    def _do_format_file(self, filename):
+        """Run format_code() on a file.
+
+        Parameters
+        ----------
+        filename: str
+            The path to the file to be formatted.
+
+        Return
+        ------
+        code: int
+            One of the FormatResult codes.
+        """
+        encoding = detect_encoding(filename)
+        with open_with_encoding(filename, encoding=encoding) as input_file:
+            source = input_file.read()
+            formatted_source = self._do_format_code(source)
+
+        if source != formatted_source:
+            if self.args.check:
+                return FormatResult.check_failed
+            elif self.args.in_place:
+                with open_with_encoding(
+                    filename, mode="w", encoding=encoding
+                ) as output_file:
+                    output_file.write(formatted_source)
+            else:
+                # Standard Library Imports
+                import difflib
+
+                diff = difflib.unified_diff(
+                    source.splitlines(),
+                    formatted_source.splitlines(),
+                    f"before/{filename}",
+                    f"after/{filename}",
+                    lineterm="",
+                )
+                self.stdout.write("\n".join(list(diff) + [""]))
+
+        return FormatResult.ok
+
+    def _do_format_code(self, source):
+        """Return source code with docstrings formatted.
+
+        Parameters
+        ----------
+        source: str
+            The text from the source file.
+        """
+        try:
+            original_newline = find_newline(source.splitlines(True))
+            code = self._format_code(source)
+
+            return normalize_line_endings(
+                code.splitlines(True), original_newline
+            )
+        except (tokenize.TokenError, IndentationError):
+            return source
+
+    def _format_code(
+        self,
+        source,
+    ):
+        """Return source code with docstrings formatted.
+
+        Parameters
+        ----------
+        source: str
+            The source code string.
+
+        Returns
+        -------
+        formatted_source: str
+            The source code with formatted docstrings.
+        """
+        if not source:
+            return source
+
+        if self.args.line_range is not None:
+            assert self.args.line_range[0] > 0 and self.args.line_range[1] > 0
+
+        if self.args.length_range is not None:
+            assert (
+                self.args.length_range[0] > 0 and self.args.length_range[1] > 0
+            )
+
+        modified_tokens = []
+
+        sio = io.StringIO(source)
+        previous_token_string = ""
+        previous_token_type = None
+        only_comments_so_far = True
+
+        try:
+            for (
+                token_type,
+                token_string,
+                start,
+                end,
+                line,
+            ) in tokenize.generate_tokens(sio.readline):
+                if (
+                    token_type == tokenize.STRING
+                    and token_string.startswith(QUOTE_TYPES)
+                    and (
+                        previous_token_type == tokenize.INDENT
+                        or only_comments_so_far
+                    )
+                    and is_in_range(self.args.line_range, start[0], end[0])
+                    and has_correct_length(
+                        self.args.length_range, start[0], end[0]
+                    )
+                ):
+                    indentation = (
+                        "" if only_comments_so_far else previous_token_string
+                    )
+                    token_string = self._do_format_docstring(
+                        indentation,
+                        token_string,
+                    )
+
+                if token_type not in [
+                    tokenize.COMMENT,
+                    tokenize.NEWLINE,
+                    tokenize.NL,
+                ]:
+                    only_comments_so_far = False
+
+                previous_token_string = token_string
+                previous_token_type = token_type
+
+                # If the current token is a newline, the previous token was a
+                # newline or a comment, and these two sequential newlines
+                # follow a function definition, ignore the blank line.
+                if (
+                    len(modified_tokens) <= 2
+                    or token_type not in {tokenize.NL, tokenize.NEWLINE}
+                    or modified_tokens[-1][0]
+                    not in {tokenize.NL, tokenize.NEWLINE}
+                    or modified_tokens[-2][1] != ":"
+                    and modified_tokens[-2][0] != tokenize.COMMENT
+                    or modified_tokens[-2][4][:3] != "def"
+                ):
+                    modified_tokens.append(
+                        (token_type, token_string, start, end, line)
+                    )
+
+            return untokenize.untokenize(modified_tokens)
+        except tokenize.TokenError:
+            return source
+
+    def _do_format_docstring(
+        self,
+        indentation: str,
+        docstring: str,
+    ) -> str:
+        """Return formatted version of docstring.
+
+        Parameters
+        ----------
+        indentation: str
+            The indentation characters for the docstring.
+        docstring: str
+            The docstring itself.
+
+        Returns
+        -------
+        docstring_formatted: str
+            The docstring formatted according the various options.
+        """
+        contents, open_quote = strip_docstring(docstring)
+        open_quote = (
+            f"{open_quote} " if self.args.pre_summary_space else open_quote
+        )
+
+        # Skip if there are nested triple double quotes
+        if contents.count(QUOTE_TYPES[0]):
+            return docstring
+
+        # Do not modify things that start with doctests.
+        if contents.lstrip().startswith(">>>"):
+            return docstring
+
+        summary, description = split_summary_and_description(contents)
+
+        # Leave docstrings with underlined summaries alone.
+        if remove_section_header(description).strip() != description.strip():
+            return docstring
+
+        if not self.args.force_wrap and is_some_sort_of_list(
+            summary,
+            self.args.non_strict,
+        ):
+            # Something is probably not right with the splitting.
+            return docstring
+
+        # Compensate for textwrap counting each tab in indentation as 1
+        # character.
+        tab_compensation = indentation.count("\t") * (self.args.tab_width - 1)
+        self.args.wrap_summaries -= tab_compensation
+        self.args.wrap_descriptions -= tab_compensation
+
+        if description:
+            # Compensate for triple quotes by temporarily prepending 3 spaces.
+            # This temporary prepending is undone below.
+            initial_indent = (
+                indentation
+                if self.args.pre_summary_newline
+                else 3 * " " + indentation
+            )
+            pre_summary = (
+                "\n" + indentation if self.args.pre_summary_newline else ""
+            )
+            summary = wrap_summary(
+                normalize_summary(summary),
+                wrap_length=self.args.wrap_summaries,
+                initial_indent=initial_indent,
+                subsequent_indent=indentation,
+            ).lstrip()
+            description = wrap_description(
+                description,
+                indentation=indentation,
+                wrap_length=self.args.wrap_descriptions,
+                force_wrap=self.args.force_wrap,
+                strict=self.args.non_strict,
+            )
+            post_description = "\n" if self.args.post_description_blank else ""
+            return f'''\
+{open_quote}{pre_summary}{summary}
+
+{description}{post_description}
+{indentation}"""\
+'''
+        else:
+            if not self.args.make_summary_multi_line:
+                summary_wrapped = wrap_summary(
+                    open_quote + normalize_summary(contents) + '"""',
+                    wrap_length=self.args.wrap_summaries,
+                    initial_indent=indentation,
+                    subsequent_indent=indentation,
+                ).strip()
+                if (
+                    self.args.close_quotes_on_newline
+                    and "\n" in summary_wrapped
+                ):
+                    summary_wrapped = (
+                        f"{summary_wrapped[:-3]}"
+                        f"\n{indentation}"
+                        f"{summary_wrapped[-3:]}"
+                    )
+                return summary_wrapped
+            else:
+                beginning = f"{open_quote}\n{indentation}"
+                ending = f'\n{indentation}"""'
+                summary_wrapped = wrap_summary(
+                    normalize_summary(contents),
+                    wrap_length=self.args.wrap_summaries,
+                    initial_indent=indentation,
+                    subsequent_indent=indentation,
+                ).strip()
+                return f"{beginning}{summary_wrapped}{ending}"
+
+
 def has_correct_length(length_range, start, end):
     """Return True if docstring's length is in range."""
     if length_range is None:
@@ -402,219 +764,6 @@ def _find_shortest_indentation(lines):
     return indentation or ""
 
 
-def format_code(source, **kwargs):
-    """Return source code with docstrings formatted.
-
-    Wrap summary lines if summary_wrap_length is greater than 0.
-
-    See "_format_code()" for parameters.
-    """
-    try:
-        original_newline = find_newline(source.splitlines(True))
-        code = _format_code(source, **kwargs)
-
-        return normalize_line_endings(code.splitlines(True), original_newline)
-    except (tokenize.TokenError, IndentationError):
-        return source
-
-
-def _format_code(
-    source,
-    summary_wrap_length=79,
-    description_wrap_length=72,
-    force_wrap=False,
-    tab_width=1,
-    pre_summary_newline=False,
-    pre_summary_space=False,
-    make_summary_multi_line=False,
-    close_quotes_on_newline=False,
-    post_description_blank=False,
-
-    line_range=None,
-    length_range=None,
-    strict=True,
-):
-    """Return source code with docstrings formatted."""
-    if not source:
-        return source
-
-    if line_range is not None:
-        assert line_range[0] > 0 and line_range[1] > 0
-
-    if length_range is not None:
-        assert length_range[0] > 0 and length_range[1] > 0
-
-    modified_tokens = []
-
-    sio = io.StringIO(source)
-    previous_token_string = ""
-    previous_token_type = None
-    only_comments_so_far = True
-
-    for (
-        token_type,
-        token_string,
-        start,
-        end,
-        line,
-    ) in tokenize.generate_tokens(sio.readline):
-        if (
-            token_type == tokenize.STRING
-            and token_string.startswith(QUOTE_TYPES)
-            and (
-                previous_token_type == tokenize.INDENT or only_comments_so_far
-            )
-            and is_in_range(line_range, start[0], end[0])
-            and has_correct_length(length_range, start[0], end[0])
-        ):
-            indentation = "" if only_comments_so_far else previous_token_string
-
-            token_string = format_docstring(
-                indentation,
-                token_string,
-                summary_wrap_length=summary_wrap_length,
-                description_wrap_length=description_wrap_length,
-                force_wrap=force_wrap,
-                tab_width=tab_width,
-                pre_summary_newline=pre_summary_newline,
-                pre_summary_space=pre_summary_space,
-                make_summary_multi_line=make_summary_multi_line,
-                close_quotes_on_newline=close_quotes_on_newline,
-                post_description_blank=post_description_blank,
-                strict=strict,
-            )
-
-        if token_type not in [tokenize.COMMENT, tokenize.NEWLINE, tokenize.NL]:
-            only_comments_so_far = False
-
-        previous_token_string = token_string
-        previous_token_type = token_type
-
-        # If the current token is a newline, the previous token was a
-        # newline or a comment, and these two sequential newlines follow a
-        # function definition, ignore the blank line.
-        if (
-            len(modified_tokens) <= 2
-            or token_type not in {tokenize.NL, tokenize.NEWLINE}
-            or modified_tokens[-1][0] not in {tokenize.NL, tokenize.NEWLINE}
-            or modified_tokens[-2][1] != ":"
-            and modified_tokens[-2][0] != tokenize.COMMENT
-            or modified_tokens[-2][4][:3] != "def"
-        ):
-            modified_tokens.append(
-                (token_type, token_string, start, end, line)
-            )
-
-    return untokenize.untokenize(modified_tokens)
-
-
-def format_docstring(
-    indentation,
-    docstring,
-    summary_wrap_length=0,
-    description_wrap_length=0,
-    force_wrap=False,
-    tab_width=1,
-    pre_summary_newline=False,
-    pre_summary_space=False,
-    make_summary_multi_line=False,
-    close_quotes_on_newline=False,
-    post_description_blank=False,
-    strict=True,
-):
-    """Return formatted version of docstring.
-
-    Wrap summary lines if summary_wrap_length is greater than 0.
-
-    Relevant parts of PEP 257:
-        - For consistency, always use triple double quotes around docstrings.
-        - Triple quotes are used even though the string fits on one line.
-        - Multi-line docstrings consist of a summary line just like a one-line
-          docstring, followed by a blank line, followed by a more elaborate
-          description.
-        - Unless the entire docstring fits on a line, place the closing quotes
-          on a line by themselves.
-    """
-    contents, open_quote = strip_docstring(docstring)
-    open_quote = f"{open_quote} " if pre_summary_space else open_quote
-
-    # Skip if there are nested triple double quotes
-    if contents.count(QUOTE_TYPES[0]):
-        return docstring
-
-    # Do not modify things that start with doctests.
-    if contents.lstrip().startswith(">>>"):
-        return docstring
-
-    summary, description = split_summary_and_description(contents)
-
-    # Leave docstrings with underlined summaries alone.
-    if remove_section_header(description).strip() != description.strip():
-        return docstring
-
-    if not force_wrap and is_some_sort_of_list(summary, strict):
-        # Something is probably not right with the splitting.
-        return docstring
-
-    # Compensate for textwrap counting each tab in indentation as 1 character.
-    tab_compensation = indentation.count('\t') * (tab_width - 1)
-    summary_wrap_length -= tab_compensation
-    description_wrap_length -= tab_compensation
-
-    if description:
-        # Compensate for triple quotes by temporarily prepending 3 spaces.
-        # This temporary prepending is undone below.
-        initial_indent = (
-            indentation if pre_summary_newline else 3 * " " + indentation
-        )
-        pre_summary = "\n" + indentation if pre_summary_newline else ""
-        summary = wrap_summary(
-            normalize_summary(summary),
-            wrap_length=summary_wrap_length,
-            initial_indent=initial_indent,
-            subsequent_indent=indentation,
-        ).lstrip()
-        description = wrap_description(
-            description,
-            indentation=indentation,
-            wrap_length=description_wrap_length,
-            force_wrap=force_wrap,
-            strict=strict,
-        )
-        post_description = "\n" if post_description_blank else ""
-        return f'''\
-{open_quote}{pre_summary}{summary}
-
-{description}{post_description}
-{indentation}"""\
-'''
-    else:
-        if not make_summary_multi_line:
-            summary_wrapped = wrap_summary(
-                open_quote + normalize_summary(contents) + '"""',
-                wrap_length=summary_wrap_length,
-                initial_indent=indentation,
-                subsequent_indent=indentation,
-            ).strip()
-            if close_quotes_on_newline and "\n" in summary_wrapped:
-                summary_wrapped = (
-                    f"{summary_wrapped[:-3]}"
-                    f"\n{indentation}"
-                    f"{summary_wrapped[-3:]}"
-                )
-            return summary_wrapped
-        else:
-            beginning = f"{open_quote}\n{indentation}"
-            ending = f'\n{indentation}"""'
-            summary_wrapped = wrap_summary(
-                normalize_summary(contents),
-                wrap_length=summary_wrap_length,
-                initial_indent=indentation,
-                subsequent_indent=indentation,
-            ).strip()
-            return f"{beginning}{summary_wrapped}{ending}"
-
-
 def is_probably_beginning_of_sentence(line):
     """Return True if this line begins a new sentence."""
     # Check heuristically for a parameter list.
@@ -949,101 +1098,24 @@ def detect_encoding(filename):
         return "latin-1"
 
 
-def format_file(filename, args, standard_out):
-    """Run format_code() on a file.
-
-    Return: one of the FormatResult codes.
-    """
-    encoding = detect_encoding(filename)
-    with open_with_encoding(filename, encoding=encoding) as input_file:
-        source = input_file.read()
-        formatted_source = _format_code_with_args(source, args)
-
-    if source != formatted_source:
-        if args.check:
-            return FormatResult.check_failed
-        elif args.in_place:
-            with open_with_encoding(
-                filename, mode="w", encoding=encoding
-            ) as output_file:
-                output_file.write(formatted_source)
-        else:
-            # Standard Library Imports
-            import difflib
-
-            diff = difflib.unified_diff(
-                source.splitlines(),
-                formatted_source.splitlines(),
-                f"before/{filename}",
-                f"after/{filename}",
-                lineterm="",
-            )
-            standard_out.write("\n".join(list(diff) + [""]))
-
-    return FormatResult.ok
-
-
-def _format_code_with_args(source, args):
-    """Run format_code with parsed command-line arguments."""
-    return format_code(
-        source,
-        summary_wrap_length=args.wrap_summaries,
-        description_wrap_length=args.wrap_descriptions,
-        force_wrap=args.force_wrap,
-        tab_width=args.tab_width,
-        pre_summary_newline=args.pre_summary_newline,
-        pre_summary_space=args.pre_summary_space,
-        make_summary_multi_line=args.make_summary_multi_line,
-        close_quotes_on_newline=args.close_quotes_on_newline,
-        post_description_blank=args.post_description_blank,
-        line_range=args.line_range,
-        strict=not args.non_strict,
-    )
-
-
 def _main(argv, standard_out, standard_error, standard_in):
     """Run internal main entry point."""
     configurator = Configurator(argv)
     configurator.do_parse_arguments()
 
+    formator = Formator(
+        configurator.args,
+        stderror=standard_error,
+        stdin=standard_in,
+        stdout=standard_out,
+    )
+
     if "-" in configurator.args.files:
-        _format_standard_in(
-            configurator.args,
-            parser=configurator.parser,
-            standard_out=standard_out,
-            standard_in=standard_in,
+        formator.do_format_standard_in(
+            configurator.parser,
         )
     else:
-        return _format_files(
-            configurator.args,
-            standard_out=standard_out,
-            standard_error=standard_error,
-        )
-
-
-def _format_standard_in(args, parser, standard_out, standard_in):
-    """Print formatted text to standard out."""
-    if len(args.files) > 1:
-        parser.error("cannot mix standard in and regular files")
-
-    if args.in_place:
-        parser.error("--in-place cannot be used with standard input")
-
-    if args.recursive:
-        parser.error("--recursive cannot be used with standard input")
-
-    encoding = None
-    source = standard_in.read()
-    if not isinstance(source, unicode):
-        encoding = standard_in.encoding or _get_encoding()
-        source = source.decode(encoding)
-
-    formatted_source = _format_code_with_args(source, args=args)
-
-    if encoding:
-        formatted_source = formatted_source.encode(encoding)
-
-    standard_out.write(formatted_source)
+        return formator.do_format_files()
 
 
 def _get_encoding():
@@ -1104,37 +1176,6 @@ def find_py_files(sources, recursive, exclude=None):
             yield name
 
 
-def _format_files(args, standard_out, standard_error):
-    """Format multiple files.
-
-    Return: one of the FormatResult codes.
-    """
-    outcomes = collections.Counter()
-    for filename in find_py_files(
-        set(args.files), args.recursive, args.exclude
-    ):
-        try:
-            result = format_file(
-                filename, args=args, standard_out=standard_out
-            )
-            outcomes[result] += 1
-            if result == FormatResult.check_failed:
-                print(unicode(filename), file=standard_error)
-        except IOError as exception:
-            outcomes[FormatResult.error] += 1
-            print(unicode(exception), file=standard_error)
-
-    return_codes = [  # in order of preference
-        FormatResult.error,
-        FormatResult.check_failed,
-        FormatResult.ok,
-    ]
-
-    for code in return_codes:
-        if outcomes[code]:
-            return code
-
-
 def main():
     """Run main entry point."""
     # SIGPIPE is not available on Windows.
diff --git a/pyproject.toml b/pyproject.toml
index caeda9b..ec068f7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -182,6 +182,7 @@ deps =
 setenv =
     COVERAGE_FILE={toxinidir}/.coverage
 commands =
+    pip install -U pip
     pip install .[tomli]
     pytest -s -x -c ./pyproject.toml --cache-clear \
         --cov-config=pyproject.toml --cov=docformatter \


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index d2b1c90..c31d6e1 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -27,6 +27,7 @@
 """docformatter test suite configuration file."""
 
 # Standard Library Imports
+import argparse
 import os
 import shutil
 import subprocess
@@ -100,3 +101,127 @@ def run_docformatter(arguments, temporary_file):
         stdin=subprocess.PIPE,
         env=environ,
     )
+
+
+@pytest.fixture(scope="function")
+def test_args(args):
+    """Create a set of arguments to use with tests.
+
+    To pass no arguments, just an empty file name:
+        @pytest.mark.parametrize("args", [[""]])
+
+    To pass an argument AND empty file name:
+        @pytest.mark.parametrize("args", [["--wrap-summaries", "79", ""]])
+    """
+    parser = argparse.ArgumentParser(
+        description="parser object for docformatter tests",
+        prog="docformatter",
+    )
+
+    changes = parser.add_mutually_exclusive_group()
+    changes.add_argument(
+        "-i",
+        "--in-place",
+        action="store_true",
+    )
+    changes.add_argument(
+        "-c",
+        "--check",
+        action="store_true",
+    )
+    parser.add_argument(
+        "-r",
+        "--recursive",
+        action="store_true",
+        default=False,
+    )
+    parser.add_argument(
+        "-e",
+        "--exclude",
+        nargs="*",
+    )
+    parser.add_argument(
+        "--wrap-summaries",
+        default=79,
+        type=int,
+        metavar="length",
+    )
+    parser.add_argument(
+        "--wrap-descriptions",
+        default=72,
+        type=int,
+        metavar="length",
+    )
+    parser.add_argument(
+        "--force-wrap",
+        action="store_true",
+        default=False,
+    )
+    parser.add_argument(
+        "--tab-width",
+        type=int,
+        dest="tab_width",
+        metavar="width",
+        default=1,
+    )
+    parser.add_argument(
+        "--blank",
+        dest="post_description_blank",
+        action="store_true",
+        default=False,
+    )
+    parser.add_argument(
+        "--pre-summary-newline",
+        action="store_true",
+        default=False,
+    )
+    parser.add_argument(
+        "--pre-summary-space",
+        action="store_true",
+        default=False,
+    )
+    parser.add_argument(
+        "--make-summary-multi-line",
+        action="store_true",
+        default=False,
+    )
+    parser.add_argument(
+        "--close-quotes-on-newline",
+        action="store_true",
+        default=False,
+    )
+    parser.add_argument(
+        "--range",
+        metavar="line",
+        dest="line_range",
+        default=None,
+        type=int,
+        nargs=2,
+    )
+    parser.add_argument(
+        "--docstring-length",
+        metavar="length",
+        dest="length_range",
+        default=None,
+        type=int,
+        nargs=2,
+    )
+    parser.add_argument(
+        "--non-strict",
+        action="store_true",
+        default=False,
+    )
+    parser.add_argument(
+        "--config",
+    )
+    parser.add_argument(
+        "--version",
+        action="version",
+        version="test version",
+    )
+    parser.add_argument(
+        "files",
+        nargs="+",
+    )
+
+    yield parser.parse_args(args)
diff --git a/tests/test_format_code.py b/tests/test_format_code.py
index 706e35d..a830e32 100644
--- a/tests/test_format_code.py
+++ b/tests/test_format_code.py
@@ -24,91 +24,72 @@
 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
-"""Module for testing the format_code() function."""
+"""Module for testing the Formattor._do_format_code() function."""
+
+# Standard Library Imports
+import sys
 
 # Third Party Imports
 import pytest
 
 # docformatter Package Imports
 import docformatter
+from docformatter import Formator
 
 
 class TestFormatCode:
-    """Class for testing format_code() with no arguments."""
+    """Class for testing _format_code() with no arguments."""
 
     @pytest.mark.unit
-    def test_format_code(self):
-        """Should place one-liner on single line."""
-        assert '''\
-def foo():
-    """Hello foo."""
-''' == docformatter.format_code(
-            '''\
-def foo():
-    """
-    Hello foo.
-    """
-'''
-        )
-
-    @pytest.mark.unit
-    def test_format_code_with_module_docstring(self):
-        """Should format module docstrings."""
-        assert '''\
-#!/usr/env/bin python
-"""This is a module docstring.
-
-1. One
-2. Two
-"""
-
-"""But
-this
-is
-not."""
-''' == docformatter.format_code(
-            '''\
-#!/usr/env/bin python
-"""This is
-a module
-docstring.
-
-1. One
-2. Two
-"""
-
-"""But
-this
-is
-not."""
-'''
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_should_ignore_non_docstring(self, test_args, args):
+        """Should ignore triple quoted strings that are assigned values."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
         )
 
-    @pytest.mark.unit
-    def test_format_code_should_ignore_non_docstring(self):
-        """Shoud ignore triple quoted strings that are assigned values."""
         source = '''\
 x = """This
 is
-not."""
+not a
+docstring."""
 '''
-        assert source == docformatter.format_code(source)
+        assert source == uut._format_code(source)
 
     @pytest.mark.unit
-    def test_format_code_with_empty_string(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_empty_string(self, test_args, args):
         """Should do nothing with an empty string."""
-        assert "" == docformatter.format_code("")
-        assert "" == docformatter.format_code("")
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        assert "" == uut._format_code("")
+        assert "" == uut._format_code("")
 
     @pytest.mark.unit
-    def test_format_code_with_tabs(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_tabs(self, test_args, args):
         """Should retain tabbed indentation."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
 \t"""Hello foo."""
 \tif True:
 \t\tx = 1
-''' == docformatter.format_code(
+''' == uut._format_code(
             '''\
 def foo():
 \t"""
@@ -120,14 +101,22 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_with_mixed_tabs(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_mixed_tabs(self, test_args, args):
         """Should retain mixed tabbed and spaced indentation."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
 \t"""Hello foo."""
 \tif True:
 \t    x = 1
-''' == docformatter.format_code(
+''' == uut._format_code(
             '''\
 def foo():
 \t"""
@@ -139,13 +128,21 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_with_escaped_newlines(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_escaped_newlines(self, test_args, args):
         """Should leave escaped newlines in code untouched."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert r'''def foo():
     """Hello foo."""
     x = \
             1
-''' == docformatter.format_code(
+''' == uut._format_code(
             r'''def foo():
     """
     Hello foo.
@@ -156,15 +153,23 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_with_comments(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_comments(self, test_args, args):
         """Should leave comments as is."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert r'''
 def foo():
     """Hello foo."""
     # My comment
     # My comment with escape \
     123
-'''.lstrip() == docformatter.format_code(
+'''.lstrip() == uut._format_code(
             r'''
 def foo():
     """
@@ -177,13 +182,23 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_with_escaped_newline_in_inline_comment(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_escaped_newline_in_inline_comment(
+        self, test_args, args
+    ):
         """Should leave code with inline comment as is."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert r'''
 def foo():
     """Hello foo."""
     def test_method_no_chr_92(): the501(92) # \
-'''.lstrip() == docformatter.format_code(
+'''.lstrip() == uut._format_code(
             r'''
 def foo():
     """
@@ -194,16 +209,24 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_raw_docstring_double_quotes(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_raw_docstring_double_quotes(self, test_args, args):
         """Should format raw docstrings with triple double quotes.
 
         See requirement PEP_257_2.  See issue #54 for request to handle
         raw docstrings.
         """
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
     r"""Hello raw foo."""
-''' == docformatter.format_code(
+''' == uut._format_code(
             '''\
 def foo():
     r"""
@@ -215,7 +238,7 @@ def foo():
         assert '''\
 def foo():
     R"""Hello Raw foo."""
-''' == docformatter.format_code(
+''' == uut._format_code(
             '''\
 def foo():
     R"""
@@ -225,16 +248,24 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_raw_docstring_single_quotes(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_raw_docstring_single_quotes(self, test_args, args):
         """Should format raw docstrings with triple single quotes.
 
         See requirement PEP_257_2.  See issue #54 for request to handle
         raw docstrings.
         """
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
     r"""Hello raw foo."""
-''' == docformatter.format_code(
+''' == uut._format_code(
             """\
 def foo():
     r'''
@@ -246,7 +277,7 @@ def foo():
         assert '''\
 def foo():
     R"""Hello Raw foo."""
-''' == docformatter.format_code(
+''' == uut._format_code(
             """\
 def foo():
     R'''
@@ -256,16 +287,26 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_unicode_docstring_double_quotes(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_unicode_docstring_double_quotes(
+        self, test_args, args
+    ):
         """Should format unicode docstrings with triple double quotes.
 
         See requirement PEP_257_3.  See issue #54 for request to handle
         raw docstrings.
         """
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
     u"""Hello unicode foo."""
-''' == docformatter.format_code(
+''' == uut._format_code(
             '''\
 def foo():
     u"""
@@ -277,7 +318,7 @@ def foo():
         assert '''\
 def foo():
     U"""Hello Unicode foo."""
-''' == docformatter.format_code(
+''' == uut._format_code(
             '''\
 def foo():
     U"""
@@ -287,16 +328,26 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_unicode_docstring_single_quotes(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_unicode_docstring_single_quotes(
+        self, test_args, args
+    ):
         """Should format unicode docstrings with triple single quotes.
 
         See requirement PEP_257_3.  See issue #54 for request to handle
         raw docstrings.
         """
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
     u"""Hello unicode foo."""
-''' == docformatter.format_code(
+''' == uut._format_code(
             """\
 def foo():
     u'''
@@ -308,7 +359,7 @@ def foo():
         assert '''\
 def foo():
     U"""Hello Unicode foo."""
-''' == docformatter.format_code(
+''' == uut._format_code(
             """\
 def foo():
     U'''
@@ -318,25 +369,41 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_skip_nested(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_skip_nested(self, test_args, args):
         """Should ignore nested triple quotes."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         code = """\
 def foo():
     '''Hello foo. \"\"\"abc\"\"\"
     '''
 """
-        assert code == docformatter.format_code(code)
+        assert code == uut._format_code(code)
 
     @pytest.mark.unit
-    def test_format_code_with_multiple_sentences(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_multiple_sentences(self, test_args, args):
         """Should create multi-line docstring from multiple sentences."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
     """Hello foo.
 
     This is a docstring.
     """
-''' == docformatter.format_code(
+''' == uut._format_code(
             '''\
 def foo():
     """
@@ -347,15 +414,25 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_with_multiple_sentences_same_line(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_multiple_sentences_same_line(
+        self, test_args, args
+    ):
         """Should create multi-line docstring from multiple sentences."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
     """Hello foo.
 
     This is a docstring.
     """
-''' == docformatter.format_code(
+''' == uut._format_code(
             '''\
 def foo():
     """
@@ -365,15 +442,25 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_with_multiple_sentences_multi_line_summary(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_multiple_sentences_multi_line_summary(
+        self, test_args, args
+    ):
         """Should put summary line on a single line."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
     """Hello foo.
 
     This is a docstring.
     """
-''' == docformatter.format_code(
+''' == uut._format_code(
             '''\
 def foo():
     """
@@ -384,15 +471,23 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_with_empty_lines(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_empty_lines(self, test_args, args):
         """Summary line on one line when wrapped, followed by empty line."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
     """Hello foo and this is a docstring.
 
     More stuff.
     """
-''' == docformatter.format_code(
+''' == uut._format_code(
             '''\
 def foo():
     """
@@ -405,15 +500,23 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_with_trailing_whitespace(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_trailing_whitespace(self, test_args, args):
         """Should strip trailing whitespace."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
     """Hello foo and this is a docstring.
 
     More stuff.
     """
-''' == docformatter.format_code(
+''' == uut._format_code(
             (
                 '''\
 def foo():
@@ -428,8 +531,16 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_with_parameters_list(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_parameters_list(self, test_args, args):
         """Should treat parameters list as elaborate description."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
     """Test.
@@ -437,7 +548,7 @@ def foo():
     one - first
     two - second
     """
-''' == docformatter.format_code(
+''' == uut._format_code(
             (
                 '''\
 def foo():
@@ -450,16 +561,24 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_ignore_code_with_single_quote(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_ignore_code_with_single_quote(self, test_args, args):
         """Single single quote on first line of code should remain untouched.
 
         See requirement PEP_257_1.  See issue #66 for example of
         docformatter breaking code when encountering single quote.
         """
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert """\
 def foo():
     'Just a regular string'
-""" == docformatter.format_code(
+""" == uut._format_code(
             """\
 def foo():
     'Just a regular string'
@@ -467,16 +586,24 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_ignore_code_with_double_quote(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_ignore_code_with_double_quote(self, test_args, args):
         """Single double quotes on first line of code should remain untouched.
 
         See requirement PEP_257_1.  See issue #66 for example of
         docformatter breaking code when encountering single quote.
         """
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert """\
 def foo():
     "Just a regular string"
-""" == docformatter.format_code(
+""" == uut._format_code(
             """\
 def foo():
     "Just a regular string"
@@ -484,21 +611,39 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_should_skip_nested_triple_quotes(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_should_skip_nested_triple_quotes(
+        self, test_args, args
+    ):
         """Should ignore triple quotes nested in a string."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         line = '''\
 def foo():
     'Just a """foo""" string'
 '''
-        assert line == docformatter.format_code(line)
+        assert line == uut._format_code(line)
 
     @pytest.mark.unit
-    def test_format_code_with_assignment_on_first_line(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_assignment_on_first_line(self, test_args, args):
         """Should ignore triple quotes in variable assignment."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
     x = """Just a regular string. Alpha."""
-''' == docformatter.format_code(
+''' == uut._format_code(
             '''\
 def foo():
     x = """Just a regular string. Alpha."""
@@ -506,8 +651,16 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_with_regular_strings_too(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_regular_strings_too(self, test_args, args):
         """Should ignore triple quoted strings after the docstring."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def foo():
     """Hello foo and this is a docstring.
@@ -520,7 +673,7 @@ def foo():
     """More stuff
     that should not be
     touched\t"""
-''' == docformatter.format_code(
+''' == uut._format_code(
             '''\
 def foo():
     """
@@ -539,23 +692,59 @@ def foo():
         )
 
     @pytest.mark.unit
-    def test_format_code_with_syntax_error(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_syntax_error(self, test_args, args):
         """Should ignore single set of triple quotes followed by newline."""
-        assert '"""\n' == docformatter.format_code('"""\n')
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        assert '"""\n' == uut._format_code('"""\n')
 
     @pytest.mark.unit
-    def test_format_code_with_syntax_error_case_slash_r(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_syntax_error_case_slash_r(self, test_args, args):
         """Should ignore single set of triple quotes followed by return."""
-        assert '"""\r' == docformatter.format_code('"""\r')
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        assert '"""\r' == uut._format_code('"""\r')
 
     @pytest.mark.unit
-    def test_format_code_with_syntax_error_case_slash_r_slash_n(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_with_syntax_error_case_slash_r_slash_n(
+        self, test_args, args
+    ):
         """Should ignore single triple quote followed by return, newline."""
-        assert '"""\r\n' == docformatter.format_code('"""\r\n')
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        assert '"""\r\n' == uut._format_code('"""\r\n')
 
     @pytest.mark.unit
-    def test_format_code_dominant_line_ending_style_preserved(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_dominant_line_ending_style_preserved(
+        self, test_args, args
+    ):
         """Should retain carriage return line endings."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         goes_in = '''\
 def foo():\r
     """\r
@@ -572,29 +761,49 @@ def foo():\r
 \r
     This is a docstring.\r
     """\r
-''' == docformatter.format_code(
+''' == uut._do_format_code(
             goes_in
         )
 
     @pytest.mark.unit
-    def test_format_code_additional_empty_line_before_doc(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_additional_empty_line_before_doc(
+        self, test_args, args
+    ):
         """Should remove empty line between function def and docstring.
 
         See issue #51.
         """
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert (
             '\n\n\ndef my_func():\n"""Summary of my function."""\npass'
-            == docformatter._format_code(
+            == uut._do_format_code(
                 '\n\n\ndef my_func():\n\n"""Summary of my function."""\npass'
             )
         )
 
     @pytest.mark.unit
-    def test_format_code_extra_newline_following_comment(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_extra_newline_following_comment(
+        self, test_args, args
+    ):
         """Should remove extra newline following in-line comment.
 
         See issue #51.
         """
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         docstring = '''\
 def crash_rocket(location):    # pragma: no cover
 
@@ -604,53 +813,80 @@ def crash_rocket(location):    # pragma: no cover
         assert '''\
 def crash_rocket(location):    # pragma: no cover
     """This is a docstring following an in-line comment."""
-    return location''' == docformatter._format_code(
+    return location''' == uut._do_format_code(
             docstring
         )
 
     @pytest.mark.unit
-    def test_format_code_no_docstring(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_no_docstring(self, test_args, args):
         """Should leave code as is if there is no docstring.
 
         See issue #97.
         """
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         docstring = (
             "def pytest_addoption(parser: pytest.Parser) -> "
             "None:\n    register_toggle.pytest_addoption(parser)\n"
         )
-        assert docstring == docformatter._format_code(docstring)
+        assert docstring == uut._do_format_code(docstring)
 
         docstring = (
             "def pytest_addoption(parser: pytest.Parser) -> "
             "None:    # pragma: no cover\n    "
             "register_toggle.pytest_addoption(parser)\n"
         )
-        assert docstring == docformatter._format_code(docstring)
+        assert docstring == uut._do_format_code(docstring)
+
 
 class TestFormatCodeRanges:
-    """Class for testing format_code() with the line_range or
-    length_range arguments."""
+    """Class for testing _format_code() with the line_range or length_range
+    arguments."""
 
     @pytest.mark.unit
-    def test_format_code_range_miss(self):
+    @pytest.mark.parametrize("args", [["--range", "1", "1", ""]])
+    def test_format_code_range_miss(self, test_args, args):
         """Should leave docstrings outside line range as is."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
     def f(x):
         """  This is a docstring. That should be on more lines"""
         pass
     def g(x):
         """  Badly indented docstring"""
-        pass''' == docformatter.format_code('''\
+        pass''' == uut._format_code(
+            '''\
     def f(x):
         """  This is a docstring. That should be on more lines"""
         pass
     def g(x):
         """  Badly indented docstring"""
-        pass''', line_range=[1, 1])
+        pass'''
+        )
 
     @pytest.mark.unit
-    def test_format_code_range_hit(self):
+    @pytest.mark.parametrize("args", [["--range", "1", "2", ""]])
+    def test_format_code_range_hit(self, test_args, args):
         """Should format docstrings within line_range."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def f(x):
     """This is a docstring.
@@ -660,17 +896,27 @@ def f(x):
     pass
 def g(x):
     """  Badly indented docstring"""
-    pass''' == docformatter.format_code('''\
+    pass''' == uut._format_code(
+            '''\
 def f(x):
     """  This is a docstring. That should be on more lines"""
     pass
 def g(x):
     """  Badly indented docstring"""
-    pass''', line_range=[1, 2])
+    pass'''
+        )
 
     @pytest.mark.unit
-    def test_format_code_docstring_length(self):
+    @pytest.mark.parametrize("args", [["--docstring-length", "1", "1", ""]])
+    def test_format_code_docstring_length(self, test_args, args):
         """Should leave docstrings outside length_range as is."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''\
 def f(x):
     """This is a docstring.
@@ -681,7 +927,8 @@ def f(x):
     pass
 def g(x):
     """Badly indented docstring."""
-    pass''' == docformatter.format_code('''\
+    pass''' == uut._format_code(
+            '''\
 def f(x):
     """This is a docstring.
 
@@ -691,4 +938,73 @@ def f(x):
     pass
 def g(x):
     """  Badly indented docstring"""
-    pass''', length_range=[1, 1])
+    pass'''
+        )
+
+
+class TestDoFormatCode:
+    """Class for testing _do_format_code() with no arguments."""
+
+    @pytest.mark.unit
+    @pytest.mark.parametrize("args", [[""]])
+    def test_do_format_code(self, test_args, args):
+        """Should place one-liner on single line."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        assert '''\
+def foo():
+    """Hello foo."""
+''' == uut._do_format_code(
+            '''\
+def foo():
+    """
+    Hello foo.
+    """
+'''
+        )
+
+    @pytest.mark.unit
+    @pytest.mark.parametrize("args", [[""]])
+    def test_do_format_code_with_module_docstring(self, test_args, args):
+        """Should format module docstrings."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        assert '''\
+#!/usr/env/bin python
+"""This is a module docstring.
+
+1. One
+2. Two
+"""
+
+"""But
+this
+is
+not."""
+''' == uut._do_format_code(
+            '''\
+#!/usr/env/bin python
+"""This is
+a module
+docstring.
+
+1. One
+2. Two
+"""
+
+"""But
+this
+is
+not."""
+'''
+        )
diff --git a/tests/test_format_docstring.py b/tests/test_format_docstring.py
index f5394e0..5bd49e4 100644
--- a/tests/test_format_docstring.py
+++ b/tests/test_format_docstring.py
@@ -30,25 +30,37 @@
 # Standard Library Imports
 import itertools
 import random
+import sys
 
 # Third Party Imports
 import pytest
 
 # docformatter Package Imports
 import docformatter
+from docformatter import Formator
 
 # docformatter Local Imports
 from . import generate_random_docstring
 
+INDENTATION = "    "
+
 
 class TestFormatDocstring:
-    """Class for testing format_docstring() with no arguments."""
+    """Class for testing _do_format_docstring() with no arguments."""
 
     @pytest.mark.unit
-    def test_format_docstring(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_docstring(self, test_args, args):
         """Return one-line docstring."""
-        assert '"""Hello."""' == docformatter.format_docstring(
-            "    ",
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        assert '"""Hello."""' == uut._do_format_docstring(
+            INDENTATION,
             '''
 """
 
@@ -58,10 +70,20 @@ Hello.
         )
 
     @pytest.mark.unit
-    def test_format_docstring_with_summary_that_ends_in_quote(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_docstring_with_summary_that_ends_in_quote(
+        self, test_args, args
+    ):
         """Return one-line docstring with period after quote."""
-        assert '''""""Hello"."""''' == docformatter.format_docstring(
-            "    ",
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        assert '''""""Hello"."""''' == uut._do_format_docstring(
+            INDENTATION,
             '''
 """
 
@@ -71,15 +93,23 @@ Hello.
         )
 
     @pytest.mark.unit
-    def test_format_docstring_with_bad_indentation(self):
+    @pytest.mark.parametrize("args", [["--wrap-descriptions", "44", ""]])
+    def test_format_docstring_with_bad_indentation(self, test_args, args):
         """Add spaces to indentation when too few."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''"""Hello.
 
-    This should be indented but it is not. The
-    next line should be indented too. And
-    this too.
-    """''' == docformatter.format_docstring(
-            "    ",
+    This should be indented but it is not.
+    The next line should be indented too.
+    And this too.
+    """''' == uut._do_format_docstring(
+            INDENTATION,
             '''
 """Hello.
 
@@ -91,8 +121,16 @@ Hello.
         )
 
     @pytest.mark.unit
-    def test_format_docstring_with_too_much_indentation(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_docstring_with_too_much_indentation(self, test_args, args):
         """Remove spaces from indentation when too many."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''"""Hello.
 
     This should be dedented.
@@ -100,8 +138,8 @@ Hello.
     1. This too.
     2. And this.
     3. And this.
-    """''' == docformatter.format_docstring(
-            "    ",
+    """''' == uut._do_format_docstring(
+            INDENTATION,
             '''
 """Hello.
 
@@ -116,14 +154,23 @@ Hello.
         )
 
     @pytest.mark.unit
-    def test_format_docstring_with_trailing_whitespace(self):
+    @pytest.mark.parametrize("args", [["--wrap-descriptions", "52", ""]])
+    def test_format_docstring_with_trailing_whitespace(self, test_args, args):
         """Remove trailing white space."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''"""Hello.
 
     This should be not have trailing whitespace. The
-    next line should not have trailing whitespace either.
-    """''' == docformatter.format_docstring(
-            "    ",
+    next line should not have trailing whitespace
+    either.
+    """''' == uut._do_format_docstring(
+            INDENTATION,
             '''
 """Hello.\t
 \t
@@ -135,15 +182,31 @@ Hello.
         )
 
     @pytest.mark.unit
-    def test_format_docstring_with_empty_docstring(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_docstring_with_empty_docstring(self, test_args, args):
         """Do nothing with empty docstring."""
-        assert '""""""' == docformatter.format_docstring("    ", '""""""')
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        assert '""""""' == uut._do_format_docstring(INDENTATION, '""""""')
 
     @pytest.mark.unit
-    def test_format_docstring_with_no_period(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_docstring_with_no_period(self, test_args, args):
         """Add period to end of one-line and summary line."""
-        assert '"""Hello."""' == docformatter.format_docstring(
-            "    ",
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        assert '"""Hello."""' == uut._do_format_docstring(
+            INDENTATION,
             '''
 """
 
@@ -153,10 +216,18 @@ Hello
         )
 
     @pytest.mark.unit
-    def test_format_docstring_with_single_quotes(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_docstring_with_single_quotes(self, test_args, args):
         """Replace single triple quotes with triple double quotes."""
-        assert '"""Hello."""' == docformatter.format_docstring(
-            "    ",
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        assert '"""Hello."""' == uut._do_format_docstring(
+            INDENTATION,
             """
 '''
 
@@ -166,15 +237,25 @@ Hello.
         )
 
     @pytest.mark.unit
-    def test_format_docstring_with_single_quotes_multi_line(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_docstring_with_single_quotes_multi_line(
+        self, test_args, args
+    ):
         """Replace single triple quotes with triple double quotes."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''
     """Return x factorial.
 
     This uses math.factorial.
     """
-'''.strip() == docformatter.format_docstring(
-            "    ",
+'''.strip() == uut._do_format_docstring(
+            INDENTATION,
             """
     '''
     Return x factorial.
@@ -185,7 +266,18 @@ Hello.
         )
 
     @pytest.mark.unit
-    def test_format_docstring_leave_underlined_summaries_alone(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_docstring_leave_underlined_summaries_alone(
+        self, test_args, args
+    ):
+        """Leave underlined summary lines as is."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         docstring = '''"""
     Foo bar
     -------
@@ -193,51 +285,92 @@ Hello.
     This is more.
 
     """'''
-        assert docstring == docformatter.format_docstring("    ", docstring)
+        assert docstring == uut._do_format_docstring(INDENTATION, docstring)
 
 
 class TestFormatLists:
     """Class for testing format_docstring() with lists in the docstring."""
 
     @pytest.mark.unit
-    def test_format_docstring_should_ignore_numbered_lists(self):
+    @pytest.mark.parametrize("args", [["--wrap-descriptions", "72", ""]])
+    def test_format_docstring_should_ignore_numbered_lists(
+        self, test_args, args
+    ):
         """Ignore lists beginning with numbers."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         docstring = '''"""Hello.
 
     1. This should be indented but it is not. The
     next line should be indented too. But
     this is okay.
     """'''
-        assert docstring == docformatter.format_docstring(
-            "    ", docstring, description_wrap_length=72
+        assert docstring == uut._do_format_docstring(
+            INDENTATION,
+            docstring,
         )
 
     @pytest.mark.unit
-    def test_format_docstring_should_ignore_parameter_lists(self):
+    @pytest.mark.parametrize("args", [["--wrap-descriptions", "72", ""]])
+    def test_format_docstring_should_ignore_parameter_lists(
+        self, test_args, args
+    ):
         """Ignore lists beginning with <word> -."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         docstring = '''"""Hello.
 
     foo - This is a foo. This is a foo. This is a foo. This is a foo. This is.
     bar - This is a bar. This is a bar. This is a bar. This is a bar. This is.
     """'''
-        assert docstring == docformatter.format_docstring(
-            "    ", docstring, description_wrap_length=72
+        assert docstring == uut._do_format_docstring(
+            INDENTATION,
+            docstring,
         )
 
     @pytest.mark.unit
-    def test_format_docstring_should_ignore_colon_parameter_lists(self):
+    @pytest.mark.parametrize("args", [["--wrap-descriptions", "72", ""]])
+    def test_format_docstring_should_ignore_colon_parameter_lists(
+        self, test_args, args
+    ):
         """Ignore lists beginning with <word>:"""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         docstring = '''"""Hello.
 
     foo: This is a foo. This is a foo. This is a foo. This is a foo. This is.
     bar: This is a bar. This is a bar. This is a bar. This is a bar. This is.
     """'''
-        assert docstring == docformatter.format_docstring(
-            "    ", docstring, description_wrap_length=72
+        assert docstring == uut._do_format_docstring(
+            INDENTATION,
+            docstring,
         )
 
     @pytest.mark.unit
-    def test_format_docstring_should_leave_list_alone(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_docstring_should_leave_list_alone(self, test_args, args):
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         docstring = '''"""
     one
     two
@@ -251,13 +384,14 @@ class TestFormatLists:
     ten
     eleven
     """'''
-        assert docstring == docformatter.format_docstring(
-            "    ", docstring, strict=False
+        assert docstring == uut._do_format_docstring(
+            INDENTATION,
+            docstring,
         )
 
 
 class TestFormatWrap:
-    """Class for testing format_docstring() when requesting line wrapping."""
+    """Class for testing _do_format_docstring() with line wrapping."""
 
     @pytest.mark.unit
     def test_unwrap_summary(self):
@@ -267,8 +401,20 @@ class TestFormatWrap:
         )
 
     @pytest.mark.unit
-    def test_format_docstring_with_wrap(self):
-        """Wrap docstring."""
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_docstring_with_wrap(
+        self,
+        test_args,
+        args,
+    ):
+        """Wrap the docstring."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         # This function uses `random` so make sure each run of this test is
         # repeatable.
         random.seed(0)
@@ -278,12 +424,12 @@ class TestFormatWrap:
             range(min_line_length, 100), range(20)
         ):
             indentation = " " * num_indents
-            formatted_text = indentation + docformatter.format_docstring(
+            uut.args.wrap_summaries = max_length
+            formatted_text = indentation + uut._do_format_docstring(
                 indentation=indentation,
                 docstring=generate_random_docstring(
                     max_word_length=min_line_length // 2
                 ),
-                summary_wrap_length=max_length,
             )
             for line in formatted_text.split("\n"):
                 # It is not the formatter's fault if a word is too long to
@@ -292,18 +438,29 @@ class TestFormatWrap:
                     assert len(line) <= max_length
 
     @pytest.mark.unit
-    def test_format_docstring_with_weird_indentation_and_punctuation(self):
-        """"""
+    @pytest.mark.parametrize("args", [["--wrap-summaries", "79", ""]])
+    def test_format_docstring_with_weird_indentation_and_punctuation(
+        self,
+        test_args,
+        args,
+    ):
+        """Wrap and dedent docstring."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''
     """Creates and returns four was awakens to was created tracked ammonites
     was the fifty, arithmetical four was pyrotechnic to pyrotechnic physicists.
 
-    `four' falsified x falsified ammonites
-    to awakens to. `created' to ancestor was four to x dynamo to was
-    four ancestor to physicists().
+    `four' falsified x falsified ammonites to awakens to. `created' to
+    ancestor was four to x dynamo to was four ancestor to physicists().
     """
-        '''.strip() == docformatter.format_docstring(
-            "    ",
+        '''.strip() == uut._do_format_docstring(
+            INDENTATION,
             '''
             """Creates and returns four was awakens to was created tracked
                ammonites was the fifty, arithmetical four was pyrotechnic to
@@ -312,18 +469,29 @@ class TestFormatWrap:
                four ancestor to physicists().
             """
         '''.strip(),
-            summary_wrap_length=79,
         )
 
     @pytest.mark.unit
-    def test_format_docstring_with_description_wrapping(self):
+    @pytest.mark.parametrize("args", [["--wrap-descriptions", "72", ""]])
+    def test_format_docstring_with_description_wrapping(
+        self,
+        test_args,
+        args,
+    ):
         """Wrap description at 72 characters."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''"""Hello.
 
     This should be indented but it is not. The next line should be
     indented too. But this is okay.
-    """''' == docformatter.format_docstring(
-            "    ",
+    """''' == uut._do_format_docstring(
+            INDENTATION,
             '''
 """Hello.
 
@@ -333,12 +501,23 @@ class TestFormatWrap:
 
     """
 '''.strip(),
-            description_wrap_length=72,
         )
 
     @pytest.mark.unit
-    def test_format_docstring_should_ignore_multi_paragraph(self):
+    @pytest.mark.parametrize("args", [["--wrap-descriptions", "72", ""]])
+    def test_format_docstring_should_ignore_multi_paragraph(
+        self,
+        test_args,
+        args,
+    ):
         """Ignore multiple paragraphs in elaborate description."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         docstring = '''"""Hello.
 
     This should be indented but it is not. The
@@ -349,44 +528,83 @@ class TestFormatWrap:
     next line should be indented too. But
     this is okay.
     """'''
-        assert docstring == docformatter.format_docstring(
-            "    ", docstring, description_wrap_length=72
+        assert docstring == uut._do_format_docstring(
+            INDENTATION,
+            docstring,
         )
 
     @pytest.mark.unit
-    def test_format_docstring_should_ignore_doctests(self):
+    @pytest.mark.parametrize("args", [["--wrap-descriptions", "72", ""]])
+    def test_format_docstring_should_ignore_doctests(
+        self,
+        test_args,
+        args,
+    ):
         """Leave doctests alone."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         docstring = '''"""Hello.
 
     >>> 4
     4
     """'''
-        assert docstring == docformatter.format_docstring(
-            "    ", docstring, description_wrap_length=72
+        assert docstring == uut._do_format_docstring(
+            INDENTATION,
+            docstring,
         )
 
     @pytest.mark.unit
-    def test_format_docstring_should_ignore_doctests_in_summary(self):
+    @pytest.mark.parametrize("args", [["--wrap-descriptions", "72", ""]])
+    def test_format_docstring_should_ignore_doctests_in_summary(
+        self,
+        test_args,
+        args,
+    ):
         """Leave doctests alone if they're in the summary."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         docstring = '''"""
     >>> 4
     4
 
     """'''
-        assert docstring == docformatter.format_docstring(
-            "    ", docstring, description_wrap_length=72
+        assert docstring == uut._do_format_docstring(
+            INDENTATION,
+            docstring,
         )
 
     @pytest.mark.unit
-    def test_format_docstring_should_maintain_indentation_of_doctest(self):
+    @pytest.mark.parametrize("args", [["--wrap-descriptions", "72", ""]])
+    def test_format_docstring_should_maintain_indentation_of_doctest(
+        self,
+        test_args,
+        args,
+    ):
         """Don't change indentation of doctest lines."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''"""Foo bar bing bang.
 
         >>> tests = DocTestFinder().find(_TestClass)
         >>> runner = DocTestRunner(verbose=False)
         >>> tests.sort(key = lambda test: test.name)
-    """''' == docformatter.format_docstring(
-            "    ",
+    """''' == uut._do_format_docstring(
+            INDENTATION,
             docstring='''"""Foo bar bing bang.
 
         >>> tests = DocTestFinder().find(_TestClass)
@@ -394,12 +612,35 @@ class TestFormatWrap:
         >>> tests.sort(key = lambda test: test.name)
 
     """''',
-            description_wrap_length=72,
         )
 
     @pytest.mark.unit
-    def test_force_wrap(self):
+    @pytest.mark.parametrize(
+        "args",
+        [
+            [
+                "--wrap-descriptions",
+                "72",
+                "--wrap-summaries",
+                "50",
+                "--force-wrap",
+                "",
+            ]
+        ],
+    )
+    def test_force_wrap(
+        self,
+        test_args,
+        args,
+    ):
         """Force even lists to be wrapped."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert (
             (
                 '''\
@@ -408,42 +649,80 @@ class TestFormatWrap:
     convergence."""\
 '''
             )
-            == docformatter.format_docstring(
-                "    ",
+            == uut._do_format_docstring(
+                INDENTATION,
                 '''\
 """
 num_iterations is the number of updates - instead of a better definition of convergence.
 """\
 ''',
-                description_wrap_length=50,
-                summary_wrap_length=50,
-                force_wrap=True,
             )
         )
 
     @pytest.mark.unit
+    @pytest.mark.parametrize(
+        "args",
+        [
+            [
+                "--wrap-summaries",
+                "30",
+                "--tab-width",
+                "4",
+                "",
+            ]
+        ],
+    )
     def test_format_docstring_with_summary_only_and_wrap_and_tab_indentation(
         self,
+        test_args,
+        args,
     ):
         """"Should account for length of tab when wrapping.
 
         See PR #69.
         """
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''
 \t\t"""Some summary x x x
 \t\tx."""
-'''.strip() == docformatter.format_docstring(
+'''.strip() == uut._do_format_docstring(
             "\t\t",
             '''
 \t\t"""Some summary x x x x."""
 '''.strip(),
-            summary_wrap_length=30,
-            tab_width=4,
         )
 
     @pytest.mark.unit
-    def test_format_docstring_for_multi_line_summary_alone(self):
+    @pytest.mark.parametrize(
+        "args",
+        [
+            [
+                "--wrap-summaries",
+                "69",
+                "--close-quotes-on-newline",
+                "",
+            ]
+        ],
+    )
+    def test_format_docstring_for_multi_line_summary_alone(
+        self,
+        test_args,
+        args,
+    ):
         """Place closing quotes on newline when wrapping one-liner."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert (
             (
                 '''\
@@ -452,33 +731,51 @@ num_iterations is the number of updates - instead of a better definition of conv
     """\
 '''
             )
-            == docformatter.format_docstring(
-                "    ",
+            == uut._do_format_docstring(
+                INDENTATION,
                 '''\
 """This one-line docstring will be multi-line because it's quite long."""\
 ''',
-                summary_wrap_length=69,
-                close_quotes_on_newline=True,
             )
         )
 
     @pytest.mark.unit
-    def test_format_docstring_for_one_line_summary_alone_but_too_long(self):
+    @pytest.mark.parametrize(
+        "args",
+        [
+            [
+                "--wrap-summaries",
+                "88",
+                "--close-quotes-on-newline",
+                "",
+            ]
+        ],
+    )
+    def test_format_docstring_for_one_line_summary_alone_but_too_long(
+        self,
+        test_args,
+        args,
+    ):
         """"""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert (
             (
                 '''\
 """This one-line docstring will not be wrapped and quotes will be in-line."""\
 '''
             )
-            == docformatter.format_docstring(
-                "    ",
+            == uut._do_format_docstring(
+                INDENTATION,
                 '''\
 """This one-line docstring will not be wrapped and quotes will be 
 in-line."""\
 ''',
-                summary_wrap_length=88,
-                close_quotes_on_newline=True,
             )
         )
 
@@ -487,13 +784,25 @@ class TestFormatStyleOptions:
     """Class for testing format_docstring() when requesting style options."""
 
     @pytest.mark.unit
-    def test_format_docstring_with_no_post_description_blank(self):
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_docstring_with_no_post_description_blank(
+        self,
+        test_args,
+        args,
+    ):
         """Remove blank lines before closing triple quotes."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''"""Hello.
 
     Description.
-    """''' == docformatter.format_docstring(
-            "    ",
+    """''' == uut._do_format_docstring(
+            INDENTATION,
             '''
 """
 
@@ -504,18 +813,29 @@ Hello.
 
     """
 '''.strip(),
-            post_description_blank=False,
         )
 
     @pytest.mark.unit
-    def test_format_docstring_with_pre_summary_newline(self):
+    @pytest.mark.parametrize("args", [["--pre-summary-newline", ""]])
+    def test_format_docstring_with_pre_summary_newline(
+        self,
+        test_args,
+        args,
+    ):
         """Remove blank line before summary."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert '''"""
     Hello.
 
     Description.
-    """''' == docformatter.format_docstring(
-            "    ",
+    """''' == uut._do_format_docstring(
+            INDENTATION,
             '''
 """
 
@@ -526,11 +846,23 @@ Hello.
 
     """
 '''.strip(),
-            pre_summary_newline=True,
         )
 
     @pytest.mark.unit
-    def test_format_docstring_make_summary_multi_line(self):
+    @pytest.mark.parametrize("args", [["--make-summary-multi-line", ""]])
+    def test_format_docstring_make_summary_multi_line(
+        self,
+        test_args,
+        args,
+    ):
+        """Place the one-line docstring between triple quotes."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert (
             (
                 '''\
@@ -539,24 +871,34 @@ Hello.
     """\
 '''
             )
-            == docformatter.format_docstring(
-                "    ",
+            == uut._do_format_docstring(
+                INDENTATION,
                 '''\
 """This one-line docstring will be multi-line"""\
 ''',
-                make_summary_multi_line=True,
             )
         )
 
     @pytest.mark.unit
-    def test_format_docstring_pre_summary_space(self):
-        """"""
+    @pytest.mark.parametrize("args", [["--pre-summary-space", ""]])
+    def test_format_docstring_pre_summary_space(
+        self,
+        test_args,
+        args,
+    ):
+        """Place a space between the opening quotes and the summary."""
+        uut = Formator(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
         assert (
             '''""" This one-line docstring will have a leading space."""'''
-        ) == docformatter.format_docstring(
-            "    ",
+        ) == uut._do_format_docstring(
+            INDENTATION,
             '''\
 """This one-line docstring will have a leading space."""\
 ''',
-            pre_summary_space=True,
         )