Repository: sympy/sympy

Issue:
add function to test for unchanged expression
In #2942 there was a lot of discussion about tests that write `assert foo == foo`.

An idea that I left there was this function which I still think could be used for cases when you want to assert that what you typed remained unchanged:

```python
def unchanged(func, *args):
    """Return True if `func` applied to the `args` is unchanged.

    Examples
    ========

    >>> unchanged(cos, 1)  # instead of assert cos(1) == cos(1)
    True
    """
    f = func(*args)
    return f.func == func and f.args == tuple([sympify(a) for a in args])
```

Discussion:

Patch:
diff --git a/sympy/core/expr.py b/sympy/core/expr.py
index 38bd1d2def..cd76f61a87 100644
--- a/sympy/core/expr.py
+++ b/sympy/core/expr.py
@@ -3537,6 +3537,27 @@ def _n2(a, b):
             return dif
 
 
+def unchanged(func, *args):
+    """Return True if `func` applied to the `args` is unchanged.
+    Can be used instead of `assert foo == foo`.
+
+    Examples
+    ========
+
+    >>> from sympy.core.expr import unchanged
+    >>> from sympy.functions.elementary.trigonometric import cos
+    >>> from sympy.core.numbers import pi
+
+    >>> unchanged(cos, 1)  # instead of assert cos(1) == cos(1)
+    True
+
+    >>> unchanged(cos, pi)
+    False
+    """
+    f = func(*args)
+    return f.func == func and f.args == tuple([sympify(a) for a in args])
+
+
 from .mul import Mul
 from .add import Add
 from .power import Pow


Test patch:
diff --git a/sympy/functions/elementary/tests/test_complexes.py b/sympy/functions/elementary/tests/test_complexes.py
index d328db8c8e..b056820962 100644
--- a/sympy/functions/elementary/tests/test_complexes.py
+++ b/sympy/functions/elementary/tests/test_complexes.py
@@ -5,6 +5,7 @@
     Interval, comp, Integral, Matrix, ImmutableMatrix, SparseMatrix,
     ImmutableSparseMatrix, MatrixSymbol, FunctionMatrix, Lambda, Derivative)
 from sympy.utilities.pytest import XFAIL, raises
+from sympy.core.expr import unchanged
 
 
 def N_equals(a, b):
@@ -32,7 +33,7 @@ def test_re():
     assert re(E) == E
     assert re(-E) == -E
 
-    assert re(x) == re(x)
+    assert unchanged(re, x)
     assert re(x*I) == -im(x)
     assert re(r*I) == 0
     assert re(r) == r
@@ -128,7 +129,7 @@ def test_im():
     assert im(E*I) == E
     assert im(-E*I) == -E
 
-    assert im(x) == im(x)
+    assert unchanged(im, x)
     assert im(x*I) == re(x)
     assert im(r*I) == r
     assert im(r) == 0
diff --git a/sympy/polys/tests/test_rootoftools.py b/sympy/polys/tests/test_rootoftools.py
index 4940947433..a402b4cd9d 100644
--- a/sympy/polys/tests/test_rootoftools.py
+++ b/sympy/polys/tests/test_rootoftools.py
@@ -16,6 +16,7 @@
 )
 
 from sympy.utilities.pytest import raises, slow
+from sympy.core.expr import unchanged
 from sympy.core.compatibility import range
 
 from sympy.abc import a, b, x, y, z, r
@@ -138,11 +139,11 @@ def test_CRootOf___eval_Eq__():
     r1 = rootof(eq, 1)
     assert Eq(r, r1) is S.false
     assert Eq(r, r) is S.true
-    assert Eq(r, x).lhs is r and Eq(r, x).rhs is x
+    assert unchanged(Eq, r, x)
     assert Eq(r, 0) is S.false
     assert Eq(r, S.Infinity) is S.false
     assert Eq(r, I) is S.false
-    assert Eq(r, f(0)).lhs is r and Eq(r, f(0)).rhs is f(0)
+    assert unchanged(Eq, r, f(0))
     sol = solve(eq)
     for s in sol:
         if s.is_real:
@@ -572,4 +573,4 @@ def test_eval_approx_relative():
 def test_issue_15920():
     r = rootof(x**5 - x + 1, 0)
     p = Integral(x, (x, 1, y))
-    assert Eq(r, p).lhs is r and Eq(r, p).rhs is p
+    assert unchanged(Eq, r, p)