1

Recently, I was trying to query data with preload function that gorm provided.

Here's the actual implementation looks like:

func (repo *SectorRepository) GetSectorsByStockCode(stockCode string) *model.Sector {
    var foundStocks []*model.Stock
    repo.DB.Where("code = ?", stockCode).Preload("Sectors").Find(&foundStocks)

    if foundStocks == nil {
        return nil
    } else if len(foundStocks) == 0 {
        return nil
    } else if foundStocks[0].Sectors == nil {
        return nil
    } else if len(foundStocks[0].Sectors) == 0 {
        return nil
    } else {
        return foundStocks[0].Sectors[0]
    }
}

Then this is what I've done on my unit test:

func Test_SectorRepo_GetSectorsByStockCode(t *testing.T) {
    db, gdb, sqlMock, err := InitTestSectorRepo()
    require.NoError(t, err)
    defer db.Close()

    repository := repository.SectorRepository{DB: gdb}
    var stockCode = "AAPL"

    var expectedCode = "code1"
    var expectedName = "name1"
    var exchangeCode = "exchange_code1"
    var sectorCode = "sector_code1"
    var now = time.Now()

    sqlMock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "stocks" WHERE code = $1`)).WithArgs(stockCode).WillReturnRows(
        sqlMock.NewRows([]string{"code", "name", "exchange_code", "created_at", "updated_at"}).
            AddRow(expectedCode, expectedName, exchangeCode, now, now),
    )

    sqlMock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "stock_sectors" WHERE ("stock_sectors"."stock_code","stock_sectors"."stock_exchange_code")  IN (($1,$2))`)).
        WithArgs(expectedCode, exchangeCode).WillReturnRows(sqlMock.NewRows([]string{"stock_code", "stock_exchange_code", "sector_code"}).AddRow(expectedCode, exchangeCode, sectorCode))

    sqlMock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "sectors" WHERE "sectors"."code" = $1'`)).WithArgs(stockCode).WillReturnRows(
        sqlMock.NewRows([]string{"code", "name", "created_at", "updated_at"}).AddRow(sectorCode, "sector_name1", now, now),
    )

    res := repository.GetSectorsByStockCode(stockCode)
    require.Equal(t, res.Name, expectedName)
}

But I got a failing message as provided below:

Running tool: /usr/local/go/bin/go test -timeout 30s -run ^Test_SectorRepo_GetSectorsByStockCode$ gitlab.com/xxx/yyy-service/test/repository


2022/07/02 20:38:10 [32m/Users/aaa/yyy-service/api/repository/sector_repository.go:38
[0m[33m[0.045ms] [34;1m[rows:1][0m SELECT * FROM "stock_sectors" WHERE ("stock_sectors"."stock_code","stock_sectors"."stock_exchange_code") IN (('code1','exchange_code1'))

2022/07/02 20:38:10 [31;1m/Users/aaa/yyy-service/api/repository/sector_repository.go:38 [35;1mQuery: could not match actual sql: "SELECT * FROM "sectors" WHERE "sectors"."code" = $1" with expected regexp "SELECT \* FROM "sectors" WHERE "sectors"\."code" = \$1'"
[0m[33m[0.025ms] [34;1m[rows:0][0m SELECT * FROM "sectors" WHERE "sectors"."code" = 'sector_code1'

2022/07/02 20:38:10 [31;1m/Users/aaa/yyy-service/api/repository/sector_repository.go:38 [35;1mQuery: could not match actual sql: "SELECT * FROM "sectors" WHERE "sectors"."code" = $1" with expected regexp "SELECT \* FROM "sectors" WHERE "sectors"\."code" = \$1'"
[0m[33m[1.300ms] [34;1m[rows:1][0m SELECT * FROM "stocks" WHERE code = 'AAPL'
--- FAIL: Test_SectorRepo_GetSectorsByStockCode (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
    panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x14499f6]

goroutine 4 [running]:
testing.tRunner.func1.2({0x1497620, 0x1896c40})
    /usr/local/go/src/testing/testing.go:1209 +0x24e
testing.tRunner.func1()
    /usr/local/go/src/testing/testing.go:1212 +0x218
panic({0x1497620, 0x1896c40})
    /usr/local/go/src/runtime/panic.go:1038 +0x215
gitlab.com/xxx/yyy-service/test/repository_test.Test_SectorRepo_GetSectorsByStockCode(0x0)
    /Users/aaa/yyy-service/test/repository/sector_repository_test.go:131 +0x8b6
testing.tRunner(0xc00011b380, 0x1526fd8)
    /usr/local/go/src/testing/testing.go:1259 +0x102
created by testing.(*T).Run
    /usr/local/go/src/testing/testing.go:1306 +0x35a
FAIL    gitlab.com/xxx/yyy-service/test/repository  0.533s
FAIL

I thought in the first place that gorm preload function will create subsequent queries, hence I on the above code I was trying to evaluate all the subsequent queries.

Until now I'm still figuring it out if it's possible to test the preload gorm query by using sqlmock.

If there's anyone who could help to answer this problem will be much appreciated :)

otnieldocs
  • 325
  • 1
  • 5
  • 20
  • shouldn't you pass `sectorCode` to the third query? `sqlMock.ExpectQuery(regexp.QuoteMeta(\`SELECT * FROM "sectors" WHERE "sectors"."code" = $1'\`)).WithArgs(sectorCode).WillReturnRows( sqlMock.NewRows([]string{"code", "name", "created_at", "updated_at"}).AddRow(sectorCode, "sector_name1", now, now), )` – Neenad Jul 03 '22 at 09:17
  • Ya I think I should, but I thought in terms of the query it doesn't matter what value I supply. The thing here is it looks like the query can't be validated. Because from what I know the way gorm preload query will generate subsequent query – otnieldocs Jul 04 '22 at 12:07
  • there is a nil pointer returned from somewhere, what is on line 131 as per this `/Users/aaa/yyy-service/test/repository/sector_repository_test.go:131` – Neenad Jul 04 '22 at 12:41
  • and also your queries are not matching: `Query: could not match actual sql: "SELECT * FROM "sectors" WHERE "sectors"."code" = $1" with expected regexp "SELECT \* FROM "sectors" WHERE "sectors"\."code" = \$1'"` – Neenad Jul 04 '22 at 12:51
  • Yes I was aware that what causing the unit test failed is because the query doesn't match, I'm wondering what gorm generates when we're use preload for 2 tables with one to many association? – otnieldocs Jul 06 '22 at 00:43

0 Answers0