测试从恐慌中恢复

时间:2015-07-29 16:29:02

标签: go

我想测试一个构造函数,但是如果没有提供某些数据我需要恐慌,我怎样才能从测试中的恐慌中恢复过来?

目前我已经在我的TestNew函数中添加了一个延迟恢复,但是如果我的地图中的某个元素有一个空URL,则不会检查其余部分。

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)
  }
}

3 个答案:

答案 0 :(得分:3)

我想说,为依赖恐慌/恢复的库设计API并不是一种正确的方法。 Go有错误模式,所以如果New方法无法测试,它可以返回状态。

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
}

然后

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

如果您坚持使用panic,那么您可以将调用包装到函数中并在其中恢复。但是你仍然需要向调用者提供状态。

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)
        }()
    }
}

答案 1 :(得分:2)

您的实施存在一些小问题。首先,您甚至无法确定已恢复,因为您正在调用方法但忽略返回值。所以对于初学者来说,可以转换它;

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

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

到此;

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"])
  }
}

现在另一个问题......你正在使用推迟错误。你推迟NewTest的顶部,这意味着当它被调用时,你将要退出新的测试。相反,你想要它在panicing的方法。现在恢复并继续迭代已经太晚了。当你在NewTest被调用的地方恢复时。所以这应该这样做;

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)
  }
}

我做了一个快速的示例问题来证明这一点。将延迟/恢复移动到NewT​​est,您会发现它只打印一次,在当前形式下打印10次,因为当我恢复时,我仍然在循环中。 https://play.golang.org/p/ZA1Ijvsimz

编辑:抱歉,我的例子有点误导,因为当我将代码中的一些内容复制到游乐场时,我将恐慌/恢复位移动到hasUrl。在你的情况下,它实际上就是这个;

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
}

当然,这个例子非常人为,并没有完整的记录。如果你明确地慌乱,我会说调用范围应该是使用recover的,如果你正在调用一个恐慌的库,那么你应该是使用recover的那个。

答案 2 :(得分:0)

我对问题中的词语感到困惑,但我认为它始于我所拥有的同样的关注。你已经定义了一个在违反前提条件时恐慌的函数,而不是一个替代方法(返回错误,使用默认值,什么都不做......);就像为数组做出的合理选择一样:

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

有时候你想要写一个测试,证明一个先决条件违规导致恐慌并且没有被扫地。以前的答案并不完全,所以这就是我所要做的:

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\n", 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")
}

如果测试用例说该函数不应该出现恐慌,最后一个测试用例会检查正常的恐慌处理是否占优势。

相同的代码是on Playground。也可以ignore the return value of recover但仍可靠地报告错过的panic