diff --git a/AUTHORS b/AUTHORS
index b09516a1dd0..2bc304981c8 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -13,6 +13,7 @@ Andrzej Ostrowski
 Andy Freeland
 Anthon van der Neut
 Antony Lee
+Anthony Sottile
 Armin Rigo
 Aron Curzon
 Aviv Palivoda
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index a0706243787..fe78210854c 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,10 @@
 3.0.7 (unreleased)
 ==================
 
+* Fix ``AttributeError`` on ``sys.stdout.buffer`` / ``sys.stderr.buffer``
+  while using ``capsys`` fixture in python 3. (`#1407`_).
+  Thanks to `@asottile`_.
+
 * Fix regression, pytest now skips unittest correctly if run with ``--pdb``
   (`#2137`_). Thanks to `@gst`_ for the report and `@mbyt`_ for the PR.
 
@@ -32,6 +36,7 @@
 .. _@sirex: https://github.com/sirex
 .. _@vidartf: https://github.com/vidartf
 
+.. _#1407: https://github.com/pytest-dev/pytest/issues/1407
 .. _#2137: https://github.com/pytest-dev/pytest/issues/2137
 .. _#2160: https://github.com/pytest-dev/pytest/issues/2160
 .. _#2231: https://github.com/pytest-dev/pytest/issues/2231
diff --git a/_pytest/capture.py b/_pytest/capture.py
index eea81ca187d..9b0e35ac7da 100644
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -11,8 +11,8 @@
 
 import py
 import pytest
+from _pytest.compat import CaptureIO
 
-from py.io import TextIO
 unicode = py.builtin.text
 
 patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'}
@@ -402,7 +402,7 @@ def __init__(self, fd, tmpfile=None):
             if name == "stdin":
                 tmpfile = DontReadFromInput()
             else:
-                tmpfile = TextIO()
+                tmpfile = CaptureIO()
         self.tmpfile = tmpfile
 
     def start(self):
diff --git a/_pytest/compat.py b/_pytest/compat.py
index d278b89cd44..a4022f8d094 100644
--- a/_pytest/compat.py
+++ b/_pytest/compat.py
@@ -239,3 +239,19 @@ def safe_str(v):
         except UnicodeError:
             errors = 'replace'
             return v.encode('ascii', errors)
+
+
+if _PY2:
+    from py.io import TextIO as CaptureIO
+else:
+    import io
+
+    class CaptureIO(io.TextIOWrapper):
+        def __init__(self):
+            super(CaptureIO, self).__init__(
+                io.BytesIO(),
+                encoding='UTF-8', newline='', write_through=True,
+            )
+
+        def getvalue(self):
+            return self.buffer.getvalue().decode('UTF-8')
diff --git a/_pytest/pytester.py b/_pytest/pytester.py
index de24f904442..017e6631137 100644
--- a/_pytest/pytester.py
+++ b/_pytest/pytester.py
@@ -12,6 +12,7 @@
 
 from py.builtin import print_
 
+from _pytest.capture import MultiCapture, SysCapture
 from _pytest._code import Source
 import py
 import pytest
@@ -734,7 +735,8 @@ def runpytest_inprocess(self, *args, **kwargs):
         if kwargs.get("syspathinsert"):
             self.syspathinsert()
         now = time.time()
-        capture = py.io.StdCapture()
+        capture = MultiCapture(Capture=SysCapture)
+        capture.start_capturing()
         try:
             try:
                 reprec = self.inline_run(*args, **kwargs)
@@ -749,7 +751,8 @@ class reprec:
                 class reprec:
                     ret = 3
         finally:
-            out, err = capture.reset()
+            out, err = capture.readouterr()
+            capture.stop_capturing()
             sys.stdout.write(out)
             sys.stderr.write(err)
 
diff --git a/testing/test_capture.py b/testing/test_capture.py
index 763e28315df..b7375775385 100644
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -281,7 +281,7 @@ def test_logging_stream_ownership(self, testdir):
             def test_logging():
                 import logging
                 import pytest
-                stream = capture.TextIO()
+                stream = capture.CaptureIO()
                 logging.basicConfig(stream=stream)
                 stream.close() # to free memory/release resources
         """)
@@ -622,16 +622,16 @@ def bad_snap(self):
     ])
 
 
-class TestTextIO:
+class TestCaptureIO:
     def test_text(self):
-        f = capture.TextIO()
+        f = capture.CaptureIO()
         f.write("hello")
         s = f.getvalue()
         assert s == "hello"
         f.close()
 
     def test_unicode_and_str_mixture(self):
-        f = capture.TextIO()
+        f = capture.CaptureIO()
         if sys.version_info >= (3, 0):
             f.write("\u00f6")
             pytest.raises(TypeError, "f.write(bytes('hello', 'UTF-8'))")
@@ -642,6 +642,13 @@ def test_unicode_and_str_mixture(self):
             f.close()
             assert isinstance(s, unicode)
 
+    def test_write_bytes_to_buffer(self):
+        f = capture.CaptureIO()
+        # In python3, stdout / stderr are text io wrappers (exposing a buffer
+        # property of the underlying bytestream).
+        getattr(f, 'buffer', f).write(b'foo\n')
+        assert f.getvalue() == 'foo\n'
+
 
 def test_bytes_io():
     f = py.io.BytesIO()
@@ -900,8 +907,8 @@ def test_capturing_modify_sysouterr_in_between(self):
         with self.getcapture() as cap:
             sys.stdout.write("hello")
             sys.stderr.write("world")
-            sys.stdout = capture.TextIO()
-            sys.stderr = capture.TextIO()
+            sys.stdout = capture.CaptureIO()
+            sys.stderr = capture.CaptureIO()
             print ("not seen")
             sys.stderr.write("not seen\n")
             out, err = cap.readouterr()