I have this net/http server setup with several middleware in a chain and I can't find examples on how I should test these...
I am using basic net/http with the gorilla/mux router and one Handle looks somewhat like this:
r.Handle("/documents", addCors(checkAPIKey(getDocuments(sendJSON)))).Methods("GET")
In these I aggregate some data and supply them via Gorilla Context context.Set methods.
Usually I test my http functions with httptest, and I hope to do it with these as well but I can't figure out how and I am curious as to what is the best way. Should I test each middleware seperately? Should I prefill the appropriate context values then when they are needed? Can I test this entire chain at once so I can just check desired states on input?
I would not test anything involving Gorilla or any other 3rd party package. If you want to test to make sure it works, i'd setup some external test runner or integration suite for the endpoints of a running version of your app (e.g. a C.I. server).
Instead, test your Middleware and Handlers individually - as those you have control over.
But, if you are set on testing the stack (mux -> handler -> handler -> handler -> MyHandler), this is where defining the middleware globally using functions as vars could help:
var addCors = func(h http.Handler) http.Handler {
...
}
var checkAPIKey = func(h http.Handler) http.Handler {
...
}
During normal use, their implementation remains the same with change.
r.Handle("/documents", addCors(checkAPIKey(getDocuments(sendJSON)))).Methods("GET")
But for unit testing, you can override them:
// important to keep the same package name for
// your test file, so you can get to the private
// vars.
package main
import (
"testing"
)
func TestXYZHandler(t *testing.T) {
// save the state, so you can restore at the end
addCorsBefore := addCors
checkAPIKeyBefore := checkAPIKey
// override with whatever customization you want
addCors = func(h http.Handler) http.Handler {
return h
}
checkAPIKey = func(h http.Handler) http.Handler {
return h
}
// arrange, test, assert, etc.
//
// when done, be a good dev and restore the global state
addCors = addCorsBefore
checkAPIKey = checkAPIKeyBefore
}
If you find yourself copy-n-pasting this boiler plate code often, move it to a global pattern within your unit tests:
package main
import (
"testing"
)
var (
addCorsBefore = addCors
checkAPIKeyBefore = checkAPIKey
)
func clearMiddleware() {
addCors = func(h http.Handler) http.Handler {
return h
}
checkAPIKey = func(h http.Handler) http.Handler {
return h
}
}
func restoreMiddleware() {
addCors = addCorsBefore
checkAPIKey = checkAPIKeyBefore
}
func TestXYZHandler(t *testing.T) {
clearMiddleware()
// arrange, test, assert, etc.
//
restoreMiddleware()
}
A side note on unit testing end points...
Since middleware should operate with sensible defaults (expected to pass normally and not mutex state of the underlying stream of data you want to test in func), I advise to unit test the middleware outside of the context of your actual main Handler function.
That way, you have one set of unit tests strictly for your middleware. And another set of tests focusing purely on the primary Handler of the url you are calling. It makes discovering the code much easier for newcomers.
Related
At 'urichecking2' log, I can see there is value. But in 'uriChecking' the uriList is null.
why the uriList.add not work??
private fun getPhotoList() {
val fileName = intent.getStringExtra("fileName")
Log.d("fileNameChecking", "$fileName")
val listRef = FirebaseStorage.getInstance().reference.child("image").child(fileName!!)
var tmpUrl:Uri = Uri.parse(fileName)
Log.d("firstTmpUri","$tmpUrl")
listRef.listAll()
.addOnSuccessListener { listResult ->
for (item in listResult.items) {
item.downloadUrl.addOnCompleteListener { task ->
if (task.isSuccessful) {
tmpUrl = task.result
Log.d("secondTmpUri","$tmpUrl")
Log.d("urichecking2","$task.result")
uriList.add(task.result)
} else {
}
}.addOnFailureListener {
// Uh-oh, an error occurred!
}
}
}
Log.d("thirdTmpUri","$tmpUrl")
Log.d("urichecking", "$uriList")
}
If I do this, the log is output in the order of first, third, and second, and the desired value is in second, but when third comes out, it returns to the value of first.
The listAll method (like most cloud APIs these days, including downloadUrl which you also use) is asynchronous, since it needs to make a call to the server - which may take time. This means the code executes in a different order than you may expect, which is easiest to see if you add some logging:
Log.d("Firebase","Before starting listAll")
listRef.listAll()
.addOnSuccessListener { listResult ->
Log.d("Firebase","Got listResult")
}
Log.d("Firebase","After starting listAll")
When you run this code it outputs:
Before starting listAll
After starting listAll
Got listResult
This is probably not the order you expected, but it perfectly explains why you can't see the list result. By the time your Log.d("urichecking", "$uriList") runs, none of the uriList.add(task.result) has been called yet.
The solution for this is always the same: any code that needs the list result, has to be inside the addOnCompleteListener callback, be called from there, or be otherwise synchronized.
So in its simplest way:
listRef.listAll()
.addOnSuccessListener { listResult ->
for (item in listResult.items) {
item.downloadUrl.addOnCompleteListener { task ->
if (task.isSuccessful) {
uriList.add(task.result)
Log.d("urichecking", "$uriList")
}
}
}
}
This is an incredibly common mistake to make if you're new to programming with asynchronous APIs, so I recommend checking out
Asynchronous programming techniques in the Kotlin language guide
How to get URL from Firebase Storage getDownloadURL
Can someone help me with logic of the firebase on success listener
Why does my function that calls an API or launches a coroutine return an empty or null value?
I'm starting to learn Rust and the rocket framework https://crates.io/crates/rocket.
I have a dumb question that I can't figure out.
How do I return my_universe that I created on the first line of main() when calling GET /universe/ports/21?
fn main() {
let my_universe = universe::model::Universe::new();
rocket::ignite().mount("/universe", routes![ports]).launch();
}
#[get("/ports/<id>")]
fn ports(id: u16) -> universe::model::Universe {
// need to return my_universe here
}
The issue I'm having is that if I define my_universe within the route controller ports(), it'll recreate the my_universe object on each request. Instead, I need the route to return the same my_universe object on each request
Sharing non-mutable state in rocket is fairly easy. You can add the state with manage during the build of rocket.
rocket::build()
.manage(my_universe) // put the shared state here
.mount("/universe", routes![ports])
If you want to return this state in a route you will have to add both serde as a dependency and the json feature of rocket.
[dependencies]
rocket = { version = "0.5.0-rc.2", features = ["json"]}
serde = "1.0.147"
You can now annotate your struct with Serialize so we can send it as a JSON response later.
#[derive(Serialize)]
struct Universe {
/* ... */
}
And access this state in your route with a &State parameter.
#[get("/ports/<id>")]
fn ports(id: u16, universe: &State<Universe>) -> Json<&Universe> {
Json(universe.inner())
}
Here we can access the inner value of the state and return it as Json.
So far, the state is immutable and can not be changed in the route which might not be what you want. Consider wrapping your state into a Mutex to allow for interior mutability.
I have a service to get db data and get others data from third party api.
Like this:
type Service interface {
GetDataFromDB(params apiParams, thirdClient ApiCient)
}
type Repository interface {
GetDataFromDB(orm *gorm.DB)
}
type DataService struct {
repo Repository
}
func (s *DataService) GetDataFromDB(params apiParams, thirdClient ApiClient) []interface{} {
var result []interface{}
dataFromDb := s.repo.GetDataFromDB()
dataFromAPI := thirdClient.Do(url)
result = append(result, dataFromDb)
result = append(result, dataFromAPI)
return result
}
func getData(c *gin.Context) {
//already implement interface
repo := NewRepository(orm)
srv := NewService(repo)
thirdPartyClient := NewApiClient()
params := ¶ms{Id:1,Name:"hello world"}
res := srv.GetDataFromDB(params, thirdPartyClient)
c.JSON(200,res)
}
func TestGetData(t *testing.T) {
w := httptest.NewRecorder()
request := http.NewRequest(http.MethodGet, "/v1/get_data", nil)
route.ServeHTTP(w, request)
}
And third party api client will return random data.
In this situation, what should I do ?
If I want to mock client to get stable data to test, how to fake it in integration test ?
I am assuming "integration test" means you will be running your entire application and then testing the running instance together with its dependencies (in your case a database & third party service). I assume you do not mean unit testing.
For integration tests you have a few options. In my case, usually I would integration test including whatever the third party client is connecting to (no mocking) because I want to test the integration of my service with the third party one. Or if that is not possible I might write a simple stub application with the same public interface as the third party service and run it on localhost (or somewhere) for my application to connect to during testing.
If you don't want to or can't do either of those and want to stub the external dependency inside your Go application, you can write an interface for the third party client and provide a stubbed implementation of the interface when running integration tests (using a flag on your application to tell it to run in "stubbed" mode or something of that nature).
Here's an example of what this might look like. Here's the source code file you posted - but using an interface for getting the third party data:
type Service interface {
GetDataFromDB(params apiParams, thirdClient ApiCient)
}
type Repository interface {
GetDataFromDB(orm *gorm.DB)
}
type ThirdPartyDoer interface {
Do(url string) interface{}
}
type DataService struct {
repo Repository
thirdParty ThirdPartyDoer
}
func (s *DataService) GetDataFromDB(params apiParams, thirdClient ApiClient) []interface{} {
var result []interface{}
dataFromDb := s.repo.GetDataFromDB()
dataFromAPI := s.thirdParty.Do(url)
result = append(result, dataFromDb)
result = append(result, dataFromAPI)
return result
}
Then you can write a stub implementation for ThirdPartyDoer and use it when testing. When running in Production you can use the real third party client as ThirdPartyDoer's implementation:
type thirdPartyDoerStub struct {}
func (s *thirdPartyDoerStub) Do(url string) interface{} {
return "some static test data"
}
// ...
// Test setup:
integrationTestDataService := &DataService{repo: realRepository, thirdParty: &thirdPartyDoerStub{}}
// Production setup:
integrationTestDataService := &DataService{repo: realRepository, thirdParty: realThirdParty}
You would need to have a flag to select between "test setup" and "production setup" when starting your application.
I have read the set-based consistency validation blog and I want to validate through a dispatch interceptor. I follow the example, but I use reactive repository and it doesn't really work for me. I have tried both block and not block. with block it throws error, but without block it doesn't execute anything. here is my code.
class SubnetCommandInterceptor : MessageDispatchInterceptor<CommandMessage<*>> {
#Autowired
private lateinit var privateNetworkRepository: PrivateNetworkRepository
override fun handle(messages: List<CommandMessage<*>?>): BiFunction<Int, CommandMessage<*>, CommandMessage<*>> {
return BiFunction<Int, CommandMessage<*>, CommandMessage<*>> { index: Int?, command: CommandMessage<*> ->
if (CreateSubnetCommand::class.simpleName == (command.payloadType.simpleName)){
val interceptCommand = command.payload as CreateSubnetCommand
privateNetworkRepository
.findById(interceptCommand.privateNetworkId)
// ..some validation logic here ex.
// .filter { network -> network.isSubnetOverlap() }
.switchIfEmpty(Mono.error(IllegalArgumentException("Requested subnet is overlap with the previous subnet.")))
// .block() also doesn't work here it throws error
// block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-
}
command
}
}
}
Subscribing to a reactive repository inside a message dispatcher is not really recommended and might lead to weird behavior as underling ThreadLocal (used by Axox) is not adapted to be used in reactive programing
Instead, check out Axon's Reactive Extension and reactive interceptors section.
For example what you might do:
reactiveCommandGateway.registerDispatchInterceptor(
cmdMono -> cmdMono.flatMap(cmd->privateNetworkRepository
.findById(cmd.privateNetworkId))
.switchIfEmpty(
Mono.error(IllegalArgumentException("Requested subnet is overlap with the previous subnet."))
.then(cmdMono)));
I'm trying to customize the testing.T with my own assert method to lower the number of lines I'm writing. I tried the following, ended with an error: "wrong signature for TestCustom, must be: func TestCustom(t *testing.T)".
How can I make TestCustom use CustomTester interface with a new method, assert?
I don't want to use a 3rd-party framework.
custom_testing.go
type CustomTester struct {
testing.TB
}
func (t *CustomTester) assert(exp interface{}, act interface{}) {
if exp != act {
t.Errorf("expected: %v. got: %v\n", exp, act)
}
}
// I want testing package inject testing.T here
// But, by using my own wrapper: CustomTester struct with,
// my own assert method to get rid of using t as an argument,
// in each assert like: assert(t, exp, act)
func TestCustom(t *testing.TB) {
t.assert(3, len(foo))
}
NOTE: I also tried this, it works but, I don't want to pass t each time when I'm testing:
working_not_wanted.go
func assert(t *testing.TB, exp interface{}, act interface{}) {
if exp != act {
t.Errorf("expected: %v. got: %v\n", exp, act)
}
}
func TestCustom(t *testing.T) {
assert(t, 3, len(foo))
}
The Go testing framework executes test functions of a specific signature, and that signature takes a *testing.T. If you want to use the stdlib testing system, your test functions have to have the required signature.
You could wrap it with one line in every test function:
func MyTest(stdt *testing.T) {
// This line:
t := &CustomTester{stdt}
t.assert(true)
t.Error("An error done happened")
}
There are other ways to do it, but there is no way to have a testing function, run by go test, using the stdlib testing package, that takes anything other than *testing.T as its sole parameter.