Repository: marshmallow-code/apispec
Pull request #419 (2019-04-02 09:23:42)

Factorize components subsections mapping and add utils.build_ref
This PR provides the factorization discussed in https://github.com/marshmallow-code/apispec/pull/408.

(I'll fix the black issues at home.)


Patch:
diff --git a/src/apispec/core.py b/src/apispec/core.py
index cf260ab..33304a8 100644
--- a/src/apispec/core.py
+++ b/src/apispec/core.py
@@ -8,7 +8,7 @@ from .exceptions import (
     PluginMethodNotImplementedError,
     DuplicateComponentNameError,
 )
-from .utils import OpenAPIVersion, deepupdate
+from .utils import OpenAPIVersion, deepupdate, COMPONENT_SUBSECTIONS, build_reference
 
 VALID_METHODS_OPENAPI_V2 = ["get", "post", "put", "patch", "delete", "head", "options"]
 
@@ -51,13 +51,7 @@ def clean_operations(operations, openapi_major_version):
         """
         if isinstance(obj, dict):
             return obj
-
-        ref_paths = {
-            "parameter": {2: "parameters", 3: "components/parameters"},
-            "response": {2: "responses", 3: "components/responses"},
-        }
-        ref_path = ref_paths[obj_type][openapi_major_version]
-        return {"$ref": "#/{}/{}".format(ref_path, obj)}
+        return build_reference(obj_type, openapi_major_version, obj)
 
     for operation in (operations or {}).values():
         if "parameters" in operation:
@@ -95,17 +89,15 @@ class Components(object):
         self._security_schemes = {}
 
     def to_dict(self):
-        if self.openapi_version.major < 3:
-            schemas_key = "definitions"
-            security_key = "securityDefinitions"
-        else:
-            schemas_key = "schemas"
-            security_key = "securitySchemes"
+        subsections = {
+            "schema": self._schemas,
+            "parameter": self._parameters,
+            "response": self._responses,
+            "security_scheme": self._security_schemes,
+        }
         return {
-            "parameters": self._parameters,
-            "responses": self._responses,
-            schemas_key: self._schemas,
-            security_key: self._security_schemes,
+            COMPONENT_SUBSECTIONS[self.openapi_version.major][k]: v
+            for k, v in iteritems(subsections)
         }
 
     def schema(self, name, component=None, **kwargs):
diff --git a/src/apispec/ext/marshmallow/openapi.py b/src/apispec/ext/marshmallow/openapi.py
index 612efae..cd72e3f 100644
--- a/src/apispec/ext/marshmallow/openapi.py
+++ b/src/apispec/ext/marshmallow/openapi.py
@@ -19,7 +19,7 @@ from marshmallow.compat import iteritems
 from marshmallow.orderedset import OrderedSet
 
 from apispec.compat import RegexType
-from apispec.utils import OpenAPIVersion
+from apispec.utils import OpenAPIVersion, build_reference
 from .common import (
     resolve_schema_cls,
     get_fields,
@@ -684,22 +684,14 @@ class OpenAPIConverter(object):
         """Method to create a dictionary containing a JSON reference to the
         schema in the spec
         """
-        ref_path = self.get_ref_path()
         schema_key = make_schema_key(schema)
-        ref_schema = {"$ref": "#/{}/{}".format(ref_path, self.refs[schema_key])}
+        ref_schema = build_reference(
+            "schema", self.openapi_version.major, self.refs[schema_key]
+        )
         if getattr(schema, "many", False):
             return {"type": "array", "items": ref_schema}
         return ref_schema
 
-    def get_ref_path(self):
-        """Return the path for references based on the openapi version
-
-        :param int openapi_version.major: The major version of the OpenAPI standard
-            to use. Supported values are 2 and 3.
-        """
-        ref_paths = {2: "definitions", 3: "components/schemas"}
-        return ref_paths[self.openapi_version.major]
-
     def resolve_schema_dict(self, schema):
         if isinstance(schema, dict):
             if schema.get("type") == "array" and "items" in schema:
diff --git a/src/apispec/utils.py b/src/apispec/utils.py
index 95c5795..f86f3f9 100644
--- a/src/apispec/utils.py
+++ b/src/apispec/utils.py
@@ -10,6 +10,38 @@ from distutils import version
 from apispec import exceptions
 
 
+COMPONENT_SUBSECTIONS = {
+    2: {
+        "schema": "definitions",
+        "parameter": "parameters",
+        "response": "responses",
+        "security_scheme": "securityDefinitions",
+    },
+    3: {
+        "schema": "schemas",
+        "parameter": "parameters",
+        "response": "responses",
+        "security_scheme": "securitySchemes",
+    },
+}
+
+
+def build_reference(component_type, openapi_major_version, component_name):
+    """Return path to reference
+
+    :param str component_type: Component type (schema, parameter, response, security_scheme)
+    :param int openapi_major_version: OpenAPI major version (2 or 3)
+    :param str component_name: Name of component to reference
+    """
+    return {
+        "$ref": "#/{}{}/{}".format(
+            "components/" if openapi_major_version >= 3 else "",
+            COMPONENT_SUBSECTIONS[openapi_major_version][component_type],
+            component_name,
+        )
+    }
+
+
 def validate_spec(spec):
     """Validate the output of an :class:`APISpec` object against the
     OpenAPI specification.


Test patch:
diff --git a/tests/test_core.py b/tests/test_core.py
index 4449450..bff4193 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -13,6 +13,7 @@ from .utils import (
     get_parameters,
     get_responses,
     get_security_schemes,
+    build_ref,
 )
 
 
@@ -386,15 +387,12 @@ class TestPath:
         metadata = spec.to_dict()
         p = get_paths(spec)["/pet/{petId}"]["get"]
 
+        assert p["parameters"][0] == build_ref(spec, "parameter", "test_parameter")
         if spec.openapi_version.major < 3:
-            assert p["parameters"][0] == {"$ref": "#/parameters/test_parameter"}
             assert (
                 route_spec["parameters"][0] == metadata["parameters"]["test_parameter"]
             )
         else:
-            assert p["parameters"][0] == {
-                "$ref": "#/components/parameters/test_parameter"
-            }
             assert (
                 route_spec["parameters"][0]
                 == metadata["components"]["parameters"]["test_parameter"]
@@ -413,15 +411,12 @@ class TestPath:
         metadata = spec.to_dict()
         p = get_paths(spec)["/pet/{petId}"]["get"]
 
+        assert p["responses"]["200"] == build_ref(spec, "response", "test_response")
         if spec.openapi_version.major < 3:
-            assert p["responses"]["200"] == {"$ref": "#/responses/test_response"}
             assert (
                 route_spec["responses"]["200"] == metadata["responses"]["test_response"]
             )
         else:
-            assert p["responses"]["200"] == {
-                "$ref": "#/components/responses/test_response"
-            }
             assert (
                 route_spec["responses"]["200"]
                 == metadata["components"]["responses"]["test_response"]
diff --git a/tests/test_ext_marshmallow.py b/tests/test_ext_marshmallow.py
index 56dd0f1..c1ffee1 100644
--- a/tests/test_ext_marshmallow.py
+++ b/tests/test_ext_marshmallow.py
@@ -22,7 +22,7 @@ from .schemas import (
     AnalysisWithListSchema,
 )
 
-from .utils import get_schemas, get_parameters, get_responses, get_paths, ref_path
+from .utils import get_schemas, get_parameters, get_responses, get_paths, build_ref
 
 
 class TestDefinitionHelper:
@@ -58,7 +58,9 @@ class TestDefinitionHelper:
             "/test",
             operations={
                 "get": {
-                    "responses": {"200": {"schema": {"$ref": "#/definitions/analysis"}}}
+                    "responses": {
+                        "200": {"schema": build_ref(spec, "schema", "analysis")}
+                    }
                 }
             },
         )
@@ -89,7 +91,9 @@ class TestDefinitionHelper:
             "/test",
             operations={
                 "get": {
-                    "responses": {"200": {"schema": {"$ref": "#/definitions/analysis"}}}
+                    "responses": {
+                        "200": {"schema": build_ref(spec, "schema", "analysis")}
+                    }
                 }
             },
         )
@@ -138,10 +142,10 @@ class TestDefinitionHelper:
         pet_unmodified_ref = definitions["MultiModifierSchema"]["properties"][
             "pet_unmodified"
         ]
-        assert pet_unmodified_ref == {"$ref": ref_path(spec) + "Pet"}
+        assert pet_unmodified_ref == build_ref(spec, "schema", "Pet")
 
         pet_exclude = definitions["MultiModifierSchema"]["properties"]["pet_exclude"]
-        assert pet_exclude == {"$ref": ref_path(spec) + "Pet_Exclude"}
+        assert pet_exclude == build_ref(spec, "schema", "Pet_Exclude")
 
     def test_schema_with_clashing_names(self, spec):
         class Pet(PetSchema):
@@ -197,7 +201,7 @@ class TestComponentParameterHelper:
             reference = parameter["schema"]
         else:
             reference = parameter["content"]["application/json"]["schema"]
-        assert reference == {"$ref": ref_path(spec) + "Pet"}
+        assert reference == build_ref(spec, "schema", "Pet")
 
         resolved_schema = spec.components._schemas["Pet"]
         assert resolved_schema["properties"]["name"]["type"] == "string"
@@ -218,7 +222,7 @@ class TestComponentResponseHelper:
             reference = response["schema"]
         else:
             reference = response["content"]["application/json"]["schema"]
-        assert reference == {"$ref": ref_path(spec) + "Pet"}
+        assert reference == build_ref(spec, "schema", "Pet")
 
         resolved_schema = spec.components._schemas["Pet"]
         assert resolved_schema["properties"]["id"]["type"] == "integer"
@@ -231,7 +235,7 @@ class TestComponentResponseHelper:
         spec.components.response("GetPetOk", resp)
         response = get_responses(spec)["GetPetOk"]
         reference = response["headers"]["PetHeader"]["schema"]
-        assert reference == {"$ref": ref_path(spec) + "Pet"}
+        assert reference == build_ref(spec, "schema", "Pet")
 
         resolved_schema = spec.components._schemas["Pet"]
         assert resolved_schema["properties"]["id"]["type"] == "integer"
@@ -314,8 +318,8 @@ class TestOperationHelper:
         else:
             schema_reference = get["responses"][200]["schema"]
             header_reference = get["responses"][200]["headers"]["PetHeader"]["schema"]
-        assert schema_reference == {"$ref": ref_path(spec_fixture.spec) + "Pet"}
-        assert header_reference == {"$ref": ref_path(spec_fixture.spec) + "Pet"}
+        assert schema_reference == build_ref(spec_fixture.spec, "schema", "Pet")
+        assert header_reference == build_ref(spec_fixture.spec, "schema", "Pet")
         assert len(spec_fixture.spec.components._schemas) == 1
         resolved_schema = spec_fixture.spec.components._schemas["Pet"]
         assert resolved_schema == spec_fixture.openapi.schema2jsonschema(PetSchema)
@@ -363,8 +367,8 @@ class TestOperationHelper:
             ]
             header_reference = get["responses"][200]["headers"]["PetHeader"]["schema"]
 
