Go Gin Gonic Unit Tests in Sub Folder - unit-testing

I have a Go API built using the Gin framework.
Reading the docs in the testing section here, i tried to implement something similar:
main.go
package main
import (
"mes/routes"
"github.com/gin-gonic/gin"
)
func setupMainRoutes(engine *gin.Engine) {
engine.GET("/mesg/:language/services", routes.AllServices)
engine.GET("/mesg/:language/service/:id", routes.OneService)
engine.GET("/mesg/:language/services/search", routes.SearchService)
}
func setupErrorRoutes(engine *gin.Engine) {
engine.NoRoute(routes.Error404Handler)
}
func setupServer() *gin.Engine {
// Gin Mode
gin.SetMode(gin.ReleaseMode)
// Creates the Gin Engine
engine := gin.New()
// Setup the API Routes
setupMainRoutes(engine)
// Setup Error Routes
setupErrorRoutes(engine)
// Return engine
return engine
}
func main() {
// Run the engine
setupServer().Run()
}
main_test.go
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
/************** Error Handling Tests **************/
func TestPing404Errors1Of3(t *testing.T) {
router := setupServer()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
router.ServeHTTP(w, req)
assert.Equal(t, 404, w.Code)
}
However, it seems that I cannot move the main_test.go to another folder because the setupServer() function becomes undefined.
Is there a way to classify all my tests in a sub folder ?

Moving main_test.go to another folder means that you will be testing/calling setupServer() from a different package. For that, you would need to export as SetupServer() and import the package which contain SetupServer() in your test file. Unfortunately you can't do that with your current file structure because we can't import package main. Therefore, you need to rename the package if you really want to do so.
Having said that, putting the test files _test.go in the same directory (hence same package) is a common practice in Go.

Related

Testing logs printed using other external library in golang

My application uses other external library (which uses zap library) to print audit-logs and now I want to test the printed audit-logs in my golang testing file.
Can someone help me with it.
You could take a look at the zaptest package:
Package zaptest provides a variety of helpers for testing log output.
As example, in your test:
func Test_zap(t *testing.T) {
t.Run("Handle log message", func(t *testing.T) {
// Given
observedZapCore, observedLogs := observer.New(zap.InfoLevel)
observedLogger := zap.New(observedZapCore)
// When
myFunction(observedLogger)
// Then
require.Equal(t, 1, observedLogs.Len())
firstLog := observedLogs.All()[0]
assert.Equal(t, "log myFunction", firstLog.Message)
})
}
where myfunction is
func myFunction(logger *zap.Logger) {
logger.Info("log myFunction")
}
Check also this interesting article about that

Is there a way to set test timeout value dynamically

Currently I am using the following command for running my test with timeout value given during test call.
go test myModule -run TestSanity -v --race -timeout 10h
Is there a way in Golang testing module to set it during program execution.
Something like,
func TestMain(m *testing.M) {
// customTimeout = "10h"
// m.Timeout(customTimeout) <--- Something like this
code := m.Run()
os.Exit(code)
}
You could write your own function to do that:
func panicOnTimeout(d time.Duration) {
<-time.After(d)
panic("Test timed out")
}
func TestMain(m *testing.M) {
go panicOnTimeout(10 * time.Hour) // custom timeout
code := m.Run()
os.Exit(code)
}
This should simulate what go test -timeout does. Be sure to pass -timeout 0 to prevent the default test timeout from triggering though.

Is it possible to unit test that my package does not import a specific package?

I want to make sure my Go package use var instances provided by a "dal" package and does not accidentally import and use db access packages directly.
I guess I can do regexp search on source but I wonder if there is a way to ensure the rule through standard Go testing?
Just to give an idea what I'm going to do:
Interface package:
package dal
type UserDal interface {
GetUser(id int) User
}
Implementation package:
package dal_db_specific
import (
"some_db"
"dal"
)
type UserDalDbSpecific struct {
}
func (_ UserDalDbSpecific) GetUser(id int) User {
some_db.executeQuery(...)
...
return user
}
register_dal() {
dal.UserDal = UserDalDbSpecific{}
}
User code package:
import (
"dal"
"some_db" <-- Fail here!
)
func someFunc() {
user := dal.User.GetUser(1) // Right way
some_db.DoSomething() <-- Fail here!
}
Slightly more reliable than grep: parse the target source using the standard parser package and inspect the AST. You'd be looking for ImportSpec nodes matching the DB access packages. Fail the test if any are found.

