package main
import ( "bufio" "fmt" "os" "strings")
func main() {
fmt.Printf("Hello folks!\n") result, err := doSomeStuff("filename.txt", "find_these", 10) if err != nil { fmt.Printf("doThisStuff did not succeed! %v\n", err) } fmt.Printf("result length is: %v\n", len(result))}
func doSomeStuff(thing1 string, thing2 string, thing3 int) (map[string]int, error) {
mystep1 with: thing1 // <---------- hide all function parameters except for thing1
file, err := os.Open(thing1) if err != nil { return nil, err } defer file.Close()
var lines []string scanner := bufio.NewScanner(file) for scanner.Scan() { lines = append(lines, scanner.Text()) } err = scanner.Err() if err != nil { return nil, err }
mystep2 with: lines, thing2 // <---------- hide all local variables from above except for lines and thing2
mymap := make(map[string]int) for _, line := range lines { if strings.Contains(line, thing2) { count := strings.Count(line, thing2) mymap[line] = count } }
mystep3 with: mymap, thing3 // <---------- hide all local variables from above except for mymap and thing3 for k, v := range mymap { if v > thing3 { mymap[k] = 0 } }
return mymap, nil}
I often write a function or module to handle some process that takes 3 or 4 steps to complete.After I am happy with the code I then proceed to write tests for the code,but find that I am compelled to chop the code into pieces in order to simplify the test code-- thereby losing the nice top down readability with which I started.The influence that test code has on the structure of the application code is highly undesirable.Test code should not exert so much influence over how the primary code is written.
The labelled with statements would allow for a test to be written specifically for each step of a function.Each test would begin at the with statement, providing the variables and values required, and end at the next with statement or return statement.
// I don't want to get into the specific syntax of the test code directly,
// but a test would start execution here, supplying "lines" and "thing2"
//
mystep2 with: lines, thing2 // <---------- hide all local variables from above except for lines and thing2
mymap := make(map[string]int) for _, line := range lines { if strings.Contains(line, thing2) { count := strings.Count(line, thing2) mymap[line] = count }
}
// and the test would end here -- because the next line is another "with" (or return) statement
// it would be able to do Asserts and such on the variables within the scope of this code segment
mystep2 with: lines, thing2 => mymap
On Mar 1, 2020, at 10:21 AM, Warren Stephens <wste...@prognoshealth.com> wrote:
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/f18a9af6-240b-4f3c-a011-6d68ed69784e%40googlegroups.com.
internal mystep2(lines, thing2): (mymap, err)
So the developer is in the situation of refactoring code that is known to be working in order to write tests! — which are rarely going to be comprehensive anyway. The risk is breaking something that isn’t broken!
With my language proposal above, adding the with statements does not modifying the behavior of the code at all — it should literally compile exactly the same way.
Its main benefit is to enable tests to be more easily written without restructuring the existing code.In my opinion, it would be nice if a developer would never have to restructure code in order to write tests. Tests are quite often written well after the code is in production. That is the real world scenario.Imagine that you are doing a code review on a pull request, and the only thing that has been changed is to add with statements and tests -- Zero risk! Approved! Done!
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/9aff05ef-1c37-4626-a18d-0df418d2d258%40googlegroups.com.
Axel,I like your example. Var e would get re-declared -- so that is a change. However, var x is not visible past the with statement -- only var a is brought forward.So we need another attempt at breaking this. :-)
Splitting the code into separate funcs introduces calls, and I work with big data, many billions of records. So that introduces production inefficiency for the purpose of test writing -- which costs time and money in the cloud.
Warren
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/d55e9c5d-6d07-4c91-92c6-a3c3060790bf%40googlegroups.com.
On Mar 1, 2020, at 4:48 PM, 'Axel Wagner' via golang-nuts <golan...@googlegroups.com> wrote:
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAEkBMfERV6e5QLd5i42SUQqhE%3D7a229o2%2BYyMxiQOJ1vhxp%2B1Q%40mail.gmail.com.
Any decent compiler should in-line these private function calls in all cases - so no function call overhead.
The influence that test code has on the structure of the application code is highly undesirable.
On 2 Mar 2020, at 13:54, Warren Stephens wrote:
Note to others: Software *Engineers* must operate with the 3-way tradeoff
in mind (pick 2 is the old joke):
1. quality (good)
2. time (fast)
3. cost (cheap)
Well, if you are taking "engineering" out of sheaf, then the following essential
parts of any engineering project also deserve a mention:
A. Throwaway write-once, execute-once code.
Stakeholder roles (often both roles are played by the same person) and their preferences:
- Developer. Write ASAP, debug ASAP
- Code user. Get the answer quickly and cheaply.
Does not need tests. with
is irrelevant.
Z. Write once, maintain for 50 years code.
Stakeholder roles and their preferences (roughly):
- Original developer.
Write ASAP, debug ASAP, not to be hassled by maintainer and users.
Maintenance developer.
Fix bugs, add features as expediently as possible. Not to break anything.
Operating engineer.
Make the code work reliably to not be woken up during outages.
Technical writer.
Provide documentation for maintenance developer, users, operator as fast as possible.
Users.
Get the benefit, have bugs and features implemented as fast as possible.
Financier.
Cost of development, operations and maintenance as low as possible.
Project manager.
Time to fix bugs and add features as low as possible.
... the list can go on and on
The price of writing the code for the first time and covering with tests if
it was written without tests in this case is irrelevant.
N. Code that was written once in a hurry, needs to be covered by tests, and does
not change often enough and radical enough to expose brittleness of the tests.
That's the sweet spot. Is there much code like this, and is this use-case
important enough?
--
Misha
What if local var pointers (like x above) were invalid to list in a with statement?Would there then be any other breaking examples? Can it be broken using passed pointers or heap pointers?
--Warren
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/503e963a-49a0-4d35-8555-96fdb0ee7a53%40googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/9e906437-f68f-4564-b7dc-1af31c4eb7cd%40googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/26bba248-8a7c-453b-83ee-ae4043c7b4c1%40googlegroups.com.
On Mar 2, 2020, at 2:53 PM, Warren Stephens <wste...@prognoshealth.com> wrote:
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/e8fbdb4e-1d02-42c5-9c1e-409a846caaa4%40googlegroups.com.
On Mar 2, 2020, at 3:32 PM, Robert Engels <ren...@ix.netcom.com> wrote:
Warren,
"The over-arching goal is for me to write tests more easily. Not avoid writing tests. I am not arguing against tests."
Here is a quick fix of your code;Now, use rdr to quickly test countSomeStuff:ws_test.go: https://play.golang.org/p/10mk88Preotws
├── ws.go
└── ws_test.gows$ go test
PASS
ws$Peter
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/859b245a-22c0-4c9f-a152-c63283ea5825%40googlegroups.com.
AAABBB wwwCCC xxx yyyDDD zzz
===========
AAABBBwwwCCCxxxyyyDDDzzz
Look at the 2 things below. Which is more quickly comprehensible? We cannot conceive of a way to have the upper one and still test each piece???
Warren
AAABBBwwwCCCxxxyyyDDDzzz===========AAABBBwwwCCCxxxyyyDDDzzz
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/be59057f-bd56-4b57-9e23-3c59b1153a47%40googlegroups.com.
rog,Very well said -- but everyone keeps talking about improving the maintainability without talking about the "comprehensiblity".Look at the 2 things below. Which is more quickly comprehensible? We cannot conceive of a way to have the upper one and still test each piece???
Warren
AAABBBwwwCCCxxxyyyDDDzzz===========AAABBBwwwCCCxxxyyyDDDzzz
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/be59057f-bd56-4b57-9e23-3c59b1153a47%40googlegroups.com.
package main
import ( "fmt" "sync")
func main() {
fmt.Printf("Hello folks!\n") err := doSomeStuff(true) if err != nil { fmt.Printf("doThisStuff did not succeed! %v\n", err) } fmt.Printf("Goodbye folks!\n")}
const N = 20
var naturals = make(chan int, N)var squares = make(chan int, N)
func doSomeStuff(dummy bool) error {
var n sync.WaitGroup
// there is a bunch of nonsense added here to get the Go compiler to accept the 4 labels
// Counterstep1: if dummy { n.Add(1) go func() { for x := 0; x < N; x++ { naturals <- x } n.Done() }() n.Wait() if !dummy { goto step1 } }
// Squarerstep2: if dummy { n.Add(1) go func() { for x := 0; x < N; x++ { v := <-naturals squares <- v * v
// Squarer complainer step2A: if v > 1000000 { fmt.Println("v is too big!") v = 0 goto step2A } } n.Done() }() n.Wait() if !dummy { goto step2 } }
// Printerstep3: if dummy { n.Add(1) go func() { for x := 0; x < N; x++ { n := <-squares fmt.Println(x, n) } n.Done() }() n.Wait() if !dummy { goto step3 } }
return nil}
An then it is separately testable, documentable, and no collapsing esitor help is neede when viewing them main function, listing those (sub)steps.
I understand that refactoring is a risk, but adding a new language feature just to keep the current unstructured code, is plain wrong.
Folks,First off thanks for the feedback! -- positive and negative.Second, hopefully people can trust that I fully understand the standard approach to code refactoring and writing tests. I have done a bunch of it, and am sure will continue to do a bunch of it. I understand the merits of it. It is a valuable thing to do for the long run. No pejorative is intended here.That said, I am trying to reset here with another example about a possible new way to have good tests AND have code for which it is easy to quickly understand the relationships between all sub-tasks of the code -- it is modified code from The Go Programming Language book. Note: There is a bunch of "goto nonsense" added to get the Go compiler to accept the 4 labels for compiling.
The attributes that I would like to highlight are:
- a single function which accomplishes a complete task
- which is composed of ordered steps
- where some steps may have sub-steps, and perhaps sub-sub-steps
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/0cb2cd2f-5354-4ead-b0c5-3d9c2fa755c5%40googlegroups.com.
And I am saying Functions PLUS Structure.
Functions remain. Visible structure is added.
Warren
establish[ed] refactoring tools that are well understood and
tested