-        assert schema_reference == {"$ref": ref_path(spec_fixture.spec) + "Pet"}
-        assert header_reference == {"$ref": ref_path(spec_fixture.spec) + "Pet"}
+        assert schema_reference == build_ref(spec_fixture.spec, "schema", "Pet")
+        assert header_reference == build_ref(spec_fixture.spec, "schema", "Pet")
         assert len(spec_fixture.spec.components._schemas) == 1
         resolved_schema = spec_fixture.spec.components._schemas["Pet"]
         assert resolved_schema == spec_fixture.openapi.schema2jsonschema(PetSchema)
@@ -443,9 +447,8 @@ class TestOperationHelper:
             path="/pet", operations={"get": {"responses": {200: {"schema": PetSchema}}}}
         )
         get = get_paths(spec_fixture.spec)["/pet"]["get"]
-        assert (
-            get["responses"][200]["schema"]["$ref"]
-            == ref_path(spec_fixture.spec) + "Pet"
+        assert get["responses"][200]["schema"] == build_ref(
+            spec_fixture.spec, "schema", "Pet"
         )
 
     @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True)
@@ -462,10 +465,9 @@ class TestOperationHelper:
             },
         )
         get = get_paths(spec_fixture.spec)["/pet"]["get"]
-        assert (
-            get["responses"][200]["content"]["application/json"]["schema"]["$ref"]
-            == ref_path(spec_fixture.spec) + "Pet"
-        )
+        assert get["responses"][200]["content"]["application/json"][
+            "schema"
+        ] == build_ref(spec_fixture.spec, "schema", "Pet")
 
     def test_schema_uses_ref_if_available_name_resolver_returns_none_v2(self):
         def resolver(schema):