I'm trying to import either GoogleAPIClient or GoogleAPIClientForREST

I'm trying to follow Google's tutorial on making their QuickStart app to learn how to make API calls with Swift. I followed the tutorial completely and ended up with this code
import GoogleAPIClient
import GTMOAuth2
import UIKit
class ViewController: UIViewController {
private let kKeychainItemName = "Drive API"
private let kClientID = "592019061169-nmjle7sfv8i8eahplae3cvto2rsj4gev.apps.googleusercontent.com"
// If modifying these scopes, delete your previously saved credentials by
// resetting the iOS simulator or uninstall the app.
private let scopes = [kGTLAuthScopeDriveMetadataReadonly]
private let service = GTLServiceDrive()
let output = UITextView()
// When the view loads, create necessary subviews
// and initialize the Drive API service
override func viewDidLoad() {
super.viewDidLoad()
output.frame = view.bounds
output.editable = false
output.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0)
output.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
view.addSubview(output);
if let auth = GTMOAuth2ViewControllerTouch.authForGoogleFromKeychainForName(
kKeychainItemName,
clientID: kClientID,
clientSecret: nil) {
service.authorizer = auth
}
}
// When the view appears, ensure that the Drive API service is authorized
// and perform API calls
override func viewDidAppear(animated: Bool) {
if let authorizer = service.authorizer,
let canAuth = authorizer.canAuthorize, canAuth {
fetchFiles()
} else {
presentViewController(
createAuthController(),
animated: true,
completion: nil
)
}
}
// Construct a query to get names and IDs of 10 files using the Google Drive API
func fetchFiles() {
output.text = "Getting files..."
let query = GTLQueryDrive.queryForFilesList()
query.pageSize = 10
query.fields = "nextPageToken, files(id, name)"
service.executeQuery(
query,
delegate: self,
didFinishSelector: "displayResultWithTicket:finishedWithObject:error:"
)
}
// Parse results and display
func displayResultWithTicket(ticket : GTLServiceTicket,
finishedWithObject response : GTLDriveFileList,
error : NSError?) {
if let error = error {
showAlert("Error", message: error.localizedDescription)
return
}
var filesString = ""
if let files = response.files(), !files.isEmpty {
filesString += "Files:\n"
for file in files as! [GTLDriveFile] {
filesString += "\(file.name) (\(file.identifier))\n"
}
} else {
filesString = "No files found."
}
output.text = filesString
}
// Creates the auth controller for authorizing access to Drive API
private func createAuthController() -> GTMOAuth2ViewControllerTouch {
let scopeString = scopes.joinWithSeparator(" ")
return GTMOAuth2ViewControllerTouch(
scope: scopeString,
clientID: kClientID,
clientSecret: nil,
keychainItemName: kKeychainItemName,
delegate: self,
finishedSelector: "viewController:finishedWithAuth:error:"
)
}
// Handle completion of the authorization process, and update the Drive API
// with the new credentials.
func viewController(vc : UIViewController,
finishedWithAuth authResult : GTMOAuth2Authentication, error : NSError?) {
if let error = error {
service.authorizer = nil
showAlert("Authentication Error", message: error.localizedDescription)
return
}
service.authorizer = authResult
dismissViewControllerAnimated(true, completion: nil)
}
// Helper for showing an alert
func showAlert(title : String, message: String) {
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: UIAlertControllerStyle.Alert
)
let ok = UIAlertAction(
title: "OK",
style: UIAlertActionStyle.Default,
handler: nil
)
alert.addAction(ok)
presentViewController(alert, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
My problem is that for
import GoogleAPIClient
I get the error "No such module GoogleAPIClient", which seems weird to me since GTMOAuth2 doesn't get an error, even though it's part of the same Pod I think (I'm new to this, so I'm probably butchering the terminology).
From researching the problem, I found that GoogleAPIClientForREST should be substituted for GoogleAPIClient. This document on GitHub says to just use GoogleAPIClientForREST in the code instead of GoogleAPIClient, but I get the same error with that as well.
Then I thought maybe I could re-install the pods with some changes to Google's tutorial. In the tutorial, it says to execute this code in Terminal
$ cat << EOF > Podfile &&
> platform :ios, '7.0'
> use_frameworks!
> target 'QuickstartApp' do
> pod 'GoogleAPIClient/Drive', '~> 1.0.2'
> pod 'GTMOAuth2', '~> 1.1.0'
> end
> EOF
> pod install &&
> open QuickstartApp.xcworkspace
So I thought maybe I could replace GoogleAPIClient for GoogleAPIClientForREST in the terminal code, but that landed me with the same error
As you can see in the screenshot, the framework is there on the left-hand side, but I'm still getting the "No such module" error.
Embedded Binaries and Linked Frameworks
Search Paths
I also found some suggestions here that I tried to follow, but I didn't completely understand the explanation. Nevertheless, I tried, and did this (if I did it wrong please tell me):
So I'm trying to get either GoogleAPIClient or GoogleAPIClientForREST to work. Thank you for your help
Use this for your Podfile:
platform :ios, '7.0'
use_frameworks!
target 'QuickstartApp' do
pod 'GoogleAPIClientForREST/Drive', '~> 1.1.1'
pod 'GTMOAuth2', '~> 1.1.0'
end
Change your import to
import GoogleAPIClientForREST
Then follow the instructions here to migrate the project:
Migrating from GoogleAPIClient to GoogleAPIClientForREST
This mostly involves changing GTL calls to GTLR calls with some word swapping. For example, GTLServiceDrive becomes GTLRDriveService.
Regarding framework search paths, this image shows the section you might need to change (note it works for me using the default):
Search paths can be per target, too. Here's an image showing the application target and the framework search paths:
So I followed the Quickstart tutorial exactly as well and was able to get it working. I moved the GoogleAPIClientForRest in Framework Search Paths above GTMOAuth2:
Screenshot
I ran into an error in the code after successfully including the module and had to change this line to get it to build and run:
if (result.files!.count to if (result.files!.count > 0).
Of course now, Google has deprecated GTMOAuth2 and replaced it with GTMAppAuth, which renders this app useless.
Although the solution towards which I am pointing you might be for other library, but it will help you for sure. https://stackoverflow.com/a/25874524/5032645 . Please try and let me know, if I should simplify it more for you.
First, look at the Pods_QuickstartApp.framework in the Frameworks group of your Quickstart project. If it is still red, as it is on your screenshot, then Xcode didn't build it. If Xcode didn't build the framework, Xcode can't import it for you.
Cocoapods builds a workspace including your app project, plus another project that assembles your individual pod frameworks into a larger framework.
It seems cocoapods built your workspace, and you did open the workspace instead of the project. That's good.
Check the contents of the file named "Podfile". It should match:
platform :ios, '7.0'
use_frameworks!
target 'QuickstartApp' do
pod 'GoogleAPIClient/Drive', '~> 1.0.2'
pod 'GTMOAuth2', '~> 1.1.0'
end
If it doesn't, fix it, exit Xcode, delete the .xcodeworkspace file, and then run
pod install
from the console. That may fix your dependencies so that Xcode builds the frameworks.
If you do get it to compile, your problems have just begun. Google has deprecated the OAAuth authorization from an embedded user-agent.

httptest.ResponseRecorder has no field or method Result

So this one is really weird, I'm trying to get a mock response that renders JSON. My test looks like this:
import (
"fmt"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"testing"
)
...
func TestMessageFromResponse(t *testing.T) {
mc := MyController{}
body := "{ \"message\":\"kthxbai\" }"
w := httptest.NewRecorder()
w.Write([]byte(body))
resp := w.Result()
msg := mc.messageFromResponse(resp)
if msg != "kthxbai" {
t.Errorf("Expected response body to be kthxbai but was %s", msg)
}
}
I'm testing the method messageFromResponse on MyControllerbut its not even building. When I run go test in my project directory, I get the following error:
./my_controller_test.go:115: w.Result undefined (type *httptest.ResponseRecorder has no field or method Result)
I should also mention that I am successfully using httptest.ResponseRecorder as a writer stub elsewhere in this same file, but it's just failing when I try to access Result().
I initially addressed this as a comment because I was unsure of relevance, but for posterity:
The online godoc always references the latest release. In this case, the Result() method was added in Go1.7 which only released last week. When in doubt, check your local godoc by running godoc -server or godoc <packagename>.