diff --git a/internal/check/api.go b/internal/check/api.go
index 1c264f2..fb0b626 100644
--- a/internal/check/api.go
+++ b/internal/check/api.go
@@ -74,6 +74,9 @@ func (bldr *OutputBuilder) ReportIssue(msg string, opts ...ReportIssueOpt) *Outp
 }
 
 func (bldr *OutputBuilder) Output() Output {
+	if bldr == nil {
+		return Output{}
+	}
 	return Output{Issues: bldr.issues}
 }
 
diff --git a/internal/check/api_test.go b/internal/check/api_test.go
new file mode 100644
index 0000000..a955084
--- /dev/null
+++ b/internal/check/api_test.go
@@ -0,0 +1,27 @@
+package check_test
+
+import (
+	"testing"
+
+	"github.com/mszostok/codeowners-validator/internal/check"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestAPIBuilder(t *testing.T) {
+	var bldr *check.OutputBuilder = nil
+
+	t.Run("Does not panic on ReportIssue when builder is nil", func(t *testing.T) {
+		assert.NotPanics(t, func() {
+			issue := bldr.ReportIssue("test")
+			assert.Nil(t, issue)
+		})
+	})
+
+	t.Run("Does not panic on Output when builder is nil", func(t *testing.T) {
+		assert.NotPanics(t, func() {
+			out := bldr.Output()
+			assert.Empty(t, out)
+		})
+	})
+}
diff --git a/internal/check/file_exists_test.go b/internal/check/file_exists_test.go
index 9b9201e..71aab3c 100644
--- a/internal/check/file_exists_test.go
+++ b/internal/check/file_exists_test.go
@@ -180,6 +180,31 @@ func TestFileExists(t *testing.T) {
 	}
 }
 