@@ -482,7 +484,7 @@ class TestOperationHelper:
             path="/pet", operations={"get": {"responses": {200: {"schema": PetSchema}}}}
         )
         get = get_paths(spec)["/pet"]["get"]
-        assert get["responses"][200]["schema"]["$ref"] == ref_path(spec) + "Pet"
+        assert get["responses"][200]["schema"] == build_ref(spec, "schema", "Pet")
 
     def test_schema_uses_ref_if_available_name_resolver_returns_none_v3(self):
         def resolver(schema):
@@ -506,10 +508,9 @@ class TestOperationHelper:
             },
         )
         get = get_paths(spec)["/pet"]["get"]
-        assert (
-            get["responses"][200]["content"]["application/json"]["schema"]["$ref"]
-            == ref_path(spec) + "Pet"
-        )
+        assert get["responses"][200]["content"]["application/json"][
+            "schema"
+        ] == build_ref(spec, "schema", "Pet")
 
     @pytest.mark.parametrize("spec_fixture", ("2.0",), indirect=True)
     def test_schema_uses_ref_in_parameters_and_request_body_if_available_v2(
@@ -527,9 +528,8 @@ class TestOperationHelper:
         assert "schema" not in p["get"]["parameters"][0]
         post = p["post"]
         assert len(post["parameters"]) == 1
-        assert (
-            post["parameters"][0]["schema"]["$ref"]
-            == ref_path(spec_fixture.spec) + "Pet"
+        assert post["parameters"][0]["schema"] == build_ref(
+            spec_fixture.spec, "schema", "Pet"
         )
 
     @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True)
@@ -552,7 +552,7 @@ class TestOperationHelper:
         assert "schema" in p["get"]["parameters"][0]
         post = p["post"]
         schema_ref = post["requestBody"]["content"]["application/json"]["schema"]
-        assert schema_ref == {"$ref": ref_path(spec_fixture.spec) + "Pet"}
+        assert schema_ref == build_ref(spec_fixture.spec, "schema", "Pet")
 
     @pytest.mark.parametrize("spec_fixture", ("2.0",), indirect=True)
     def test_schema_array_uses_ref_if_available_v2(self, spec_fixture):
@@ -574,7 +574,7 @@ class TestOperationHelper:
         assert len(get["parameters"]) == 1
         resolved_schema = {
             "type": "array",
-            "items": {"$ref": ref_path(spec_fixture.spec) + "Pet"},
+            "items": build_ref(spec_fixture.spec, "schema", "Pet"),
         }
         assert get["parameters"][0]["schema"] == resolved_schema
         assert get["responses"][200]["schema"] == resolved_schema
@@ -612,7 +612,7 @@ class TestOperationHelper:
         assert len(get["parameters"]) == 1
         resolved_schema = {
             "type": "array",
-            "items": {"$ref": ref_path(spec_fixture.spec) + "Pet"},
+            "items": build_ref(spec_fixture.spec, "schema", "Pet"),
         }
         request_schema = get["parameters"][0]["content"]["application/json"]["schema"]
         assert request_schema == resolved_schema
@@ -644,8 +644,8 @@ class TestOperationHelper:
         assert get["responses"][200]["schema"] == {
             "type": "object",
             "properties": {
-                "mother": {"$ref": ref_path(spec_fixture.spec) + "Pet"},
-                "father": {"$ref": ref_path(spec_fixture.spec) + "Pet"},
+                "mother": build_ref(spec_fixture.spec, "schema", "Pet"),
+                "father": build_ref(spec_fixture.spec, "schema", "Pet"),
             },
         }
 
@@ -678,38 +678,36 @@ class TestOperationHelper:
         assert get["responses"][200]["content"]["application/json"]["schema"] == {
             "type": "object",
             "properties": {
-                "mother": {"$ref": ref_path(spec_fixture.spec) + "Pet"},
-                "father": {"$ref": ref_path(spec_fixture.spec) + "Pet"},
+                "mother": build_ref(spec_fixture.spec, "schema", "Pet"),
+                "father": build_ref(spec_fixture.spec, "schema", "Pet"),
             },
         }
 
     def test_parameter_reference(self, spec_fixture):
         if spec_fixture.spec.openapi_version.major < 3:
             param = {"schema": PetSchema}
-            reference = "#/parameters/Pet"
         else:
             param = {"content": {"application/json": {"schema": PetSchema}}}
-            reference = "#/components/parameters/Pet"
         spec_fixture.spec.components.parameter("Pet", "body", param)
         spec_fixture.spec.path(
             path="/parents", operations={"get": {"parameters": ["Pet"]}}
         )
         get = get_paths(spec_fixture.spec)["/parents"]["get"]
-        assert get["parameters"] == [{"$ref": reference}]
+        assert get["parameters"] == [build_ref(spec_fixture.spec, "parameter", "Pet")]
 
     def test_response_reference(self, spec_fixture):
         if spec_fixture.spec.openapi_version.major < 3:
             resp = {"schema": PetSchema}
-            reference = "#/responses/Pet"
         else:
             resp = {"content": {"application/json": {"schema": PetSchema}}}
-            reference = "#/components/responses/Pet"
         spec_fixture.spec.components.response("Pet", resp)
         spec_fixture.spec.path(
             path="/parents", operations={"get": {"responses": {"200": "Pet"}}}
         )
         get = get_paths(spec_fixture.spec)["/parents"]["get"]
-        assert get["responses"] == {"200": {"$ref": reference}}
+        assert get["responses"] == {
+            "200": build_ref(spec_fixture.spec, "response", "Pet")
+        }
 
     def test_schema_global_state_untouched_2json(self, spec_fixture):
         assert RunSchema._declared_fields["sample"]._Nested__schema is None
@@ -728,8 +726,8 @@ class TestCircularReference:
     def test_circular_referencing_schemas(self, spec):
         spec.components.schema("Analysis", schema=AnalysisSchema)
         definitions = get_schemas(spec)
-        ref = definitions["Analysis"]["properties"]["sample"]["$ref"]
-        assert ref == ref_path(spec) + "Sample"
+        ref = definitions["Analysis"]["properties"]["sample"]
+        assert ref == build_ref(spec, "schema", "Sample")
 
 
 # Regression tests for issue #55
@@ -737,8 +735,8 @@ class TestSelfReference:
     def test_self_referencing_field_single(self, spec):
         spec.components.schema("SelfReference", schema=SelfReferencingSchema)
         definitions = get_schemas(spec)
-        ref = definitions["SelfReference"]["properties"]["single"]["$ref"]
-        assert ref == ref_path(spec) + "SelfReference"
+        ref = definitions["SelfReference"]["properties"]["single"]
+        assert ref == build_ref(spec, "schema", "SelfReference")
 
     def test_self_referencing_field_many(self, spec):
         spec.components.schema("SelfReference", schema=SelfReferencingSchema)
@@ -746,7 +744,7 @@ class TestSelfReference:
         result = definitions["SelfReference"]["properties"]["many"]
         assert result == {
             "type": "array",
-            "items": {"$ref": ref_path(spec) + "SelfReference"},
+            "items": build_ref(spec, "schema", "SelfReference"),
         }
 
 
@@ -813,7 +811,7 @@ class TestDictValues:
 
         result = get_schemas(spec)["SchemaWithDict"]["properties"]["dict_field"]
         assert result == {
-            "additionalProperties": {"$ref": ref_path(spec) + "Pet"},
+            "additionalProperties": build_ref(spec, "schema", "Pet"),
             "type": "object",
         }
 
@@ -828,4 +826,4 @@ class TestList:
         assert len(get_schemas(spec)) == 2
 
         result = get_schemas(spec)["SchemaWithList"]["properties"]["list_field"]
-        assert result == {"items": {"$ref": ref_path(spec) + "Pet"}, "type": "array"}
+        assert result == {"items": build_ref(spec, "schema", "Pet"), "type": "array"}
diff --git a/tests/test_ext_marshmallow_openapi.py b/tests/test_ext_marshmallow_openapi.py
index 3a64ef3..cce5006 100644
--- a/tests/test_ext_marshmallow_openapi.py
+++ b/tests/test_ext_marshmallow_openapi.py
@@ -12,7 +12,7 @@ from apispec.ext.marshmallow import MarshmallowPlugin
 from apispec.ext.marshmallow.openapi import MARSHMALLOW_VERSION_INFO
 from apispec import exceptions, utils, APISpec
 
-from .utils import get_schemas, ref_path
+from .utils import get_schemas, build_ref
 
 
 class TestMarshmallowFieldToOpenAPI:
@@ -482,7 +482,7 @@ class TestMarshmallowSchemaToParameters:
         assert len(res_nodump) == 1
         param = res_nodump[0]
         assert param["in"] == "body"
-        assert param["schema"] == {"$ref": ref_path(openapi.spec) + "User"}
+        assert param["schema"] == build_ref(openapi.spec, "schema", "User")
 
     # json/body is invalid for OpenAPI 3
     @pytest.mark.parametrize("openapi", ("2.0",), indirect=True)
@@ -588,7 +588,7 @@ class TestNesting:
         )
         result = spec_fixture.openapi.field2property(category)
         assert result == {
-            "allOf": [{"$ref": ref_path(spec_fixture.spec) + "Category"}],
+            "allOf": [build_ref(spec_fixture.spec, "schema", "Category")],
             "description": "A category",
             "x-extension": "A great extension",
         }
