Testing constructors in Go - unit-testing

How I can test my NewClient constructor for my Client struct ?
package busybus
import (
"bufio"
"net"
)
type Client struct {
counter integer
conn net.Conn
bufin *bufio.Reader
bufout *bufio.Writer
messages chan string
state string
}
func NewClient(conn net.Conn, messages chan string) *Client {
return &Client{
counter: 0,
conn: conn,
bufin: bufio.NewReader(conn),
bufout: bufio.NewWriter(conn),
messages: messages,
state: "waiting",
}
}
I tried some testing like this:
package busybus
import (
"net"
"testing"
)
func TestNewClient(t *testing.T) {
ln, _ := net.Listen("tcp", ":65535")
conn, _ := ln.Accept()
messages := make(chan string)
client := NewClient(conn, messages)
if client.conn != conn {
t.Errorf("NewClient(%q, %q).conn == %q, want %q", conn, messages, client.conn, conn)
}
}
But this hangs during the test runs due to ln.Accept, it seems a totally wrong approach anyway... any suggestion how I can test this constructor ?

Some code is so short and simple that ensuring that the test is correct is more complicated than ensuring that the code itself is correct.
Your constructor is such code.
What I would do is either not test it at all, or just call it (using some dummy implementation of net.Conn) to ensure that it doesn't blow up in smoke when called (therefore called a smoke test).
Later you can test it as part of a bigger test (integration test), where you have a real server to connect to, and which uses your client struct to talk to it.
If you ever find a bug that is due to this constructor, then first add a test that demonstrates the problem, then fix it. Then you will have your test :-)
A test should check that the constructor "works as intended". Checking the value of client.conn is hardly checking any intended behavior, as the value of an unexported field is not behavior. It just checks HOW the struct and constructor is implemented, not WHAT it implements. Test the what, not the how.
By the way, you could maybe embed a *ReadWriter in your client.
client.buf = bufio.NewReadWriter(...)

In addition to what #bjarke-ebert said.
There are no constructors in Go, they are just normal functions. Test them as you would test any other function. If a function is too complex to test then probably there is a way to change the design to make it more testable.
Also from my experience tests that check internal details of implementation too much (like the test from the question) are very hard to maintain as they constantly need to be updated.

Related

Testing a function that contains stderr, stdout and os.Exit()

I'm building an UI for cli apps. I completed the functions but I couldn't figure out how to test it.
Repo: https://github.com/erdaltsksn/cui
func Success(message string) {
color.Success.Println("√", message)
os.Exit(0)
}
// Error prints a success message and exit status 1
func Error(message string, err ...error) {
color.Danger.Println("X", message)
if len(err) > 0 {
for _, e := range err {
fmt.Println(" ", e.Error())
}
}
os.Exit(1)
}
I want to write unit tests for functions. The problem is functions contains print and os.Exit(). I couldn't figure out how to write test for both.
This topic: How to test a function's output (stdout/stderr) in unit tests helps me test print function. I need to add os.Exit()
My Solution for now:
func captureOutput(f func()) string {
var buf bytes.Buffer
log.SetOutput(&buf)
f()
log.SetOutput(os.Stderr)
return buf.String()
}
func TestSuccess(t *testing.T) {
type args struct {
message string
}
tests := []struct {
name string
args args
output string
}{
{"Add test cases.", args{message: "my message"}, "ESC[1;32m"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
want := tt.output
got := captureOutput(func() {
cui.Success(tt.args.message)
})
got := err
if got.Error() != want {
t.Error("Got:", got, ",", "Want:", want)
}
})
}
}
The usual answer in TDD is that you take your function and divide it into two parts; one part that is easy to test, but is not tightly coupled to specific file handles, or a specific implementation of os::Exit; the other part is tightly coupled to these things, but is so simple it obviously has no deficiencies.
Your "unit tests" are mistake detectors that measure the first part.
The second part you write once, inspect it "by hand", and then leave it alone. The idea here being that things are so simple that, once implemented correctly, they don't need to change.
// Warning: untested code ahead
func Foo_is_very_stable() {
bar_is_easy_to_test(stdin, stdout, os.exit)
}
func bar_is_easy_to_test(in *File, out *File , exit func(int)) {
// Do complicated things here.
}
Now, we are cheating a little bit -- os.exit is special magic that never returns, but bar_is_easy_to_test doesn't really know that.
Another design that is a bit more fair is to put the complicated code into a state machine. The state machine decides what to do, and the host invoking the machine decides how to do that....
// More untested code
switch state_machine.next() {
case OUT:
println(state_machine.line())
state_machine.onOut()
case EXIT:
os.exit(state_machine.exitCode())
Again, you get a complicated piece that is easy to test (the state machine) and a much simpler piece that is stable and easily verified by inspection.
This is one of the core ideas underlying TDD - that we deliberately design our code in such a way that it is "easy to test". The justification for this is the claim that code that is easy to test is also easy to maintain (because mistakes are easily detected and because the designs themselves are "cleaner").
Recommended viewing
Boundaries, by Gary Bernhardt
Building Protocol Libraries..., by Cory Benfield
What you have there is called a "side effect" - a situation when execution of your application spans beyond it's environment, it's address space. And the thing is, you don't test side effects. It is not always possible, and when it is - it is unreasonably complicated and ugly.
The basic idea is to have your side effects, like CLI output or os.Exit() (or network connections, or accessing files), decoupled from you main body of logic. There are plenty of ways to do it, the entire "software design" discipline is devoted to that, and #VoiceOfUnreason gives a couple of viable examples.
In your example I would go with wrapping side effects in functions and arranging some way to inject dependencies into Success() & Error(). If you want to keep those two just plain functions, then it's either a function argument or a global variable holding a function for exiting (as per #Peter's comment), but I'd recommend going OO way, employing some patterns and achieving much greater flexibility for you lib.