+func TestFileExistCheckFileSystemFailure(t *testing.T) {
+	// given
+	tmpdir, err := ioutil.TempDir("", "file-checker")
+	require.NoError(t, err)
+	defer func() {
+		assert.NoError(t, os.RemoveAll(tmpdir))
+	}()
+
+	err = os.MkdirAll(filepath.Join(tmpdir, "foo"), 0222)
+	require.NoError(t, err)
+
+	in := loadInput("* @pico")
+	in.RepoDir = tmpdir
+
+	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Millisecond)
+	defer cancel()
+
+	// when
+	out, err := check.NewFileExist().Check(ctx, in)
+
+	// then
+	require.Error(t, err)
+	assert.Empty(t, out)
+}
+
 func newErrIssue(msg string) check.Issue {
 	return check.Issue{
 		Severity: check.Error,
diff --git a/internal/check/package_test.go b/internal/check/package_test.go
new file mode 100644
index 0000000..bb3a927
--- /dev/null
+++ b/internal/check/package_test.go
@@ -0,0 +1,43 @@
+package check_test
+
+import (
+	"context"
+	"errors"
+	"testing"
+
+	"github.com/mszostok/codeowners-validator/internal/check"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestRespectingCanceledContext(t *testing.T) {
+	must := func(checker check.Checker, err error) check.Checker {
+		require.NoError(t, err)
+		return checker
+	}
+
+	checkers := []check.Checker{
+		check.NewDuplicatedPattern(),
+		check.NewFileExist(),
+		check.NewValidSyntax(),
+		check.NewNotOwnedFile(check.NotOwnedFileConfig{}),
+		must(check.NewValidOwner(check.ValidOwnerConfig{Repository: "org/repo"}, nil)),
+	}
+
+	for _, checker := range checkers {
+		sut := checker
+		t.Run(checker.Name(), func(t *testing.T) {
+			// given: canceled context
+			ctx, cancel := context.WithCancel(context.Background())
+			cancel()
+
+			// when
+			out, err := sut.Check(ctx, loadInput(validCODEOWNERS))
+
+			// then
+			assert.True(t, errors.Is(err, context.Canceled))
+			assert.Empty(t, out)
+		})
+	}
+}
diff --git a/internal/printer/testdata/TestTTYPrinterPrintCheckResult/Should_print_OK_status_on_empty_issues_list.golden.txt b/internal/printer/testdata/TestTTYPrinterPrintCheckResult/Should_print_OK_status_on_empty_issues_list.golden.txt
new file mode 100644
index 0000000..e401d95
--- /dev/null
+++ b/internal/printer/testdata/TestTTYPrinterPrintCheckResult/Should_print_OK_status_on_empty_issues_list.golden.txt
@@ -0,0 +1,2 @@
+==> Executing Foo Checker (1s)
+    Check OK
diff --git a/internal/printer/testdata/TestTTYPrinterPrintCheckResult/Should_print_all_reported_issues.golden.txt b/internal/printer/testdata/TestTTYPrinterPrintCheckResult/Should_print_all_reported_issues.golden.txt
new file mode 100644
index 0000000..a1dd6b4
--- /dev/null
+++ b/internal/printer/testdata/TestTTYPrinterPrintCheckResult/Should_print_all_reported_issues.golden.txt
@@ -0,0 +1,5 @@
+==> Executing Foo Checker (1s)
+    [err] line 42: Simulate error in line 42
+    [war] line 2020: Simulate warning in line 2020
+    [err] Error without line number
+    [war] Warning without line number
diff --git a/internal/printer/testdata/TestTTYPrinterPrintSummary/Should_print_'no'_when_there_is_no_failures.golden.txt b/internal/printer/testdata/TestTTYPrinterPrintSummary/Should_print_'no'_when_there_is_no_failures.golden.txt
new file mode 100644
index 0000000..67b2f99
--- /dev/null
+++ b/internal/printer/testdata/TestTTYPrinterPrintSummary/Should_print_'no'_when_there_is_no_failures.golden.txt
@@ -0,0 +1,2 @@
+
+20 check(s) executed, no failure(s)
diff --git a/internal/printer/testdata/TestTTYPrinterPrintSummary/Should_print_number_of_failures.golden.txt b/internal/printer/testdata/TestTTYPrinterPrintSummary/Should_print_number_of_failures.golden.txt
new file mode 100644
index 0000000..95ffd2c
--- /dev/null
+++ b/internal/printer/testdata/TestTTYPrinterPrintSummary/Should_print_number_of_failures.golden.txt
@@ -0,0 +1,2 @@
+
+20 check(s) executed, 10 failure(s)
diff --git a/internal/printer/tty.go b/internal/printer/tty.go
index 07d14b4..fa4a35f 100644
--- a/internal/printer/tty.go
+++ b/internal/printer/tty.go
@@ -2,6 +2,8 @@ package printer
 
 import (
 	"fmt"
+	"io"
+	"os"
 	"strings"
 	"sync"
 	"time"
@@ -10,6 +12,9 @@ import (
 	"github.com/mszostok/codeowners-validator/internal/check"
 )
 
+// writer used for test purpose
+var writer io.Writer = os.Stdout
+
 type TTYPrinter struct {
 	m sync.RWMutex
 }
@@ -18,27 +23,27 @@ func (tty *TTYPrinter) PrintCheckResult(checkName string, duration time.Duration
 	tty.m.Lock()
 	defer tty.m.Unlock()
 
-	header := color.New(color.Bold).PrintfFunc()
-	issueBody := color.New(color.FgWhite).PrintfFunc()
-	okCheck := color.New(color.FgGreen).PrintlnFunc()
+	header := color.New(color.Bold).FprintfFunc()
+	issueBody := color.New(color.FgWhite).FprintfFunc()
+	okCheck := color.New(color.FgGreen).FprintlnFunc()
 
-	header("==> Executing %s (%v)\n", checkName, duration)
+	header(writer, "==> Executing %s (%v)\n", checkName, duration)
 	for _, i := range checkOut.Issues {
 		issueSeverity := tty.severityPrintfFunc(i.Severity)
 
-		issueSeverity("    [%s]", strings.ToLower(i.Severity.String()[:3]))
+		issueSeverity(writer, "    [%s]", strings.ToLower(i.Severity.String()[:3]))
 		if i.LineNo != nil {
-			issueBody(" line %d:", *i.LineNo)
+			issueBody(writer, " line %d:", *i.LineNo)
 		}
-		issueBody(" %s\n", i.Message)
+		issueBody(writer, " %s\n", i.Message)
 	}
 
 	if len(checkOut.Issues) == 0 {
-		okCheck("    Check OK")
+		okCheck(writer, "    Check OK")
 	}
 }
 
-func (*TTYPrinter) severityPrintfFunc(severity check.SeverityType) func(format string, a ...interface{}) {
+func (*TTYPrinter) severityPrintfFunc(severity check.SeverityType) func(w io.Writer, format string, a ...interface{}) {
 	p := color.New()
 	switch severity {
 	case check.Warning:
@@ -47,7 +52,7 @@ func (*TTYPrinter) severityPrintfFunc(severity check.SeverityType) func(format s
 		p.Add(color.FgRed)
 	}
 
-	return p.PrintfFunc()
+	return p.FprintfFunc()
 }
 
 func (*TTYPrinter) PrintSummary(allCheck, failedChecks int) {
@@ -55,5 +60,5 @@ func (*TTYPrinter) PrintSummary(allCheck, failedChecks int) {
 	if failedChecks > 0 {
 		failures = fmt.Sprintf("%d", failedChecks)
 	}
-	fmt.Printf("\n%d check(s) executed, %s failure(s)\n", allCheck, failures)
+	fmt.Fprintf(writer, "\n%d check(s) executed, %s failure(s)\n", allCheck, failures)
 }
diff --git a/internal/printer/tty_test.go b/internal/printer/tty_test.go
new file mode 100644
index 0000000..e9f57d8
--- /dev/null
+++ b/internal/printer/tty_test.go
@@ -0,0 +1,110 @@
+package printer
+
+import (
+	"bytes"
+	"io"
+	"testing"
+	"time"
+
+	"github.com/mszostok/codeowners-validator/internal/check"
+	"github.com/mszostok/codeowners-validator/internal/ptr"
+
+	"github.com/sebdah/goldie/v2"
+)
+
+func TestTTYPrinterPrintCheckResult(t *testing.T) {
+	t.Run("Should print all reported issues", func(t *testing.T) {
+		// given
+		tty := TTYPrinter{}
+
+		buff := &bytes.Buffer{}
+		restore := overrideWriter(buff)
+		defer restore()
+
+		// when
+		tty.PrintCheckResult("Foo Checker", time.Second, check.Output{
+			Issues: []check.Issue{
+				{
+					Severity: check.Error,
+					LineNo:   ptr.Uint64Ptr(42),
+					Message:  "Simulate error in line 42",
+				},
+				{
+					Severity: check.Warning,
+					LineNo:   ptr.Uint64Ptr(2020),
+					Message:  "Simulate warning in line 2020",
+				},
+				{
+					Severity: check.Error,
+					Message:  "Error without line number",
+				},
+				{
+					Severity: check.Warning,
+					Message:  "Warning without line number",
+				},
+			},
+		})
+
+		// then
+		g := goldie.New(t, goldie.WithNameSuffix(".golden.txt"))
+		g.Assert(t, t.Name(), buff.Bytes())
+	})
+
+	t.Run("Should print OK status on empty issues list", func(t *testing.T) {
+		// given
+		tty := TTYPrinter{}
+
+		buff := &bytes.Buffer{}
+		restore := overrideWriter(buff)
+		defer restore()
+
+		// when
+		tty.PrintCheckResult("Foo Checker", time.Second, check.Output{
+			Issues: nil,
+		})
+
+		// then
+		g := goldie.New(t, goldie.WithNameSuffix(".golden.txt"))
+		g.Assert(t, t.Name(), buff.Bytes())
+	})
+}
+
+func TestTTYPrinterPrintSummary(t *testing.T) {
+	t.Run("Should print number of failures", func(t *testing.T) {
+		// given
+		tty := TTYPrinter{}
+
+		buff := &bytes.Buffer{}
+		restore := overrideWriter(buff)
+		defer restore()
+
+		// when
+		tty.PrintSummary(20, 10)
+
+		// then
+		g := goldie.New(t, goldie.WithNameSuffix(".golden.txt"))
+		g.Assert(t, t.Name(), buff.Bytes())
+	})
+
+	t.Run("Should print 'no' when there is no failures", func(t *testing.T) {
+		// given
+		tty := TTYPrinter{}
+
+		buff := &bytes.Buffer{}
+		restore := overrideWriter(buff)
+		defer restore()
+
+		// when
+		tty.PrintSummary(20, 0)
+
+		// then
+		g := goldie.New(t, goldie.WithNameSuffix(".golden.txt"))
+		g.Assert(t, t.Name(), buff.Bytes())
+	})
+}
+
+func overrideWriter(in io.Writer) func() {
+	old := writer
+	writer = in
+	return func() { writer = old }
+}