@@ -596,35 +596,35 @@ class TestNesting:
     def test_field2property_nested_spec(self, spec_fixture):
         spec_fixture.spec.components.schema("Category", schema=CategorySchema)
         category = fields.Nested(CategorySchema)
-        assert spec_fixture.openapi.field2property(category) == {
-            "$ref": ref_path(spec_fixture.spec) + "Category"
-        }
+        assert spec_fixture.openapi.field2property(category) == build_ref(
+            spec_fixture.spec, "schema", "Category"
+        )
 
     def test_field2property_nested_many_spec(self, spec_fixture):
         spec_fixture.spec.components.schema("Category", schema=CategorySchema)
         category = fields.Nested(CategorySchema, many=True)
         ret = spec_fixture.openapi.field2property(category)
         assert ret["type"] == "array"
-        assert ret["items"] == {"$ref": ref_path(spec_fixture.spec) + "Category"}
+        assert ret["items"] == build_ref(spec_fixture.spec, "schema", "Category")
 
     def test_field2property_nested_ref(self, spec_fixture):
         category = fields.Nested(CategorySchema)
         ref = spec_fixture.openapi.field2property(category)
-        assert ref == {"$ref": ref_path(spec_fixture.spec) + "Category"}
+        assert ref == build_ref(spec_fixture.spec, "schema", "Category")
 
     def test_field2property_nested_many(self, spec_fixture):
         categories = fields.Nested(CategorySchema, many=True)
         res = spec_fixture.openapi.field2property(categories)
         assert res["type"] == "array"
