diff --git a/test/boostLocale/test/unit_test.hpp b/test/boostLocale/test/unit_test.hpp
index 81db1829..cbfc8cd4 100644
--- a/test/boostLocale/test/unit_test.hpp
+++ b/test/boostLocale/test/unit_test.hpp
@@ -10,6 +10,7 @@
 #define BOOST_LOCALE_UNIT_TEST_HPP
 
 #include <boost/locale/config.hpp>
+#include <boost/config/helper_macros.hpp>
 #include <cstdlib>
 #include <iomanip>
 #include <iostream>
@@ -23,12 +24,20 @@
 #    include <crtdbg.h>
 #endif
 
+#ifndef BOOST_LOCALE_ERROR_LIMIT
+#    define BOOST_LOCALE_ERROR_LIMIT 20
+#endif
+
+#define BOOST_LOCALE_STRINGIZE(x) #x
+
 namespace boost { namespace locale { namespace test {
     /// Name/path of current executable
     std::string exe_name;
 
+    class test_context;
+
     struct test_result {
-        test_result() : error_counter(0), test_counter(0)
+        test_result()
         {
 #if defined(_MSC_VER) && (_MSC_VER > 1310)
             // disable message boxes on assert(), abort()
@@ -40,60 +49,85 @@ namespace boost { namespace locale { namespace test {
             _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
 #endif
         }
-        int error_counter;
-        int test_counter;
+        int error_counter = 0;
+        int test_counter = 0;
+        const test_context* context = nullptr;
     };
     inline test_result& results()
     {
         static test_result instance;
         return instance;
     }
-}}} // namespace boost::locale::test
 
-#ifndef BOOST_LOCALE_ERROR_LIMIT
-#    define BOOST_LOCALE_ERROR_LIMIT 20
-#endif
+    class test_context {
+        const test_context* oldCtx_;
+        const std::string msg_;
 
-#define BOOST_LOCALE_STRINGIZE(x) #x
+    public:
+        test_context(std::string ctx) : oldCtx_(results().context), msg_(std::move(ctx)) { results().context = this; }
+        ~test_context() { results().context = oldCtx_; }
+        friend std::ostream& operator<<(std::ostream& os, const test_context& c)
+        {
+            const test_context* current = &c;
+            os << "CONTEXT: ";
+            std::string indent = "\n\t";
+            do {
+                os << indent << current->msg_;
+                indent += '\t';
+            } while((current = current->oldCtx_) != nullptr);
+            return os;
+        }
+    };
 
-#define THROW_IF_TOO_BIG(X)            \
-    if((X) > BOOST_LOCALE_ERROR_LIMIT) \
-    throw std::runtime_error("Error limits reached, stopping unit test")
-
-#define TEST(X)                                                           \
-    do {                                                                  \
-        boost::locale::test::results().test_counter++;                    \
-        if(X)                                                             \
-            break;                                                        \
-        std::cerr << "Error in line:" << __LINE__ << " " #X << std::endl; \
-        THROW_IF_TOO_BIG(boost::locale::test::results().error_counter++); \
-        BOOST_LOCALE_START_CONST_CONDITION                                \
+    inline void report_error(const char* expr, const char* file, int line)
+    {
+        std::cerr << "Error at " << file << '#' << line << ": " << expr << std::endl;
+        const auto* context = results().context;
+        if(context)
+            std::cerr << ' ' << *context << std::endl;
+        if(++boost::locale::test::results().error_counter > BOOST_LOCALE_ERROR_LIMIT)
+            throw std::runtime_error("Error limits reached, stopping unit test");
+    }
+}}} // namespace boost::locale::test
+
+#define BOOST_LOCALE_TEST_REPORT_ERROR(expr) boost::locale::test::report_error(expr, __FILE__, __LINE__)
+
+#define TEST(X)                                        \
+    do {                                               \
+        boost::locale::test::results().test_counter++; \
+        if(X)                                          \
+            break;                                     \
+        BOOST_LOCALE_TEST_REPORT_ERROR(#X);            \
+        BOOST_LOCALE_START_CONST_CONDITION             \
     } while(0) BOOST_LOCALE_END_CONST_CONDITION
 
-#define TEST_REQUIRE(X)                                                    \
-    do {                                                                   \
-        boost::locale::test::results().test_counter++;                     \
-        if(X)                                                              \
-            break;                                                         \
-        std::cerr << "Error in line " << __LINE__ << ": " #X << std::endl; \
-        throw std::runtime_error("Critical test " #X " failed");           \
-        BOOST_LOCALE_START_CONST_CONDITION                                 \
+#define TEST_REQUIRE(X)                                          \
+    do {                                                         \
+        boost::locale::test::results().test_counter++;           \
+        if(X)                                                    \
+            break;                                               \
+        BOOST_LOCALE_TEST_REPORT_ERROR(#X);                      \
+        throw std::runtime_error("Critical test " #X " failed"); \
+        BOOST_LOCALE_START_CONST_CONDITION                       \
     } while(0) BOOST_LOCALE_END_CONST_CONDITION
 
-#define TEST_THROWS(X, E)                                                  \
-    do {                                                                   \
-        boost::locale::test::results().test_counter++;                     \
-        try {                                                              \
-            X;                                                             \
-        } catch(E const& /*e*/) {                                          \
-            break;                                                         \
-        } catch(...) {                                                     \
-        }                                                                  \
-        std::cerr << "Error in line " << __LINE__ << ": " #X << std::endl; \
-        THROW_IF_TOO_BIG(boost::locale::test::results().error_counter++);  \
-        BOOST_LOCALE_START_CONST_CONDITION                                 \
+#define TEST_THROWS(X, E)                              \
+    do {                                               \
+        boost::locale::test::results().test_counter++; \
+        try {                                          \
+            X;                                         \
+        } catch(E const& /*e*/) {                      \
+            break;                                     \
+        } catch(...) {                                 \
+        }                                              \
+        BOOST_LOCALE_TEST_REPORT_ERROR(#X);            \
+        BOOST_LOCALE_START_CONST_CONDITION             \
     } while(0) BOOST_LOCALE_END_CONST_CONDITION
 
+#define TEST_CONTEXT(expr)                                                    \
+    boost::locale::test::test_context BOOST_JOIN(test_context_, __COUNTER__)( \
+      static_cast<const std::stringstream&>(std::stringstream{} << expr).str())
+
 void test_main(int argc, char** argv);
 
 int main(int argc, char** argv)
@@ -138,6 +172,11 @@ const std::string& to_string(const std::string& s)
     return s;
 }
 
+std::string to_string(std::nullptr_t)
+{
+    return "<nullptr>";
+}
+
 template<typename T>
 std::string to_string(const std::vector<T>& v)
 {
@@ -223,58 +262,52 @@ std::string to_string(const char32_t c)
 #endif
 
 template<typename T, typename U>
-void test_impl(bool success, T const& l, U const& r, const char* expr, const char* fail_expr, int line)
+void test_impl(bool success,
+               T const& l,
+               U const& r,
+               const char* expr,
+               const char* fail_expr,
+               const char* file,
+               int line)
 {
     boost::locale::test::results().test_counter++;
     if(!success) {
-        std::cerr << "Error in line " << line << ": " << expr << std::endl;
-        std::cerr << "---- [" << to_string(l) << "] " << fail_expr << " [" << to_string(r) << "]" << std::endl;
-        THROW_IF_TOO_BIG(boost::locale::test::results().error_counter++);
+        if(fail_expr) {
+            std::ostringstream s;
+            s << expr << '\n' << "---- [" << to_string(l) << "] " << fail_expr << " [" << to_string(r) << "]";
+            boost::locale::test::report_error(s.str().c_str(), file, line);
+        } else
+            boost::locale::test::report_error(expr, file, line);
     }
 }
 
-template<typename T, typename U>
-void test_eq_impl(T const& l, U const& r, const char* expr, int line)
-{
-    test_impl(l == r, l, r, expr, "!=", line);
-}
-
-template<typename T, typename U>
-void test_ne_impl(T const& l, U const& r, const char* expr, int line)
-{
-    test_impl(l != r, l, r, expr, "==", line);
-}
-
-template<typename T, typename U>
-void test_le_impl(T const& l, U const& r, const char* expr, int line)
-{
-    test_impl(l <= r, l, r, expr, ">", line);
-}
-
-template<typename T, typename U>
-void test_lt_impl(T const& l, U const& r, const char* expr, int line)
+void test_impl(bool success, const char* reason, const char* file, int line)
 {
-    test_impl(l < r, l, r, expr, ">=", line);
+    test_impl(success, nullptr, nullptr, reason, nullptr, file, line);
 }
 
-template<typename T, typename U>
-void test_ge_impl(T const& l, U const& r, const char* expr, int line)
-{
-    test_impl(l >= r, l, r, expr, "<", line);
-}
-
-template<typename T, typename U>
-void test_gt_impl(T const& l, U const& r, const char* expr, int line)
-{
-    test_impl(l > r, l, r, expr, "<=", line);
-}
+#define BOOST_LOCALE_TEST_OP_IMPL(name, test_op, fail_op)                                         \
+    template<typename T, typename U>                                                              \
+    void test_##name##_impl(T const& l, U const& r, const char* expr, const char* file, int line) \
+    {                                                                                             \
+        test_impl(l test_op r, l, r, expr, #fail_op, file, line);                                 \
+    }
 
-#define TEST_EQ(x, y) test_eq_impl(x, y, BOOST_LOCALE_STRINGIZE(x == y), __LINE__)
-#define TEST_NE(x, y) test_ne_impl(x, y, BOOST_LOCALE_STRINGIZE(x != y), __LINE__)
-#define TEST_LE(x, y) test_le_impl(x, y, BOOST_LOCALE_STRINGIZE(x <= y), __LINE__)
-#define TEST_LT(x, y) test_lt_impl(x, y, BOOST_LOCALE_STRINGIZE(x < y), __LINE__)
-#define TEST_GE(x, y) test_ge_impl(x, y, BOOST_LOCALE_STRINGIZE(x >= y), __LINE__)
-#define TEST_GT(x, y) test_gt_impl(x, y, BOOST_LOCALE_STRINGIZE(x > y), __LINE__)
+BOOST_LOCALE_TEST_OP_IMPL(eq, ==, !=)
+BOOST_LOCALE_TEST_OP_IMPL(ne, !=, ==)
+BOOST_LOCALE_TEST_OP_IMPL(le, <=, >)
+BOOST_LOCALE_TEST_OP_IMPL(lt, <, >=)
+BOOST_LOCALE_TEST_OP_IMPL(ge, >=, <)
+BOOST_LOCALE_TEST_OP_IMPL(gt, >, <=)
+
+#undef BOOST_LOCALE_TEST_OP_IMPL
+
+#define TEST_EQ(x, y) test_eq_impl(x, y, BOOST_LOCALE_STRINGIZE(x == y), __FILE__, __LINE__)
+#define TEST_NE(x, y) test_ne_impl(x, y, BOOST_LOCALE_STRINGIZE(x != y), __FILE__, __LINE__)
+#define TEST_LE(x, y) test_le_impl(x, y, BOOST_LOCALE_STRINGIZE(x <= y), __FILE__, __LINE__)
+#define TEST_LT(x, y) test_lt_impl(x, y, BOOST_LOCALE_STRINGIZE(x < y), __FILE__, __LINE__)
+#define TEST_GE(x, y) test_ge_impl(x, y, BOOST_LOCALE_STRINGIZE(x >= y), __FILE__, __LINE__)
+#define TEST_GT(x, y) test_gt_impl(x, y, BOOST_LOCALE_STRINGIZE(x > y), __FILE__, __LINE__)
 
 #if BOOST_LOCALE_SPACESHIP_NULLPTR_WARNING
 #    pragma clang diagnostic pop
diff --git a/test/test_catalog.cpp b/test/test_catalog.cpp
index 29a34c69..bc06805f 100644
--- a/test/test_catalog.cpp
+++ b/test/test_catalog.cpp
@@ -31,10 +31,8 @@ void test_plural_expr_rand(const T& ref, const char* expr)
         const auto n = getRandValue(minVal, maxVal);
         const auto result = ptr(n);
         const auto refResult = ref(n);
-        if(result != refResult) {
-            std::cerr << "Expression: " << expr << "; n=" << n << '\n'; // LCOV_EXCL_LINE
-            TEST_EQ(result, refResult);                                 // LCOV_EXCL_LINE
-        }
+        TEST_CONTEXT("Expression: " << expr << "; n=" << n);
+        TEST_EQ(result, refResult);
     }
 }
 
@@ -47,7 +45,7 @@ void test_plural_expr()
         TEST(ptr);                \
         return ptr;               \
     }()
-#define TEST_EQ_EXPR(expr, rhs) test_eq_impl(COMPILE_PLURAL_EXPR(expr)(0), rhs, expr, __LINE__)
+#define TEST_EQ_EXPR(expr, rhs) test_eq_impl(COMPILE_PLURAL_EXPR(expr)(0), rhs, expr, __FILE__, __LINE__)
     // Number only
     TEST_EQ_EXPR("0", 0);
     TEST_EQ_EXPR("42", 42);
diff --git a/test/test_codecvt.cpp b/test/test_codecvt.cpp
index 7df704d3..504da439 100644
--- a/test/test_codecvt.cpp
+++ b/test/test_codecvt.cpp
@@ -53,10 +53,8 @@ void test_codecvt_in_n_m(const cvt_type& cvt, int n, int m)
         std::mbstate_t mb2 = mb;
         std::codecvt_base::result r = cvt.in(mb, from, end, from_next, to, to_end, to_next);
 
-        int count = cvt.length(mb2, from, end, to_end - to);
+        const int count = cvt.length(mb2, from, end, to_end - to);
         TEST_EQ(memcmp(&mb, &mb2, sizeof(mb)), 0);
-        if(count != from_next - from)
-            std::cout << count << " " << from_next - from << std::endl; // LCOV_EXCL_LINE
         TEST_EQ(count, from_next - from);
 
         if(r == cvt_type::partial) {
diff --git a/test/test_date_time.cpp b/test/test_date_time.cpp
index b9e6ca0e..38280caf 100644
--- a/test/test_date_time.cpp
+++ b/test/test_date_time.cpp
@@ -30,7 +30,7 @@
 
 #define TEST_EQ_FMT(t, X)    \
     empty_stream(ss) << (t); \
-    test_eq_impl(ss.str(), X, #t "==" #X, __LINE__)
+    test_eq_impl(ss.str(), X, #t "==" #X, __FILE__, __LINE__)
 
 // Very simple container for a part of the tests. Counts its instances
 struct mock_calendar : public boost::locale::abstract_calendar {
diff --git a/test/test_encoding.cpp b/test/test_encoding.cpp
index 4de7e610..7fb07c2f 100644
--- a/test/test_encoding.cpp
+++ b/test/test_encoding.cpp
@@ -832,10 +832,9 @@ void test_simple_encodings()
     const auto encodings = get_simple_encodings();
     for(auto it = encodings.begin(), end = encodings.end(); it != end; ++it) {
         TEST_EQ(normalize_encoding(*it), *it); // Must be normalized
-        const auto it2 = std::find(it + 1, end, *it);
-        TEST(it2 == end);
-        if(it2 != end)
-            std::cerr << "Duplicate entry: " << *it << '\n'; // LCOV_EXCL_LINE
+        TEST_CONTEXT("Entry: " << *it);
+        // Must be unique
+        TEST(std::find(it + 1, end, *it) == end);
     }
     const auto it = std::is_sorted_until(encodings.begin(), encodings.end());
     TEST(it == encodings.end());
@@ -852,10 +851,9 @@ void test_win_codepages()
         auto is_same_win_codepage = [&it](const windows_encoding& rhs) -> bool {
             return it->codepage == rhs.codepage && std::strcmp(it->name, rhs.name) == 0;
         };
-        const auto* it2 = std::find_if(it + 1, end, is_same_win_codepage);
-        TEST(it2 == end);
-        if(it2 != end)
-            std::cerr << "Duplicate entry: " << it->name << ':' << it->codepage << '\n'; // LCOV_EXCL_LINE
+        TEST_CONTEXT("Entry: " << it->name << ':' << it->codepage);
+        // Must be unique
+        TEST(std::find_if(it + 1, end, is_same_win_codepage) == end);
     }
     const auto cmp = [](const windows_encoding& rhs, const windows_encoding& lhs) -> bool { return rhs < lhs.name; };
     const auto* it = std::is_sorted_until(all_windows_encodings, std::end(all_windows_encodings), cmp);
diff --git a/test/test_formatting.cpp b/test/test_formatting.cpp
index d418c583..41c30a0f 100644
--- a/test/test_formatting.cpp
+++ b/test/test_formatting.cpp
@@ -159,17 +159,17 @@ void test_fmt_impl(std::basic_ostringstream<CharType>& ss,
                    const std::basic_string<CharType>& expected,
                    int line)
 {
-    ss << value;
-    test_eq_impl(ss.str(), expected, "", line);
+    test_impl(!!(ss << value), "Formatting failed", __FILE__, line);
+    test_eq_impl(ss.str(), expected, "", __FILE__, line);
 }
 
 template<typename T, typename CharType>
 void test_parse_impl(std::basic_istringstream<CharType>& ss, const T& expected, int line)
 {
     T v;
-    ss >> v >> std::ws;
-    test_eq_impl(v, expected, "v == expected", line);
-    test_eq_impl(ss.eof(), true, "ss.eof()", line);
+    test_impl(!!(ss >> v), "Parsing failed", __FILE__, line);
+    test_eq_impl(v, expected, "v == expected", __FILE__, line);
+    test_eq_impl((ss >> std::ws).eof(), true, "ss.eof()", __FILE__, line);
 }
 
 template<typename T, typename CharType>
@@ -178,8 +178,9 @@ void test_parse_at_impl(std::basic_istringstream<CharType>& ss, const T& expecte
     T v;
     CharType c_at;
     ss >> v >> std::skipws >> c_at;
-    test_eq_impl(v, expected, "v == expected", line);
-    test_eq_impl(c_at, '@', "c_at == @", line);
+    test_impl(!!ss, "Parsing failed", __FILE__, line);
+    test_eq_impl(v, expected, "v == expected", __FILE__, line);
+    test_eq_impl(c_at, '@', "c_at == @", __FILE__, line);
 }
 
 template<typename T, typename CharType>
@@ -187,7 +188,7 @@ void test_parse_fail_impl(std::basic_istringstream<CharType>& ss, int line)
 {
     T v;
     ss >> v;
-    test_eq_impl(ss.fail(), true, "ss.fail()", line);
+    test_eq_impl(ss.fail(), true, "ss.fail()", __FILE__, line);
 }
 
 #define TEST_FMT(manip, value, expected)                                                  \
@@ -299,6 +300,7 @@ void test_parse_fail_impl(std::basic_istringstream<CharType>& ss, int line)
 
 #define TEST_MIN_MAX_POSIX(type)                                                      \
     do {                                                                              \
+        TEST_CONTEXT(#type);                                                          \
         const std::string minval = as_posix_string(std::numeric_limits<type>::min()); \
         const std::string maxval = as_posix_string(std::numeric_limits<type>::max()); \
         TEST_MIN_MAX_FMT(as::posix, type, minval, maxval);                            \
@@ -339,6 +341,7 @@ void test_as_posix(const std::string& e_charset = "UTF-8")
         localization_backend_manager::global(backend);
         for(const std::string name : {"en_US", "ru_RU", "de_DE"}) {
             const std::locale loc = boost::locale::generator{}(name + "." + e_charset);
+            TEST_CONTEXT("Locale " << (name + "." + e_charset));
             TEST_MIN_MAX_POSIX(int16_t);
             TEST_MIN_MAX_POSIX(uint16_t);
 
@@ -641,7 +644,7 @@ void test_format_class_impl(const std::string& fmt_string,
     format_type fmt(std::basic_string<CharType>(fmt_string.begin(), fmt_string.end()));
     fmt % value;
     std::basic_string<CharType> expected_str_loc(to_correct_string<CharType>(expected_str, loc));
-    test_eq_impl(fmt.str(loc), expected_str_loc, ("Format: " + fmt_string).c_str(), line);
+    test_eq_impl(fmt.str(loc), expected_str_loc, ("Format: " + fmt_string).c_str(), __FILE__, line);
 }
 
 template<typename CharType>