Golang mock a function with dynamic function calls

I'm pretty new to Go and still learning about how things work in Go, so with that said I've been looking in to Go testing approach and how mocking would work for the past few weeks and most of the information I found based on functions being concrete.
E.g. everything is a function is either passed as a receiver or a parameter, however, the problem I face is my function uses a switch case to determine what function it should be called, so it's not passed from outside.
func (n *Notification) Notify(m Message) error {
switch n.Service {
case "slack":
var s slack.Slack
s.User = m.User
s.Host = m.Host
s.Provider = m.Provider
s.SystemUser = m.SystemUser
return s.SlackSend(n.Url)
default:
return errors.New(codes.CODE5)
}
}
Above code is what the function I want to test looks like and I'm having a hard time figuring out how I could mock the SlackSend() function.
I've come across some article say I should write the function in the test file and when I'm happy with what function do, I should write the real code. This doesn't sound right to me because for me it feels like I have the same code in two places and test only uses the one in test and I could change the real code and break it without testes detecting that.
I mainly work on Python and I'm used to using things like Mock/MagicMock that can intercept the function call and replace on at runtime, so apologies in advance if I don't quite get the Go's TDD approach.
This is what test code would look like if anyone wonders:
type MockSlack struct {
*slack.Slack
}
func (s *MockSlack) SlackSend(url string) error {
if url != "" {
return nil
} else {
return errors.New("url empty")
}
}
func TestNotify(t *testing.T) {
m := Message{}
n := Notification{
Service: "slack",
Url: "https://dummy.io",
}
if err := n.Notify(m); err != nil {
t.Errorf("SlackSend, expected: %s, got: %s", "nil", err.Error())
}
}
Obviously the MockSlack structure is not taking effect because it's not really passed in anywhere.
If anyone has any advice on how I could mock this or what I should do instead would be much appreciated.
Thanks in advance.
UPDATE:
Background
This isn't a web server/applicaition of a sort. It's an SSH authentication plugin so it would be a server-side application. As for Notify method, it server's the purpose of a mapper. So it can call Slack, MS Teams, AWS SNS which give caller less conditions to process and how and where the notification it sent is decided by the Notify method.
If you are not able to change the Notify Method to make it testing friendly. One option would be to consider using monkeypatching. Here is an example provided for *net.Dialer:
func main() {
var d *net.Dialer // Has to be a pointer to because `Dial` has a pointer receiver
monkey.PatchInstanceMethod(reflect.TypeOf(d), "Dial", func(_ *net.Dialer, _, _ string) (net.Conn, error) {
return nil, fmt.Errorf("no dialing allowed")
})
_, err := http.Get("http://google.com")
fmt.Println(err) // Get http://google.com: no dialing allowed
}
WARNING: It's not safe to use it outside of a testing environment.

What should be the easiest way to unit test influxdb queries

