Repository: grappa-py/grappa

Issue:
Checking for error/exception messages
Hello, 

I'm new to grappa. I'm trying to figure out how to test the contents of an Error/Exception.

Typically,

```python
import pytest

def method():
    raise RuntimeError('message')

def test_method():
    with pytest.raises(RuntimeError, match='message'):
        method()
```

How can that be achieved with Grappa? I can't find the missing step in documentation.

Thanks.



Comments:

Patch:
diff --git a/grappa/operators/raises.py b/grappa/operators/raises.py
index 763f2a5..a60f91d 100644
--- a/grappa/operators/raises.py
+++ b/grappa/operators/raises.py
@@ -55,6 +55,14 @@ class RaisesOperator(Operator):
         'an object of type "{type}" with reference "{value}"',
     )
 
+    def after_success(self, obj, *keys):
+        message = getattr(self.value, 'message', None)
+
+        if not message:
+            message = ' '.join([str(item) for item in self.value.args])
+
+        self.ctx.subject = message
+
     def match(self, fn, *errors):
         if not callable(fn):
             return False, ['subject must be a function or method']
@@ -64,11 +72,6 @@ class RaisesOperator(Operator):
         except Exception as err:
             self.value = err
 
-            # If no errors present, just raise the exception
-            if not errors:
-                return True, []
-
-            # Otherwise match errors
             return isinstance(err, *errors), ['invalid raised exception']
         else:
             return False, ['did not raise any exception']


Test patch:
diff --git a/tests/operators/raises_test.py b/tests/operators/raises_test.py
index a3acdfa..cbdf978 100644
--- a/tests/operators/raises_test.py
+++ b/tests/operators/raises_test.py
@@ -3,12 +3,16 @@ from functools import partial
 
 
 def test_raises(should):
+    def no_error():
+        pass
+
     def error():
         raise AssertionError('foo')
 
     def error_with_params(foo_param):
         raise AssertionError(foo_param)
 
+    error | should.raise_error(Exception)
     error | should.raise_error(AssertionError)
     error | should.do_not.raise_error(NotImplementedError)
 
@@ -24,3 +28,47 @@ def test_raises(should):
 
     with pytest.raises(AssertionError):
         None | should.raise_error(AssertionError)
+
+    with pytest.raises(AssertionError):
+        no_error | should.raise_error(AssertionError)
+
+
+def test_raises_with_message_redirection(should):
+    def error():
+        raise AssertionError('foo')
+
+    def env_error():
+        raise EnvironmentError(3501, 'bar')
+
+    error | should.raise_error(AssertionError) > should.equal('foo')
+
+    error | should.raise_error(AssertionError) > should.contain('fo')
+
+    error | should.do_not.raise_error(NotImplementedError) \
+        > should.equal('foo')
+
+    env_error | should.raise_error(EnvironmentError) > should.contain('bar')
+
+    env_error | should.raise_error(EnvironmentError) > should.equal('3501 bar')
+
+    with pytest.raises(AssertionError):
+        error | should.raise_error(AssertionError) > should.equal('fooe')
+
+    with pytest.raises(AssertionError):
+        error | should.raise_error(NotImplementedError) > should.equal('foo')
+
+
+def test_raises_custom_exception_message_redirection(should):
+    class CustomException(Exception):
+        message = 'foo'
+
+        def __init__(self, *args):
+            super(CustomException, self).__init__(self.message, *args)
+
+    def custom_error():
+        raise CustomException('bar')
+
+    custom_error | should.raise_error(CustomException) > should.equal('foo')
+
+    custom_error | should.raise_error(CustomException) \
+        > should.do_not.equal('foo bar')