Repository: kevin1024/vcrpy
Pull request #459 (2019-07-25 06:13:52)

Improve the cassette failure logs
I'have played a bit with the unreleased version of vcrpy and find out the failure messages where a bit verbose. This PR tries to make thing simple and smoother :)

neozenith: I have a few azure pipelines at work which are running these steps:

```bash
    python -m pip install --upgrade pip
    pip install pipenv
    pipenv --rm
    pipenv install --dev

    # Remove PyPI installed vcrpy that pytest-vcr relies on
    pipenv uninstall vcrpy

    # Install bleeding  edge from master
    pipenv install --dev  git+https://github.com/kevin1024/vcrpy#egg=vcr
    pipenv run pip list -v

```

I will have a bit more time in the next sprint to pay down some tech debt. Let me know how I can help on this PR.
arthurHamon2: @neozenith can you review this ? We should be good after that to write the changelog and do a new release :)
arthurHamon2: @neozenith can you review this ? We should be good after that to write the changelog and do a new release :)

Patch:
diff --git a/vcr/errors.py b/vcr/errors.py
index bdccaca..107c6ae 100644
--- a/vcr/errors.py
+++ b/vcr/errors.py
@@ -5,21 +5,27 @@ class CannotOverwriteExistingCassetteException(Exception):
         message = self._get_message(kwargs["cassette"], kwargs["failed_request"])
         super(CannotOverwriteExistingCassetteException, self).__init__(message)
 
-    def _get_message(self, cassette, failed_request):
+    @staticmethod
+    def _get_message(cassette, failed_request):
         """Get the final message related to the exception"""
         # Get the similar requests in the cassette that
         # have match the most with the request.
         best_matches = cassette.find_requests_with_most_matches(failed_request)