I have a service that only make queries ( read / write ) to influxDB.
I want to unit test this, but I'm not sure how to do it, I've read a bunch of tutos talking about mocking. A lot deals with components like go-sqlmock. But as I am using influxDB, I could not use it.
I also find out other components I've tried to use like goMock or testify to be over-complicated.
What I think to do is to create a Repository Layer, an interface that should implement all the methods I need to run / test, and pass concrete classes with dependency injection.
I think it could work, but is it the easiest way to do it ?
I guess having Repositories everywhere, even for small services, just for them to be testable, seems to be over-engineered.
I can give you code if needed, but I think my question is a bit more theorical than practical. It is about the easiest way to mock a custom DB for unit testing.
To expand on #Markus W Mahlberg answer:
If the goal is to verify the queries are valid and actually execute against influx there's no shortcut for actually performing these against influx. These are usually considered to be "integration" tests. I have found with docker-compose that these tests can be just as reliable as unit tests, and fast enough to be integrated into CI. Having the tests execute in CI enables local engineers to easily run these tests to verify their query changes as well.
I guess having Repositories everywhere, even for small services, just for them to be testable, seems to be over-engineered.
I have found this to be pretty polarizing discussion. A test implementation IS a concrete implementation and paves the way for reliable, repeatable tests that support easily isolating and exercising specific components of your code.
I want to unit test this, but I'm not sure how to do it,
I think this is pretty nuanced, IMO unit testing queries provides negative value. Value comes from using a repository interface to allow your unit tests to explicitly configure responses that you would receive from influx in order to fully exercise your application code. This provides no feedback on influx, which is why the integration tests are essential in order to verify that your application can validly configure, connect, and query against influx. This validation implicitly happens when you deploy your application, at which point it becomes way more expensive in terms of feedback than verifying it locally and in CI with integration tests.
I created a diagram to try and illustrate these differences:
Unit tests with repository are focused on your application code and provide little feedback/value on anything to do with influx. Integration tests are useful for verifying your client (perhaps being extended to your application depending on where the tests are exercising but I prefer to bound it to the client since you already have the static feedback from go on the interfaces and calls). Then finally, as #Markus points out, the step to e2e tests is pretty small from integration tests, and allow you to test your full service.
By its very definition, if you test your integration with an external resource, we are talking of integration tests, not unit tests. So we have two problems to solve here.
Unit tests
What you typically do is to have a data access layer which accepts interfaces, which in turn are easy to mock and you can unittest your application logic.
package main
import (
"errors"
"fmt"
)
var (
values = map[string]string{"foo": "bar", "bar": "baz"}
Expected = errors.New("Expected error")
)
type Getter interface {
Get(name string) (string, error)
}
// ErrorGetter implements Getter and always returns an error to test the error handling code of the caller.
// ofc, you could (and prolly should) use some mocking here in order to be able to test various other cases
type ErrorGetter struct{}
func (e ErrorGetter) Get(name string) (string, error) {
return "", Expected
}
// MapGetter implements Getter and uses a map as its datasource.
// Here you can see that you actually get an advantage: you decouple your logic from the data source,
// making refactoring (and debugging) **much** easier WTSHTF.
type MapGetter struct {
data map[string]string
}
func (m MapGetter) Get(name string) (string, error) {
if v, ok := m.data[name]; ok {
return v, nil
}
return "", fmt.Errorf("No value found for %s", name)
}
type retriever struct {
g Getter
}
func (r retriever) retrieve(name string) (string, error) {
return r.g.Get(name)
}
func main() {
// Assume this is test code. No tests possible on playground ;)
bad := retriever{g: ErrorGetter{}}
s, err := bad.retrieve("baz")
if s != "" || err == nil {
panic("Something went seriously wrong")
}
// Needs to fail as well, as "baz" is not in values
good := retriever{g: MapGetter{values}}
s, err = good.retrieve("baz")
if s != "" || err == nil {
panic("Something went seriously wrong")
}
s, err = good.retrieve("foo")
if s != "bar" || err != nil {
panic("Something went seriously wrong")
}
}
In the example above, I actually had to implement two Getters to cover all test cases, since I could not use a mocking library, but you get the picture.
As for the over engineering: Plain and simple, no, that is not overengineering. It is what I personally call proper craftsmanship. It will pay in the long run to get used to it. Maybe not in this project, but in one to come.
Integration tests
Dodgy. What I tend to do is to make sure my queries are correct before I commit them ;)
In the rare case I really want to verify my queries in a CI for example, I usually create a Makefile which in turn spins up a docker(-compose) which provides the stuff I want to integrate against and then runs the tests.

How can you test code that relies on net.Conn without creating an actual network connection?

