diff --git a/.gitignore b/.gitignore
index b95220d..8f8008d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
 bin/
 *.test
 .envrc
+cover.profile
diff --git a/Makefile b/Makefile
index c8523b7..d403904 100644
--- a/Makefile
+++ b/Makefile
@@ -21,6 +21,12 @@ tidy: go.sum
 test_all:
 	$(GINKGO) run -r ./
 
+cover: cover.profile
+	go tool cover -func=$<
+
+cover.profile: $(shell $(DEVCTL) list --go) | bin/ginkgo bin/devctl
+	$(GINKGO) run --coverprofile=cover.profile -r ./
+
 go.sum: go.mod $(shell $(DEVCTL) list --go) | bin/devctl
 	go mod tidy
 
diff --git a/internal/testing/testing.go b/internal/testing/testing.go
new file mode 100644
index 0000000..22ad8b4
--- /dev/null
+++ b/internal/testing/testing.go
@@ -0,0 +1,15 @@
+package testing
+
+import "errors"
+
+type ErrReader string
+
+func (e ErrReader) Read([]byte) (int, error) {
+	return 0, errors.New(string(e))
+}
+
+type ErrWriter string
+
+func (e ErrWriter) Write(p []byte) (int, error) {
+	return 0, errors.New(string(e))
+}
diff --git a/scanner_test.go b/scanner_test.go
index dd0df20..31898ac 100644
--- a/scanner_test.go
+++ b/scanner_test.go
@@ -8,6 +8,7 @@ import (
 	. "github.com/onsi/gomega"
 
 	"github.com/unmango/go-make"
+	"github.com/unmango/go-make/internal/testing"
 	"github.com/unmango/go-make/token"
 )
 
@@ -117,4 +118,11 @@ var _ = Describe("Scanner", func() {
 		Expect(s.Scan()).To(BeTrue())
 		Expect(s.Token()).To(Equal(token.NEWLINE))
 	})
+
+	It("should return IO errors", func() {
+		s := make.NewScanner(testing.ErrReader("io error"))
+
+		Expect(s.Scan()).To(BeFalse())
+		Expect(s.Err()).To(MatchError("io error"))
+	})
 })
diff --git a/write_test.go b/write_test.go
index a79f31f..2433e3d 100644
--- a/write_test.go
+++ b/write_test.go
@@ -7,6 +7,7 @@ import (
 	. "github.com/onsi/gomega"
 
 	"github.com/unmango/go-make"
+	"github.com/unmango/go-make/internal/testing"
 )
 
 var _ = Describe("Write", func() {
@@ -105,4 +106,45 @@ var _ = Describe("Write", func() {
 		Expect(err).NotTo(HaveOccurred())
 		Expect(buf.String()).To(Equal("target:\ntarget2:\n"))
 	})
+
+	It("should write a Makefile", func() {
+		buf := &bytes.Buffer{}
+		w := make.NewWriter(buf)
+
+		_, err := w.WriteMakefile(make.Makefile{
+			Rules: []make.Rule{{
+				Target: []string{"target"},
+			}},
+		})
+
+		Expect(err).NotTo(HaveOccurred())
+	})
+
+	It("should return errors found when writing a Makefile", func() {
+		w := make.NewWriter(testing.ErrWriter("io error"))
+
+		_, err := w.WriteMakefile(make.Makefile{
+			Rules: []make.Rule{{
+				Target: []string{"target"},
+			}},
+		})
+
+		Expect(err).To(MatchError("io error"))
+	})
+
+	It("should return errors found when writing PreReqs", func() {
+		w := make.NewWriter(testing.ErrWriter("io error"))
+
+		_, err := w.WritePreReqs([]string{"blah"})
+
+		Expect(err).To(MatchError("io error"))
+	})
+
+	It("should return errors found when writing Recipes", func() {
+		w := make.NewWriter(testing.ErrWriter("io error"))
+
+		_, err := w.WriteRecipes([]string{"blah"})
+
+		Expect(err).To(MatchError("io error"))
+	})
 })