rafaeljusto/redigomock

Add a check that all registered commands have been executed the desired number of times.

Closed this issue · 3 comments

Example:
func (c *Conn) AssertRequestNumbers(t *testing.T)

I have a solution and I'm ready to do PR.

Hi @mantyr, why not check the number of calls of each command with the Conn.Stats? Also, adding a library dependency with the testing package doesn't appear to be a good practice.

I think about two things:

  1. Programmers often do not do about such verification, Assert helps to avoid this problem
  2. It may be worthwhile to completely switch to tests with run a real database, this is not always possible, but this is one way to improve the quality of the code.

Problems:

  1. Integrate the expected number of starts into the familiar Command
  2. Learn how to write tests for Asserts, what would make a good PR

@mantyr sorry for the delay. You can already do what you want with the current library API. Check the following example:

redigomock-issue36.go

package main

import (
	"fmt"
	"os"

	"github.com/garyburd/redigo/redis"
)

const (
	redisAddress = "127.0.0.1:6379"
)

type Person struct {
	Name string `redis:"name"`
	Age  int    `redis:"age"`
}

func addPerson(conn redis.Conn, id string, person Person) error {
	_, err := conn.Do("HMSET", redis.Args{}.Add(fmt.Sprintf("person:%s", id)).AddFlat(person)...)
	return err
}

func retrievePerson(conn redis.Conn, id string) (Person, error) {
	var person Person

	values, err := redis.Values(conn.Do("HGETALL", fmt.Sprintf("person:%s", id)))
	if err != nil {
		return person, err
	}

	err = redis.ScanStruct(values, &person)
	return person, err
}

func addAndRetrievePerson(conn redis.Conn, id string, person Person) (Person, error) {
	if err := addPerson(conn, id, person); err != nil {
		return Person{}, err
	}

	return retrievePerson(conn, id)
}

func Job(conn redis.Conn) error {
	person, err := addAndRetrievePerson(conn, "1", Person{
		Name: "Mr. Johnson",
		Age:  42,
	})

	if err != nil {
		return err
	}

	println(person.Name, person.Age)

	person, err = addAndRetrievePerson(conn, "2", Person{
		Name: "Mr. Tompson",
		Age:  53,
	})

	if err != nil {
		return err
	}

	println(person.Name, person.Age)

	person, err = addAndRetrievePerson(conn, "2", Person{
		Name: "Mr. Tompson",
		Age:  53,
	})

	if err != nil {
		return err
	}

	println(person.Name, person.Age)

	return nil
}

func main() {
	c, err := redis.Dial("tcp", redisAddress, redis.DialPassword("abc123"))
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer c.Close()

	if err = Job(c); err != nil {
		fmt.Println(err)
		os.Exit(2)
	}
}

Now let's test the Job function looking for how many times the commands were called.

redigomock-issue36_test.go

package main

import (
	"reflect"
	"testing"

	"github.com/rafaeljusto/redigomock"
)

func TestJob(t *testing.T) {
	c := redigomock.NewConn()

	scenarios := []struct {
		description   string
		stub          func() map[string]*redigomock.Cmd
		expected      map[string]int
		expectedError error
	}{
		{
			description: "it should add and retrieve correctly",
			stub: func() map[string]*redigomock.Cmd {
				cmds := make(map[string]*redigomock.Cmd)
				cmds["HMSET-PERSON1"] = c.Command("HMSET", "person:1", "name", "Mr. Johnson", "age", 42).Expect(int64(1))
				cmds["HMSET-PERSON2"] = c.Command("HMSET", "person:2", "name", "Mr. Tompson", "age", 53).Expect(int64(1))
				cmds["HGETALL-PERSON1"] = c.Command("HGETALL", "person:1").ExpectMap(map[string]string{
					"name": "Mr. Johnson",
					"age":  "42",
				})
				cmds["HGETALL-PERSON2"] = c.Command("HGETALL", "person:2").ExpectMap(map[string]string{
					"name": "Mr. Tompson",
					"age":  "53",
				})
				return cmds
			},
			expected: map[string]int{
				"HMSET-PERSON1":   1,
				"HMSET-PERSON2":   2,
				"HGETALL-PERSON1": 1,
				"HGETALL-PERSON2": 2,
			},
		},
	}

	for _, scenario := range scenarios {
		t.Run(scenario.description, func(t *testing.T) {
			c.Clear()
			cmds := scenario.stub()
			err := Job(c)

			for id, cmd := range cmds {
				expectedCalls := scenario.expected[id]
				delete(scenario.expected, id)

				if calls := c.Stats(cmd); calls != expectedCalls {
					t.Errorf("unexpected command call times for id “%s”. Expected “%d” and got “%d”", id, expectedCalls, calls)
				}
			}

			for id := range scenario.expected {
				t.Errorf("command id “%s” not called", id)
			}

			if !reflect.DeepEqual(scenario.expectedError, err) {
				t.Errorf("unexpected error. Expected “%#v” and got “%#v”", scenario.expectedError, err)
			}
		})
	}
}