go-mocket icon indicating copy to clipboard operation
go-mocket copied to clipboard

WithReply values getting interchanged during tests

Open jaylane opened this issue 6 years ago • 8 comments

My tests are passing half of the time and failing half of the time when using go-mocket mock and a response struct.

example test:

Describe("GetApplicationStatuses", func() {
		Context("on success", func() {
			It("should should write inReview and ineligible counts to context", func() {
				params := []gin.Param{gin.Param{Key: "company_id", Value: "1"}}

				mockContext.Params = params
				mockContext.Set("employeeIDs", []string{"1a"})

				mockReply := []map[string]interface{}{{"InReview": 1, "Ineligible": 2}}
				globalMock.NewMock().
					WithQuery("case when app.status in('SUBMITTED','REQUIRES_ACTION')").
					WithReply(mockReply)

				returnedFunc := GetApplicationStatuses()
				returnedFunc(mockContext)

				var expectValue = ApplicationStatus{InReview: 1, Ineligible: 2}
				Expect(mockContext.Value("Applications")).To(Equal(expectValue))
			})
		})

failure:

Expected
      <middleware.ApplicationStatus>: {InReview: 2, Ineligible: 1}
  to equal
      <middleware.ApplicationStatus>: {InReview: 1, Ineligible: 2}

func & struct being tested:

func GetApplicationStatuses() gin.HandlerFunc {
	return func(c *gin.Context) {
		var (
			inReview,
			ineligible int
			applications ApplicationStatus
		)

		rows, err := db.Table("application as app").
			Select("COALESCE(sum(case when app.status in('SUBMITTED','REQUIRES_ACTION') then 1 else 0 end),0) as in_review, "+
				"COALESCE(sum(case when app.status = 'INELIGIBLE' then 1 else 0 end),0) as ineligible").
			Where("app.id in(select employee.id from employee where employee.x_id in(?))", c.Value("employeeIDs")).
			Rows()

		if err != nil {
			utils.LogError(
				fmt.Sprintf("Error getting application statuses: %v", err),
				"middleware.GetApplicationStatuses",
			)

			c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
				"message": "Error getting application statuses.",
			})

			return
		}

		for rows.Next() {
			err := rows.Scan(&inReview, &ineligible)

			if err != nil {
				utils.LogError(
					fmt.Sprintf("Error getting application statuses: %v", err),
					"middleware.GetApplicationStatuses",
				)

				c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
					"message": "Error getting application statuses.",
				})

				return
			}

			applications = ApplicationStatus{inReview, ineligible}
		}

		c.Set("Applications", applications)
	}
}

// ApplicationStatus struct for response to GetApplicationStatuses
type ApplicationStatus struct {
	InReview   int `json:"inReview gorm:"column:InReview"`
	Ineligible int `json:"ineligible" gorm:"column:Ineligible"`
}

half of the time the test passes fine, the other half it fails because InReview and Ineligible values are swapped. I've been banging my head against the wall with it for a week now. any help would be greatly appreciated. Seems to be happening anytime I'm using a response struct or there is > 1 column on the WithReply. Thanks for a great mocking package btw.

jaylane avatar Oct 09 '18 02:10 jaylane

still haven't been able to figure this out, added debugging logs to the test and func, on some occasion rows.Columns() swaps the order of how they should be returned by the query & reply adding test logs with debugs below:

`•••••2018/10/21 14:05:26 Mock Reply: [map[in_review:1 ineligible:2]] 2018/10/21 14:05:26 mock_catcher: check query: SELECT COALESCE(sum(case when app.status in('SUBMITTED','REQUIRES_ACTION') then 1 else 0 end),0) as in_review, COALESCE(sum(case when app.status = 'INELIGIBLE' then 1 else 0 end),0) as ineligible FROM application as app WHERE (app.id in(select employee.id from employee where employee.cs_id in(1a)))

[2018-10-21 14:05:26] [0.05ms] SELECT COALESCE(sum(case when app.status in('SUBMITTED','REQUIRES_ACTION') then 1 else 0 end),0) as in_review, COALESCE(sum(case when app.status = 'INELIGIBLE' then 1 else 0 end),0) as ineligible FROM application as app WHERE (app.id in(select employee.id from employee where employee.cs_id in('1a'))) [0 rows affected or returned ] 2018/10/21 14:05:26 Columns Returned: [ineligible in_review]


• Failure [0.001 seconds] Contrib Middleware GetApplicationStatuses on success [It] should should write inReview and ineligible counts to context

Expected <middleware.ApplicationStatus>: {InReview: 2, Ineligible: 1} to equal <middleware.ApplicationStatus>: {InReview: 1, Ineligible: 2}

jaylane avatar Oct 21 '18 18:10 jaylane

Hi @jaylane, let me take look into. I will get back to you as soon as possible.

Selvatico avatar Oct 22 '18 07:10 Selvatico

@Selvatico I also encountered this problem.

hxmhlt avatar Nov 07 '18 12:11 hxmhlt

I am having the same issue. I am trying to mock replies for two SELECT queries, one count(*) and the other selecting columns (using sql QueryContext and rows.Next() / rows.Scan()). Half of the time the responses get swapped and the test fails.

saurori avatar Apr 30 '19 20:04 saurori

I am also having the same issue.

KaelBaldwin avatar May 23 '19 20:05 KaelBaldwin

I have since switched to: https://github.com/DATA-DOG/go-sqlmock . Appreciate the work put into go-mocket but the Datadog library has much better support.

saurori avatar May 23 '19 20:05 saurori

any movement on this? @saurori does the datadog package support gorm?

jaylane avatar Aug 07 '19 06:08 jaylane

@jaylane I'm wondering if go-mocket is looking at how the fields are returned by the DB driver.

In your SELECT statement - you're aliasing the InReview field as in_review and the Ineligible field as ineligible

Can you try modifying your mock reply as such: mockReply := []map[string]interface{}{{"in_review": 1, "ineligible": 2}}

buildscientist avatar Sep 02 '19 01:09 buildscientist