diff --git a/test/inst/sync.go b/test/inst/sync.go
new file mode 100644
index 0000000000000000000000000000000000000000..bd35d147ea73efcf80bb56255bcf497d69eede67
--- /dev/null
+++ b/test/inst/sync.go
@@ -0,0 +1,7 @@
+package inst
+
+type Sync struct{}
+
+func (Sync) instruction() {}
+
+var _ Instruction = Sync{}
diff --git a/test/runner.go b/test/runner.go
index 70e1aee7ae296d6263aab34bd251865d94312b58..3ec8071ae23b7bb43855c28c9c7934f77fba6f5f 100644
--- a/test/runner.go
+++ b/test/runner.go
@@ -2,6 +2,7 @@ package test
 
 import (
 	"errors"
+	"fmt"
 	"io"
 
 	"pggat/lib/bouncer/bouncers/v2"
@@ -16,9 +17,6 @@ import (
 type Runner struct {
 	config Config
 	test   Test
-
-	pools   map[string]*pool.Pool
-	control fed.Conn
 }
 
 func MakeRunner(config Config, test Test) Runner {
@@ -28,107 +26,111 @@ func MakeRunner(config Config, test Test) Runner {
 	}
 }
 
-func (T *Runner) setup() error {
-	// get pools ready
-	if T.control != nil {
-		_ = T.control.Close()
-	}
-	var err error
-	T.control, _, err = T.config.Peer.Dial()
-	if err != nil {
-		return err
-	}
-
-	for name, p := range T.pools {
-		delete(T.pools, name)
-		p.Close()
-	}
-	if T.pools == nil {
-		T.pools = make(map[string]*pool.Pool)
-	}
+func (T *Runner) prepare(client *gsql.Client) []Capturer {
+	results := make([]Capturer, len(T.test.Instructions))
 
-	for name, options := range T.config.Modes {
-		opts := options
-		// allowing ps sync would mess up testing
-		opts.ParameterStatusSync = pool.ParameterStatusSyncNone
-		p := pool.NewPool(opts)
-		p.AddRecipe("server", recipe.NewRecipe(
-			recipe.Options{
-				Dialer: T.config.Peer,
-			},
-		))
-		T.pools[name] = p
+	for i, x := range T.test.Instructions {
+		switch v := x.(type) {
+		case inst.SimpleQuery:
+			q := packets.Query(v)
+			client.Do(&results[i], q.IntoPacket())
+		case inst.Sync:
+			client.Do(&results[i], fed.NewPacket(packets.TypeSync))
+		}
 	}
 
-	return nil
+	return results
 }
 
-func (T *Runner) run(pkts ...fed.Packet) error {
-	// expected
-	var expected Capturer
-
-	{
-		var client gsql.Client
-		client.Do(&expected, pkts...)
-		if err := client.Close(); err != nil {
-			return err
-		}
+func (T *Runner) runControl() ([]Capturer, error) {
+	control, _, err := T.config.Peer.Dial()
+	if err != nil {
+		return nil, err
+	}
+	defer func() {
+		_ = control.Close()
+	}()
+
+	var client gsql.Client
+	results := T.prepare(&client)
+	if err = client.Close(); err != nil {
+		return nil, err
+	}
 
-		for {
-			p, err := client.ReadPacket(true)
-			if err != nil {
-				if errors.Is(err, io.EOF) {
-					break
-				}
-				return err
+	for {
+		var p fed.Packet
+		p, err = client.ReadPacket(true)
+		if err != nil {
+			if errors.Is(err, io.EOF) {
+				break
 			}
+			return nil, err
+		}
 
-			clientErr, serverErr := bouncers.Bounce(&client, T.control, p)
-			if clientErr != nil {
-				return clientErr
-			}
-			if serverErr != nil {
-				return serverErr
-			}
+		clientErr, serverErr := bouncers.Bounce(&client, control, p)
+		if clientErr != nil {
+			return nil, clientErr
+		}
+		if serverErr != nil {
+			return nil, serverErr
 		}
 	}
 
-	// actual
-	for name, p := range T.pools {
-		var result Capturer
-
-		var client gsql.Client
-		client.Do(&result, pkts...)
-		if err := client.Close(); err != nil {
-			return err
-		}
+	return results, nil
+}
 
-		if err := p.Serve(&client, nil, [8]byte{}); err != nil && !errors.Is(err, io.EOF) {
-			return err
-		}
+func (T *Runner) runMode(options pool.Options) ([]Capturer, error) {
+	opts := options
+	// allowing ps sync would mess up testing
+	opts.ParameterStatusSync = pool.ParameterStatusSyncNone
+	p := pool.NewPool(opts)
+	defer p.Close()
+	p.AddRecipe("server", recipe.NewRecipe(
+		recipe.Options{
+			Dialer: T.config.Peer,
+		},
+	))
+
+	var client gsql.Client
+	results := T.prepare(&client)
+	if err := client.Close(); err != nil {
+		return nil, err
+	}
 
-		if err := expected.Check(&result); err != nil {
-			return err
-		}
-		_ = name
+	if err := p.Serve(&client, nil, [8]byte{}); err != nil && !errors.Is(err, io.EOF) {
+		return nil, err
 	}
 
-	return nil
+	return results, nil
 }
 
 func (T *Runner) Run() error {
-	if err := T.setup(); err != nil {
+	// control
+	expected, err := T.runControl()
+	if err != nil {
 		return err
 	}
 
-	for _, i := range T.test.Instructions {
-		switch v := i.(type) {
-		case inst.SimpleQuery:
-			q := packets.Query(v)
-			if err := T.run(q.IntoPacket()); err != nil {
+	// modes
+	for name, mode := range T.config.Modes {
+		actual, err := T.runMode(mode)
+		if err != nil {
+			return err
+		}
+
+		if len(expected) != len(actual) {
+			return fmt.Errorf("wrong number of results! expected %d but got %d", len(expected), len(actual))
+		}
+
+		for i, exp := range expected {
+			act := actual[i]
+
+			if err = exp.Check(&act); err != nil {
 				return err
 			}
 		}
+
+		_ = name
 	}
 
 	return nil
diff --git a/test/tester_test.go b/test/tester_test.go
index 809190fcf2d16b4250dfbd89c75815b858bcaefc..a558e62a5049d3e828c27e81cc0646ac22835d0a 100644
--- a/test/tester_test.go
+++ b/test/tester_test.go
@@ -35,6 +35,8 @@ func TestTester(t *testing.T) {
 	})
 	if err := tester.Run(
 		tests.SimpleQuery,
+		tests.Transaction,
+		tests.Sync,
 	); err != nil {
 		t.Error(err)
 	}
diff --git a/test/tests/sync.go b/test/tests/sync.go
new file mode 100644
index 0000000000000000000000000000000000000000..976ee8e194186581a6a55dbecd85acf6b02922a5
--- /dev/null
+++ b/test/tests/sync.go
@@ -0,0 +1,15 @@
+package tests
+
+import (
+	"pggat/test"
+	"pggat/test/inst"
+)
+
+var Sync = test.Test{
+	Instructions: []inst.Instruction{
+		inst.Sync{},
+		inst.SimpleQuery("BEGIN;"),
+		inst.Sync{},
+		inst.SimpleQuery("END;"),
+	},
+}
diff --git a/test/tests/transaction.go b/test/tests/transaction.go
new file mode 100644
index 0000000000000000000000000000000000000000..ad7ca675af41d685ef689f92a6b107dbfb0fe089
--- /dev/null
+++ b/test/tests/transaction.go
@@ -0,0 +1,16 @@
+package tests
+
+import (
+	"pggat/test"
+	"pggat/test/inst"
+)
+
+var Transaction = test.Test{
+	Instructions: []inst.Instruction{
+		inst.SimpleQuery("BEGIN;"),
+		inst.SimpleQuery("select 1;"),
+		inst.SimpleQuery("this will fail;"),
+		inst.SimpleQuery("select 2;"),
+		inst.SimpleQuery("END;"),
+	},
+}