If I have code that works with a net.Conn, how can I write tests for it without actually creating a network connection to localhost?
I've seen no solutions to this online; people seem to either ignore it (no tests), write tests that cannot run in parallel (ie. use an actual network connection, which uses up ports), or use io.Pipe.
However, net.Conn defines SetReadDeadline, SetWriteDeadline; and io.Pipe doesn't. net.Pipe also doesnt, despite superficially claiming to implement the interface, it's simply implemented with:
func (p *pipe) SetDeadline(t time.Time) error {
return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (p *pipe) SetReadDeadline(t time.Time) error {
return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (p *pipe) SetWriteDeadline(t time.Time) error {
return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
(see: https://golang.org/src/net/pipe.go)
So... is there some other way of doing this?
I'll accept any answer that shows how to use a stream in a test with a working deadline that is not an actual network socket.
(Idly, this cloudflare blogpost covers the motivation for using deadlines, and why blocking forever in a goroutine per connection is not an acceptable solution; but regardless of that argument, particularly in this case I'm looking for a solution for tests where we deliberately want to handle edge cases where a bad connection hangs, etc.)
(NB. this may seem like a duplicate of Simulate a tcp connection in Go, but notice that all the solutions proposed in that question do not implement the Deadline functions, which is specifically what I'm asking about how to test here)
Your question is very open, so it is not possible to give you "the correct answer". But I think I understand the point where you stuck. This answer is also open, but it should bring you back on the right track.
A few days ago I wrote a short article, which shows the principle which you have to use.
Before I make some small examples how you such tests work, we need to fix one important point:
We do not test the net package. We asume, that the package has no bug and does, what the documentation says. That means we do not care how the Go team has implementet SetReadDeadline and SetWriteDeadline. We test only the usage in our programm.
Step 1: Refactoring your code
You did not post any code snippets, so I give you just a simple example. I guess you have a method or function, where you are using the net package.
func myConn(...) error {
// You code is here
c, err := net.Dial("tcp", "12.34.56.78:80")
c.setDeadline(t)
// More code here
}
That you are able to to test you need to refactor your function, so it is just using the net.Conn interface. To do this, the net.Dial() call has to be moved outside of the function. Remember that we don't want to test the net.Dial function.
The new function could look something like that:
func myConn(c, net.Conn, ...) error {
// You code is here
c.setDeadline(t)
// More code here
}
Step 2: Implement the net.Conn interface
For testing you need to implement the net.Conn interface:
type connTester struct {
deadline time.Time
}
func (c *connTester) Read(b []byte) (n int, err error) {
return 0, nil
}
...
func (c *connTester) SetDeadline(t time.Time) error {
c.deadline = t
return nil
}
...
Complete implementation including a small type check:
https://play.golang.org/p/taAmI61vVz
Step 3: Testing
When testing, we don't care about the Dial() Method, we just create a pointer to our testtype, which implements the net.Conn interface and put it into your function. Afterwards we look inside our test cases, if the deadline parameter is set correctly.
func TestMyConn(t *testing.T){
myconnTester = &connTester{}
err := myConn(myconnTester,...)
...
if myconntester.deadline != expectedDeadline{
//Test fails
}
}
So when testing, you should always think about, what feature you want to test. I think it is the really most difficult part to abstract the functionality you really want to write. Inside of simple unit tests you should never test functionalities of the standard library. Hope this examples can help you to bring you back on the right track.
Code that needs to be swapped out, for a controlled version in a unittest, should live behind an abstraction. In this case the abstraction would be the net.Conn interface. The production code would use go std lib net.Conn but the test code would use a test stub that is configured with the exact logic to exercise your function.
Introducing an abstraction is a powerful pattern that should allow swapping out all IO, or timing based code, to allow for controlled execution of code, during a unittest.
#apxp stated the same approach in a comment.
The same approach should work for the deadlines. It could get tricky simulating a deadline that is reached, because you may have to configure your stub with multiple responses. ie. The first response succeeds, but the second simulates a deadline that has been reached, and throws an error for the second request.

Execute test cases in a pre-defined order

Is there a way to execute test cases in GoLang in a pre-defined order.
P.S: I am writing test cases for life cycle of a event. So I have different api's for all the CURD operations. I want to run these test cases in a particular order as only if an event is created it can be destroyed.
Also can I get some value from one test case and pass it as input to another. (example:- To test the delete event api, I need a event_id which i get when I call create_event test case)
I am new to GoLang, can someone please guide me through.
Thanks in advance
The only way to do it is to encapsulate all your tests into one test function, that calls sub-functions in the right order and with the right context, and pass the testing.T pointer to each so they can fail. The down-side is that they will all appear as one test. But in fact that is the case - tests are stateless as far as the testing framework is concerned, and each function is a separate test case.
Note that although the tests may run in the order they are written in, I found no documentation stating that this is actually a contract of some sort. So even though you can write them in order and keep the state as external global variables - that's not recommended.
The only flexibility the framework gives you since go 1.4 is the TestMain method that lets you run before/after steps, or setup/teardown:
func TestMain(m *testing.M) {
if err := setUp(); err != nil {
panic(err)
}
rc := m.Run()
tearDown()
os.Exit(rc)
}
But that won't give you what you want. The only way to do that safely is to do something like:
// this is the whole stateful sequence of tests - to the testing framework it's just one case
func TestWrapper(t *testing.T) {
// let's say you pass context as some containing struct
ctx := new(context)
test1(t, ctx)
test2(t, ctx)
...
}
// this holds context between methods
type context struct {
eventId string
}
func test1(t *testing.T, c *context) {
// do your thing, and you can manipulate the context
c.eventId = "something"
}
func test2(t *testing.T, c *context) {
// do your thing, and you can manipulate the context
doSomethingWith(c.eventId)
}