-        assert res["items"] == {"$ref": ref_path(spec_fixture.spec) + "Category"}
+        assert res["items"] == build_ref(spec_fixture.spec, "schema", "Category")
 
     def test_schema2jsonschema_with_nested_fields(self, spec_fixture):
         res = spec_fixture.openapi.schema2jsonschema(PetSchema)
         props = res["properties"]
 
-        assert props["category"]["items"] == {
-            "$ref": ref_path(spec_fixture.spec) + "Category"
-        }
+        assert props["category"]["items"] == build_ref(
+            spec_fixture.spec, "schema", "Category"
+        )
 
     @pytest.mark.parametrize("modifier", ("only", "exclude"))
     def test_schema2jsonschema_with_nested_fields_only_exclude(
@@ -677,27 +677,25 @@ class TestNesting:
         assert "breed" not in category_props
 
     def test_nested_field_with_property(self, spec_fixture):
-        r_path = ref_path(spec_fixture.spec)
-
         category_1 = fields.Nested(CategorySchema)
         category_2 = fields.Nested(CategorySchema, dump_only=True)
         category_3 = fields.Nested(CategorySchema, many=True)
         category_4 = fields.Nested(CategorySchema, many=True, dump_only=True)
         spec_fixture.spec.components.schema("Category", schema=CategorySchema)
 
-        assert spec_fixture.openapi.field2property(category_1) == {
-            "$ref": r_path + "Category"
-        }
+        assert spec_fixture.openapi.field2property(category_1) == build_ref(
+            spec_fixture.spec, "schema", "Category"
+        )
         assert spec_fixture.openapi.field2property(category_2) == {
-            "allOf": [{"$ref": r_path + "Category"}],
+            "allOf": [build_ref(spec_fixture.spec, "schema", "Category")],
             "readOnly": True,
         }
         assert spec_fixture.openapi.field2property(category_3) == {
-            "items": {"$ref": r_path + "Category"},
+            "items": build_ref(spec_fixture.spec, "schema", "Category"),
             "type": "array",
         }
         assert spec_fixture.openapi.field2property(category_4) == {
-            "items": {"$ref": r_path + "Category"},
+            "items": build_ref(spec_fixture.spec, "schema", "Category"),
             "readOnly": True,
             "type": "array",
         }
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 9502bb8..d4f3f86 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -23,3 +23,26 @@ class TestOpenAPIVersion:
         assert ver.patch == 1
         assert ver.vstring == "3.0.1"
         assert str(ver) == "3.0.1"
+
+
+def test_build_reference():
+    assert utils.build_reference("schema", 2, "Test") == {"$ref": "#/definitions/Test"}
+    assert utils.build_reference("parameter", 2, "Test") == {
+        "$ref": "#/parameters/Test"
+    }
+    assert utils.build_reference("response", 2, "Test") == {"$ref": "#/responses/Test"}
+    assert utils.build_reference("security_scheme", 2, "Test") == {
+        "$ref": "#/securityDefinitions/Test"
+    }
+    assert utils.build_reference("schema", 3, "Test") == {
+        "$ref": "#/components/schemas/Test"
+    }
+    assert utils.build_reference("parameter", 3, "Test") == {
+        "$ref": "#/components/parameters/Test"
+    }
+    assert utils.build_reference("response", 3, "Test") == {
+        "$ref": "#/components/responses/Test"
+    }
+    assert utils.build_reference("security_scheme", 3, "Test") == {
+        "$ref": "#/components/securitySchemes/Test"
+    }
diff --git a/tests/utils.py b/tests/utils.py
index ce7ccd2..d335588 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -1,6 +1,8 @@
 # -*- coding: utf-8 -*-
 """Utilities to get elements of generated spec"""
 
+from apispec.utils import build_reference
+
 
 def get_schemas(spec):
     if spec.openapi_version.major < 3:
@@ -30,7 +32,5 @@ def get_paths(spec):
     return spec.to_dict()["paths"]
 
 
-def ref_path(spec):
-    if spec.openapi_version.version[0] < 3:
-        return "#/definitions/"
-    return "#/components/schemas/"
+def build_ref(spec, component_type, obj):
+    return build_reference(component_type, spec.openapi_version.major, obj)