测试从恐慌中恢复

I want to test a constructor, but in case some data is not provided I need to panic, how can I recover from that panic in testing?

At the moment I've added in my TestNew func a defer with recover, but in case an element from my map has an empty URL the rest will not be checked.

t.go

package testing

type test {
  url string
}

func New(ops map[string]string) *test {
  if ops["url"] == "" {
    panic("Url missing")
  }
  var t = new(test)
  t.url = ops["url"]
  return t
}

t_test.go

package testing

type testTest map[string]string
var testingTest = []testTest {
  testTest {
    "url": "test",
  },
  testTest{
    "url": "",
  },
}

func NewTest(t *testing.T) {
  defer func() {
    recover()
  }()

  for _, e := range testingTest {
    url := New(e)
    url.hasUrl(t, e["url"])
  }
}

func (s *test) hasUrl(t *testing.T, u string) {
  if s.url != u {
    t.Errorf("Expected %s to be equal with %s", s.url, u)
  }
}

I would say that designing API for library that relies on panic/recover is not a right way. Go has error pattern so if the method New cannot test, it can return the status instead.

package testing

type test {
  url string
}

func New(ops map[string]string) (*test, bool) {
  if ops["url"] == "" {
    return nil, false
  }
  var t = new(test)
  t.url = ops["url"]
  return t, true
}

and then

for _, e := range testingTest {
  url, ok := New(e)
  if ok {
    url.hasUrl(t, e["url"])
  }
}

If you insist on using panic, then you can wrap the invocation into function and recover in it. But then you still would need to provide the status to the caller.

package main

import "fmt"

func test(e int) {
    if e == 2 {
        panic("panic!")
    }
}

func main() {
    for _, e := range []int{1, 2, 3} {
        func() {
            defer func() { recover() }()
            test(e)
            fmt.Println("testing", e)
        }()
    }
}

There are a few small problems with your implementation. For one, you can't even be sure you have recovered because you're calling the method but ignoring the return value. So for starters lets convert this;

func NewTest(t *testing.T) {
  defer func() {
    recover()
  }()

  for _, e := range testingTest {
    url := New(e)
    url.hasUrl(t, e["url"])
  }
}

to this;

func NewTest(t *testing.T) {
  defer func() {
    if r := recover(); r != nil {
            fmt.Println("Recovered in NewTest", r)
        }
  }()

  for _, e := range testingTest {
    url := New(e)
    url.hasUrl(t, e["url"])
  }
}

Now the other problem... You're using defer wrong. You defer at the top of NewTest meaning when it is called, you're about to exit new test. Instead you want it in the method that is panicing. It's too late to recover and continue iteration at the point you have it now. When you recover it's at the place where NewTest was called. So this should do it;

func (s *test) hasUrl(t *testing.T, u string) {
  defer func() {
        if r := recover(); r != nil {
                fmt.Println("Recovered in NewTest", r)
            }
      }()
  if s.url != u {
    t.Errorf("Expected %s to be equal with %s", s.url, u)
  }
}

I made a quick sample problem to demonstrate this. Move the defer/recover into NewTest and you will find it only prints once, in the current form it prints 10 times because when I recover, I'm still inside the loop. https://play.golang.org/p/ZA1Ijvsimz

EDIT: Sorry my example was a bit misleading because I moved the panic/recover bits into hasUrl when I was copying bits of your code into the playground. In your case it would actually be this;

func New(ops map[string]string) *test {
  defer func() {
       if r := recover(); r != nil {
             fmt.Println("Recovered while panicing in New")
       }
  }
  if ops["url"] == "" {
    panic("Url missing")
  }
  var t = new(test)
  t.url = ops["url"]
  return t
}

Of course that example is pretty contrived and doesn't make a whole log of sense. If you're explicitly panicing, I'd say the calling scope should be the one using recover, if you're calling a library which panics, then you should be the one using recover.

I'm confused by the words in the question, but I think it started with the same concern I had. You have defined a function to panic when a precondition is violated, rather than an alternative (returning an error, using defaults, doing nothing...); just like the reasonable choice made for arrays:

var a [1]int
a[1] = 0 // panic

Sometimes you want to write a test that proves a precondition violation causes a panic and is not swept under the rug. The previous answers aren't quite complete, so this is what I got to:

func GreatBigFunction(url string) {
    if url == "" {
        panic("panic!")
    }
}

func main() {
    var tests = []struct {
        url       string
        completes bool
    }{
        {"a", true},
        {"", false},
        {"b", true},
        {"c", false}, // wrong expectation
        {"", true},   // wrong expectation
    }
    for _, test := range tests {
        fmt.Printf("testing that \"%s\" %s
", test.url, map[bool]string{false: "panics", true: "completes"}[test.completes])
        func() {
            if !test.completes {
                defer func() {
                    p := recover()
                    if p == nil {
                        fmt.Println("t.Fail: should have panicked")
                    }
                }()
            }
            GreatBigFunction(test.url)
        }()
    }

    fmt.Println("Bye")
}

The last test case checks that the normal panic handling prevails if the test case says the function isn't supposed to panic.

Same code is on Playground. It's also possible to ignore the return value of recover but still reliably report a missed panic.