-        # Build a comprehensible message to put in the exception.
-        best_matches_msg = ""
-        for best_match in best_matches:
-            request, _, failed_matchers_assertion_msgs = best_match
-            best_matches_msg += "Similar request found : (%r).\n" % request
-            for failed_matcher, assertion_msg in failed_matchers_assertion_msgs:
-                best_matches_msg += "Matcher failed : %s\n" "%s\n" % (
-                    failed_matcher,
-                    assertion_msg,
-                )
+        if best_matches:
+            # Build a comprehensible message to put in the exception.
+            best_matches_msg = "Found {} similar requests with {} different matcher(s) :\n".format(
+                len(best_matches), len(best_matches[0][2]))
+
+            for idx, best_match in enumerate(best_matches, start=1):
+                request, succeeded_matchers, failed_matchers_assertion_msgs = best_match
+                best_matches_msg += "\n%s - (%r).\n" \
+                                    "Matchers succeeded : %s\n" \
+                                    "Matchers failed :\n" % (idx, request, succeeded_matchers)
+                for failed_matcher, assertion_msg in failed_matchers_assertion_msgs:
+                    best_matches_msg += "%s - assertion failure :\n" \
+                                        "%s\n" % (failed_matcher, assertion_msg)
+        else:
+            best_matches_msg = "No similar requests, that have not been played, found."
         return (
             "Can't overwrite existing cassette (%r) in "
             "your current record mode (%r).\n"
diff --git a/vcr/matchers.py b/vcr/matchers.py
index 8a04f7c..d74202d 100644
--- a/vcr/matchers.py
+++ b/vcr/matchers.py
@@ -135,21 +135,8 @@ def get_matchers_results(r1, r2, matchers):
     return matches_success, matches_fails
 
 
-def get_assertion_message(assertion_details, **format_options):
+def get_assertion_message(assertion_details):
     """
     Get a detailed message about the failing matcher.
     """
-    msg = ""
-    if assertion_details:
-        separator = format_options.get("separator", "-")
-        title = format_options.get("title", " DETAILS ")
-        nb_separator = format_options.get("nb_separator", 40)
-        first_title_line = (
-            separator * ((nb_separator - len(title)) // 2)
-            + title
-            + separator * ((nb_separator - len(title)) // 2)
-        )
-        msg += "{}\n{}\n{}\n".format(
-            first_title_line, str(assertion_details), separator * nb_separator
-        )
-    return msg
+    return assertion_details


Test patch:
diff --git a/tests/unit/test_errors.py b/tests/unit/test_errors.py
new file mode 100644
index 0000000..d4a9e99
--- /dev/null
+++ b/tests/unit/test_errors.py
@@ -0,0 +1,73 @@
+import pytest
+
+from vcr.compat import mock
+from vcr import errors
+from vcr.cassette import Cassette
+
+
+@mock.patch("vcr.cassette.Cassette.find_requests_with_most_matches")
+@pytest.mark.parametrize(
+    "most_matches, expected_message",
+    [
+        # No request match found
+        (
+            [],
+            "No similar requests, that have not been played, found."
+        ),
+        # One matcher failed
+        (
+            [("similar request", ["method", "path"], [("query", "failed : query")])],
+            "Found 1 similar requests with 1 different matcher(s) :\n"
+            "\n1 - ('similar request').\n"
+            "Matchers succeeded : ['method', 'path']\n"
+            "Matchers failed :\n"
+            "query - assertion failure :\n"
+            "failed : query\n"
+
+        ),
+        # Multiple failed matchers
+        (
+            [("similar request", ["method"], [("query", "failed : query"), ("path", "failed : path")])],
+            "Found 1 similar requests with 2 different matcher(s) :\n"
+            "\n1 - ('similar request').\n"
+            "Matchers succeeded : ['method']\n"
+            "Matchers failed :\n"
+            "query - assertion failure :\n"
+            "failed : query\n"
+            "path - assertion failure :\n"
+            "failed : path\n"
+
+        ),
+        # Multiple similar requests
+        (
+            [
+                ("similar request", ["method"], [("query", "failed : query")]),
+                ("similar request 2", ["method"], [("query", "failed : query 2")])
+            ],
+            "Found 2 similar requests with 1 different matcher(s) :\n"
+            "\n1 - ('similar request').\n"
+            "Matchers succeeded : ['method']\n"
+            "Matchers failed :\n"
+            "query - assertion failure :\n"
+            "failed : query\n"
+            "\n2 - ('similar request 2').\n"
+            "Matchers succeeded : ['method']\n"
+            "Matchers failed :\n"
+            "query - assertion failure :\n"
+            "failed : query 2\n"
+
+        ),
+    ]
+)
+def test_CannotOverwriteExistingCassetteException_get_message(
+        mock_find_requests_with_most_matches, most_matches, expected_message):
+    mock_find_requests_with_most_matches.return_value = most_matches
+    cassette = Cassette("path")
+    failed_request = "request"
+    exception_message = errors.CannotOverwriteExistingCassetteException._get_message(
+        cassette, "request"
+    )
+    expected = "Can't overwrite existing cassette (%r) in your current record mode (%r).\n" \
+               "No match for the request (%r) was found.\n" \
+               "%s" % (cassette._path, cassette.record_mode, failed_request, expected_message)
+    assert exception_message == expected
diff --git a/tests/unit/test_matchers.py b/tests/unit/test_matchers.py
index 604b650..33181ae 100644
--- a/tests/unit/test_matchers.py
+++ b/tests/unit/test_matchers.py
@@ -204,17 +204,13 @@ def test_evaluate_matcher_does_not_match_with_assert_message():
 
 
 def test_get_assertion_message():
-    assert matchers.get_assertion_message(None) == ""
+    assert matchers.get_assertion_message(None) is None
     assert matchers.get_assertion_message("") == ""
 
 
 def test_get_assertion_message_with_details():
     assertion_msg = "q1=1 != q2=1"
-    expected = (
-        "--------------- DETAILS ---------------\n"
-        "{}\n"
-        "----------------------------------------\n".format(assertion_msg)
-    )
+    expected = assertion_msg
     assert matchers.get_assertion_message(assertion_msg) == expected