TDD Constructors Golang












1















Even though there are a few posts on this, I haven't found one with much substance. So hopefully a few people will share opinions on this.



One thing holding me up from having a true TDD workflow is that I can't figure out a clean way to test things that have to connect to networked services like database.



For example:



type DB struct {
conn *sql.DB
}

func NewDB(URL string) (*DB, err) {
conn, err := sql.Open("postgres", URL)
if err != nil {
return nil, err
}
}


I know I could pass the sql connection to NewDB, or directly to the struct and assign it to an interface that has all the methods I need, and that would be easily testable. But somewhere, I'm going to have to connect. The only way to test this that I've been able to find is...



var sqlOpen = sql.Open
func CreateDB() *DB {
conn, err := sqlOpen("postgres", "url...")
if err != nil {
log.Fatal(err)
}

dataBase = DB{
conn: conn
}
}


Then in the test you swap out the sqlOpen function with something that returns a function with the same signature that will give an error for one test case and not give an error for another. But this feels like a hack, especially if you're doing this for several functions in the same file. Is there a better way? The codebase I'm working with has a lot of functions in packages and network connections. Because I'm struggling to test things in clean way, it's driving me away from TDD.










share|improve this question





























    1















    Even though there are a few posts on this, I haven't found one with much substance. So hopefully a few people will share opinions on this.



    One thing holding me up from having a true TDD workflow is that I can't figure out a clean way to test things that have to connect to networked services like database.



    For example:



    type DB struct {
    conn *sql.DB
    }

    func NewDB(URL string) (*DB, err) {
    conn, err := sql.Open("postgres", URL)
    if err != nil {
    return nil, err
    }
    }


    I know I could pass the sql connection to NewDB, or directly to the struct and assign it to an interface that has all the methods I need, and that would be easily testable. But somewhere, I'm going to have to connect. The only way to test this that I've been able to find is...



    var sqlOpen = sql.Open
    func CreateDB() *DB {
    conn, err := sqlOpen("postgres", "url...")
    if err != nil {
    log.Fatal(err)
    }

    dataBase = DB{
    conn: conn
    }
    }


    Then in the test you swap out the sqlOpen function with something that returns a function with the same signature that will give an error for one test case and not give an error for another. But this feels like a hack, especially if you're doing this for several functions in the same file. Is there a better way? The codebase I'm working with has a lot of functions in packages and network connections. Because I'm struggling to test things in clean way, it's driving me away from TDD.










    share|improve this question



























      1












      1








      1








      Even though there are a few posts on this, I haven't found one with much substance. So hopefully a few people will share opinions on this.



      One thing holding me up from having a true TDD workflow is that I can't figure out a clean way to test things that have to connect to networked services like database.



      For example:



      type DB struct {
      conn *sql.DB
      }

      func NewDB(URL string) (*DB, err) {
      conn, err := sql.Open("postgres", URL)
      if err != nil {
      return nil, err
      }
      }


      I know I could pass the sql connection to NewDB, or directly to the struct and assign it to an interface that has all the methods I need, and that would be easily testable. But somewhere, I'm going to have to connect. The only way to test this that I've been able to find is...



      var sqlOpen = sql.Open
      func CreateDB() *DB {
      conn, err := sqlOpen("postgres", "url...")
      if err != nil {
      log.Fatal(err)
      }

      dataBase = DB{
      conn: conn
      }
      }


      Then in the test you swap out the sqlOpen function with something that returns a function with the same signature that will give an error for one test case and not give an error for another. But this feels like a hack, especially if you're doing this for several functions in the same file. Is there a better way? The codebase I'm working with has a lot of functions in packages and network connections. Because I'm struggling to test things in clean way, it's driving me away from TDD.










      share|improve this question
















      Even though there are a few posts on this, I haven't found one with much substance. So hopefully a few people will share opinions on this.



      One thing holding me up from having a true TDD workflow is that I can't figure out a clean way to test things that have to connect to networked services like database.



      For example:



      type DB struct {
      conn *sql.DB
      }

      func NewDB(URL string) (*DB, err) {
      conn, err := sql.Open("postgres", URL)
      if err != nil {
      return nil, err
      }
      }


      I know I could pass the sql connection to NewDB, or directly to the struct and assign it to an interface that has all the methods I need, and that would be easily testable. But somewhere, I'm going to have to connect. The only way to test this that I've been able to find is...



      var sqlOpen = sql.Open
      func CreateDB() *DB {
      conn, err := sqlOpen("postgres", "url...")
      if err != nil {
      log.Fatal(err)
      }

      dataBase = DB{
      conn: conn
      }
      }


      Then in the test you swap out the sqlOpen function with something that returns a function with the same signature that will give an error for one test case and not give an error for another. But this feels like a hack, especially if you're doing this for several functions in the same file. Is there a better way? The codebase I'm working with has a lot of functions in packages and network connections. Because I'm struggling to test things in clean way, it's driving me away from TDD.







      sql go tdd






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 16 '18 at 1:50









      Dmitry Harnitski

      3,86311834




      3,86311834










      asked Nov 16 '18 at 0:31









      A.RowdenA.Rowden

      113




      113
























          1 Answer
          1






          active

          oldest

          votes


















          1














          Typical business application has A LOT of logic in queries. We significantly decrease testing coverage and leave room for regression errors if they are not tested. So, mocking DB repositories is not the best option. Instead, we can mock database itself and test how we work with it on SQL level.



          Below are sample code using DATA-DOG/go-sqlmock, but there could be other libraries that mock sql databases.



          First of all, we need to inject sql connection into our code. GO sql connection is a misleading name and it is actually connections pool, not just single DB connection. That is why, it is make sense to create single *sql.DB in your composition root and reuse in your code even if you do not write tests.



          Sample below shows how to mock web service.



          At the beginning, we need to create new handler with injected connection:



          // New creates new handler
          func New(db *sql.DB) http.Handler {
          return &handler{
          db: db,
          }
          }


          Handler code:



          type handler struct {
          db *sql.DB
          }

          func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
          // some code that loads person name from database using id
          }


          Unit Test that code that mocks DB. It uses stretchr/testify for assertions :



          func TestHandler(t *testing.T) {
          db, sqlMock, _ := sqlmock.New()
          rows := sqlmock.NewRows(string{"name"}).AddRow("John")
          // regex is used to match query
          // assert that we execute SQL statement with parameter and return data
          sqlMock.ExpectQuery(`select name from person where id = ?`).WithArgs(42).WillReturnRows(rows)
          defer db.Close()

          sut := mypackage.New(db)

          r, err := http.NewRequest(http.MethodGet, "https://example.com", nil)
          require.NoError(t, err, fmt.Sprintf("Failed to create request: %v", err))
          w := httptest.NewRecorder()

          sut.ServeHTTP(w, r)
          // make sure that all DB expectations were met
          err = sqlMock.ExpectationsWereMet()
          assert.NoError(t, err)
          // other assertions that check DB data should be here
          assert.Equal(t, http.StatusOK, w.Code)
          }


          Our test asserts simple SQL statement against DB. But with go-sqlmock it is possible to test all CRUD operations and database transactions.



          Test above still has one weak point. We tested that our SQL statement is executed from code, but we did not test if it works against our real DB. That issue cannot be solved with unit tests. The only solution is integration test against real DB.



          We are in better position now though. Out business logic is already tested in unit tests. We do not need to create lots of integration tests to cover different scenarios and parameters, instead we need to have just one test per query to verify SQL syntax and match to our DB schema.



          Happy testing!






          share|improve this answer
























          • Thank you. I agree with testing the SQL syntax and that's a good approach for SQL specifically. But what about other networked services like web-sockets, messaging queues, etc.? Is there a better way to test past the connection functions than making them global and swapping them out at runtime in the test?

            – A.Rowden
            Nov 16 '18 at 16:42











          • @A.Rowden You can mock calls to external web services -stackoverflow.com/a/53231951/1420332. It makes sense to reuse Http client at least for oauth to reuse tokens.

            – Dmitry Harnitski
            Nov 16 '18 at 17:21













          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53329767%2ftdd-constructors-golang%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1














          Typical business application has A LOT of logic in queries. We significantly decrease testing coverage and leave room for regression errors if they are not tested. So, mocking DB repositories is not the best option. Instead, we can mock database itself and test how we work with it on SQL level.



          Below are sample code using DATA-DOG/go-sqlmock, but there could be other libraries that mock sql databases.



          First of all, we need to inject sql connection into our code. GO sql connection is a misleading name and it is actually connections pool, not just single DB connection. That is why, it is make sense to create single *sql.DB in your composition root and reuse in your code even if you do not write tests.



          Sample below shows how to mock web service.



          At the beginning, we need to create new handler with injected connection:



          // New creates new handler
          func New(db *sql.DB) http.Handler {
          return &handler{
          db: db,
          }
          }


          Handler code:



          type handler struct {
          db *sql.DB
          }

          func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
          // some code that loads person name from database using id
          }


          Unit Test that code that mocks DB. It uses stretchr/testify for assertions :



          func TestHandler(t *testing.T) {
          db, sqlMock, _ := sqlmock.New()
          rows := sqlmock.NewRows(string{"name"}).AddRow("John")
          // regex is used to match query
          // assert that we execute SQL statement with parameter and return data
          sqlMock.ExpectQuery(`select name from person where id = ?`).WithArgs(42).WillReturnRows(rows)
          defer db.Close()

          sut := mypackage.New(db)

          r, err := http.NewRequest(http.MethodGet, "https://example.com", nil)
          require.NoError(t, err, fmt.Sprintf("Failed to create request: %v", err))
          w := httptest.NewRecorder()

          sut.ServeHTTP(w, r)
          // make sure that all DB expectations were met
          err = sqlMock.ExpectationsWereMet()
          assert.NoError(t, err)
          // other assertions that check DB data should be here
          assert.Equal(t, http.StatusOK, w.Code)
          }


          Our test asserts simple SQL statement against DB. But with go-sqlmock it is possible to test all CRUD operations and database transactions.



          Test above still has one weak point. We tested that our SQL statement is executed from code, but we did not test if it works against our real DB. That issue cannot be solved with unit tests. The only solution is integration test against real DB.



          We are in better position now though. Out business logic is already tested in unit tests. We do not need to create lots of integration tests to cover different scenarios and parameters, instead we need to have just one test per query to verify SQL syntax and match to our DB schema.



          Happy testing!






          share|improve this answer
























          • Thank you. I agree with testing the SQL syntax and that's a good approach for SQL specifically. But what about other networked services like web-sockets, messaging queues, etc.? Is there a better way to test past the connection functions than making them global and swapping them out at runtime in the test?

            – A.Rowden
            Nov 16 '18 at 16:42











          • @A.Rowden You can mock calls to external web services -stackoverflow.com/a/53231951/1420332. It makes sense to reuse Http client at least for oauth to reuse tokens.

            – Dmitry Harnitski
            Nov 16 '18 at 17:21


















          1














          Typical business application has A LOT of logic in queries. We significantly decrease testing coverage and leave room for regression errors if they are not tested. So, mocking DB repositories is not the best option. Instead, we can mock database itself and test how we work with it on SQL level.



          Below are sample code using DATA-DOG/go-sqlmock, but there could be other libraries that mock sql databases.



          First of all, we need to inject sql connection into our code. GO sql connection is a misleading name and it is actually connections pool, not just single DB connection. That is why, it is make sense to create single *sql.DB in your composition root and reuse in your code even if you do not write tests.



          Sample below shows how to mock web service.



          At the beginning, we need to create new handler with injected connection:



          // New creates new handler
          func New(db *sql.DB) http.Handler {
          return &handler{
          db: db,
          }
          }


          Handler code:



          type handler struct {
          db *sql.DB
          }

          func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
          // some code that loads person name from database using id
          }


          Unit Test that code that mocks DB. It uses stretchr/testify for assertions :



          func TestHandler(t *testing.T) {
          db, sqlMock, _ := sqlmock.New()
          rows := sqlmock.NewRows(string{"name"}).AddRow("John")
          // regex is used to match query
          // assert that we execute SQL statement with parameter and return data
          sqlMock.ExpectQuery(`select name from person where id = ?`).WithArgs(42).WillReturnRows(rows)
          defer db.Close()

          sut := mypackage.New(db)

          r, err := http.NewRequest(http.MethodGet, "https://example.com", nil)
          require.NoError(t, err, fmt.Sprintf("Failed to create request: %v", err))
          w := httptest.NewRecorder()

          sut.ServeHTTP(w, r)
          // make sure that all DB expectations were met
          err = sqlMock.ExpectationsWereMet()
          assert.NoError(t, err)
          // other assertions that check DB data should be here
          assert.Equal(t, http.StatusOK, w.Code)
          }


          Our test asserts simple SQL statement against DB. But with go-sqlmock it is possible to test all CRUD operations and database transactions.



          Test above still has one weak point. We tested that our SQL statement is executed from code, but we did not test if it works against our real DB. That issue cannot be solved with unit tests. The only solution is integration test against real DB.



          We are in better position now though. Out business logic is already tested in unit tests. We do not need to create lots of integration tests to cover different scenarios and parameters, instead we need to have just one test per query to verify SQL syntax and match to our DB schema.



          Happy testing!






          share|improve this answer
























          • Thank you. I agree with testing the SQL syntax and that's a good approach for SQL specifically. But what about other networked services like web-sockets, messaging queues, etc.? Is there a better way to test past the connection functions than making them global and swapping them out at runtime in the test?

            – A.Rowden
            Nov 16 '18 at 16:42











          • @A.Rowden You can mock calls to external web services -stackoverflow.com/a/53231951/1420332. It makes sense to reuse Http client at least for oauth to reuse tokens.

            – Dmitry Harnitski
            Nov 16 '18 at 17:21
















          1












          1








          1







          Typical business application has A LOT of logic in queries. We significantly decrease testing coverage and leave room for regression errors if they are not tested. So, mocking DB repositories is not the best option. Instead, we can mock database itself and test how we work with it on SQL level.



          Below are sample code using DATA-DOG/go-sqlmock, but there could be other libraries that mock sql databases.



          First of all, we need to inject sql connection into our code. GO sql connection is a misleading name and it is actually connections pool, not just single DB connection. That is why, it is make sense to create single *sql.DB in your composition root and reuse in your code even if you do not write tests.



          Sample below shows how to mock web service.



          At the beginning, we need to create new handler with injected connection:



          // New creates new handler
          func New(db *sql.DB) http.Handler {
          return &handler{
          db: db,
          }
          }


          Handler code:



          type handler struct {
          db *sql.DB
          }

          func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
          // some code that loads person name from database using id
          }


          Unit Test that code that mocks DB. It uses stretchr/testify for assertions :



          func TestHandler(t *testing.T) {
          db, sqlMock, _ := sqlmock.New()
          rows := sqlmock.NewRows(string{"name"}).AddRow("John")
          // regex is used to match query
          // assert that we execute SQL statement with parameter and return data
          sqlMock.ExpectQuery(`select name from person where id = ?`).WithArgs(42).WillReturnRows(rows)
          defer db.Close()

          sut := mypackage.New(db)

          r, err := http.NewRequest(http.MethodGet, "https://example.com", nil)
          require.NoError(t, err, fmt.Sprintf("Failed to create request: %v", err))
          w := httptest.NewRecorder()

          sut.ServeHTTP(w, r)
          // make sure that all DB expectations were met
          err = sqlMock.ExpectationsWereMet()
          assert.NoError(t, err)
          // other assertions that check DB data should be here
          assert.Equal(t, http.StatusOK, w.Code)
          }


          Our test asserts simple SQL statement against DB. But with go-sqlmock it is possible to test all CRUD operations and database transactions.



          Test above still has one weak point. We tested that our SQL statement is executed from code, but we did not test if it works against our real DB. That issue cannot be solved with unit tests. The only solution is integration test against real DB.



          We are in better position now though. Out business logic is already tested in unit tests. We do not need to create lots of integration tests to cover different scenarios and parameters, instead we need to have just one test per query to verify SQL syntax and match to our DB schema.



          Happy testing!






          share|improve this answer













          Typical business application has A LOT of logic in queries. We significantly decrease testing coverage and leave room for regression errors if they are not tested. So, mocking DB repositories is not the best option. Instead, we can mock database itself and test how we work with it on SQL level.



          Below are sample code using DATA-DOG/go-sqlmock, but there could be other libraries that mock sql databases.



          First of all, we need to inject sql connection into our code. GO sql connection is a misleading name and it is actually connections pool, not just single DB connection. That is why, it is make sense to create single *sql.DB in your composition root and reuse in your code even if you do not write tests.



          Sample below shows how to mock web service.



          At the beginning, we need to create new handler with injected connection:



          // New creates new handler
          func New(db *sql.DB) http.Handler {
          return &handler{
          db: db,
          }
          }


          Handler code:



          type handler struct {
          db *sql.DB
          }

          func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
          // some code that loads person name from database using id
          }


          Unit Test that code that mocks DB. It uses stretchr/testify for assertions :



          func TestHandler(t *testing.T) {
          db, sqlMock, _ := sqlmock.New()
          rows := sqlmock.NewRows(string{"name"}).AddRow("John")
          // regex is used to match query
          // assert that we execute SQL statement with parameter and return data
          sqlMock.ExpectQuery(`select name from person where id = ?`).WithArgs(42).WillReturnRows(rows)
          defer db.Close()

          sut := mypackage.New(db)

          r, err := http.NewRequest(http.MethodGet, "https://example.com", nil)
          require.NoError(t, err, fmt.Sprintf("Failed to create request: %v", err))
          w := httptest.NewRecorder()

          sut.ServeHTTP(w, r)
          // make sure that all DB expectations were met
          err = sqlMock.ExpectationsWereMet()
          assert.NoError(t, err)
          // other assertions that check DB data should be here
          assert.Equal(t, http.StatusOK, w.Code)
          }


          Our test asserts simple SQL statement against DB. But with go-sqlmock it is possible to test all CRUD operations and database transactions.



          Test above still has one weak point. We tested that our SQL statement is executed from code, but we did not test if it works against our real DB. That issue cannot be solved with unit tests. The only solution is integration test against real DB.



          We are in better position now though. Out business logic is already tested in unit tests. We do not need to create lots of integration tests to cover different scenarios and parameters, instead we need to have just one test per query to verify SQL syntax and match to our DB schema.



          Happy testing!







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 16 '18 at 1:49









          Dmitry HarnitskiDmitry Harnitski

          3,86311834




          3,86311834













          • Thank you. I agree with testing the SQL syntax and that's a good approach for SQL specifically. But what about other networked services like web-sockets, messaging queues, etc.? Is there a better way to test past the connection functions than making them global and swapping them out at runtime in the test?

            – A.Rowden
            Nov 16 '18 at 16:42











          • @A.Rowden You can mock calls to external web services -stackoverflow.com/a/53231951/1420332. It makes sense to reuse Http client at least for oauth to reuse tokens.

            – Dmitry Harnitski
            Nov 16 '18 at 17:21





















          • Thank you. I agree with testing the SQL syntax and that's a good approach for SQL specifically. But what about other networked services like web-sockets, messaging queues, etc.? Is there a better way to test past the connection functions than making them global and swapping them out at runtime in the test?

            – A.Rowden
            Nov 16 '18 at 16:42











          • @A.Rowden You can mock calls to external web services -stackoverflow.com/a/53231951/1420332. It makes sense to reuse Http client at least for oauth to reuse tokens.

            – Dmitry Harnitski
            Nov 16 '18 at 17:21



















          Thank you. I agree with testing the SQL syntax and that's a good approach for SQL specifically. But what about other networked services like web-sockets, messaging queues, etc.? Is there a better way to test past the connection functions than making them global and swapping them out at runtime in the test?

          – A.Rowden
          Nov 16 '18 at 16:42





          Thank you. I agree with testing the SQL syntax and that's a good approach for SQL specifically. But what about other networked services like web-sockets, messaging queues, etc.? Is there a better way to test past the connection functions than making them global and swapping them out at runtime in the test?

          – A.Rowden
          Nov 16 '18 at 16:42













          @A.Rowden You can mock calls to external web services -stackoverflow.com/a/53231951/1420332. It makes sense to reuse Http client at least for oauth to reuse tokens.

          – Dmitry Harnitski
          Nov 16 '18 at 17:21







          @A.Rowden You can mock calls to external web services -stackoverflow.com/a/53231951/1420332. It makes sense to reuse Http client at least for oauth to reuse tokens.

          – Dmitry Harnitski
          Nov 16 '18 at 17:21






















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53329767%2ftdd-constructors-golang%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Florida Star v. B. J. F.

          Error while running script in elastic search , gateway timeout

          Adding quotations to stringified JSON object values