def test_build(self):
        def basic_implementation(self, a, b, **kwargs):
            return (a, b, kwargs)

        m = MethodBuilder("basic_wrapper", basic_implementation)
        m.with_preamble("Hello World!")
        m.with_returns("All the arguments.", annotation=Tuple)
        m.with_notes("This method doesn't do a whole lot.")
        m.with_arg("a", desc="A value.", annotation=int)

        with pytest.raises(
                RuntimeError,
                match=re.escape(
                    "Proposed method signature `basic_wrapper(self, a: int)` is not compatible with implementation signature `implementation(self, a, b, **kwargs)`"
                ),
        ):
            m.build()

        m.with_arg("b", desc="Another value.", annotation=str)
        m.with_arg(
            "c",
            desc="Yet another value.",
            annotation=float,
            virtual=True,
            kind="keyword_only",
        )

        c = m.build()

        assert (c.__doc__ == textwrap.dedent("""
            Hello World!

            Args:
                a: A value.
                b: Another value.
                c: Yet another value.

            Returns:
                All the arguments.

            Notes:
                This method doesn't do a whole lot.
        """).strip())
        assert str(
            inspect.signature(c)) == "(self, a: int, b: str, *, c: float)"
        assert c(None, 1, "two", c=3.0) == (1, "two", {"c": 3.0})
        with pytest.raises(
                TypeError,
                match=re.escape(
                    "basic_wrapper() got unexpected keyword arguments: {'d'}."
                ),
        ):
            c(None, 1, "two", c=3.0, d=None)
    def test_with_arg(self):
        m = MethodBuilder("basic_wrapper", None)
        m.with_arg("a", desc="A value.")

        assert len(m.method_args) == 2
        assert m.method_args[0].name == "self"
        assert m.method_args[1].name == "a"

        m.with_arg("c",
                   desc="Another value.",
                   kind="keyword_only",
                   virtual=True)

        assert len(m.method_args) == 3
        assert m.method_args[2].name == "kwargs"
        assert m.method_args[2].kind == Parameter.VAR_KEYWORD
        assert len(m.method_args_virtual) == 1
        assert m.method_args_virtual[0].kind == Parameter.KEYWORD_ONLY

        m.with_arg("d", desc="Another value.", only_if=False)
        assert len(m.method_args) == 3

        with pytest.raises(
                RuntimeError,
                match=re.escape(
                    "Virtual arguments can only be `KEYWORD_ONLY` or `VAR_KEYWORD`, not `POSITIONAL_OR_KEYWORD`."
                ),
        ):
            m.with_arg("d", desc="Another value.", virtual=True)

        with pytest.raises(
                RuntimeError,
                match=re.escape(
                    "Arguments of kind `POSITIONAL_OR_KEYWORD` cannot be added after `VAR_KEYWORD` arguments."
                ),
        ):
            m.with_arg("d", desc="Another value.")

        with pytest.raises(
                RuntimeError,
                match=re.escape(
                    "Arguments of kind `POSITIONAL_ONLY` cannot be added after `VAR_KEYWORD` arguments."
                ),
        ):
            m.with_arg("d", desc="Another value.", kind="positional_only")

        m.with_arg("e", desc="Collect all.", kind="var_keyword", virtual=True)

        with pytest.raises(
                RuntimeError,
                match=re.escape(
                    "Virtual arguments of kind `KEYWORD_ONLY` cannot be added after `VAR_KEYWORD` arguments."
                ),
        ):
            m.with_arg("f",
                       desc="Another value.",
                       virtual=True,
                       kind="keyword_only")