英文:
How to test code that loops forever
问题
我有一个应用程序(epazote),一旦启动就会一直运行,但我想在它阻塞/等待直到按下<kbd>ctrl</kbd>+<kbd>c</kbd>或被终止之前测试一些值。
这是一个小例子:http://play.golang.org/p/t0spQRJB36
<!-- language: go -->
package main
import (
"fmt"
"os"
"os/signal"
)
type IAddString interface {
AddString(string)
}
type addString struct{}
func (self *addString) AddString(s string) {
fmt.Println(s)
}
func block(a IAddString, s string) {
// 在测试时忽略这部分
a.AddString(s)
// 在测试时忽略这部分
block := make(chan os.Signal)
signal.Notify(block, os.Interrupt, os.Kill)
for {
signalType := <-block
switch signalType {
default:
signal.Stop(block)
fmt.Printf("%q signal received.", signalType)
os.Exit(0)
}
}
}
func main() {
a := &addString{}
block(a, "foo")
}
我想知道在测试时是否可以忽略代码的某些部分,或者如何测试这些情况。我已经实现了一个接口,用于测试AddString
,它帮助我测试了一些部分,但不知道如何避免"block"并进行测试。
有什么想法吗?
更新:将代码放在循环Addstring
中的另一个函数中可以工作,但只适用于测试该函数,但如果我想进行完整的代码覆盖,我仍然需要检查/测试阻塞部分,例如如何测试在接收到<kbd>ctrl</kbd>+<kbd>c</kbd>或kill -HUP
时是否正确行为,我在考虑创建一个虚拟的signal.Notify
,但不知道如何覆盖导入的包,以防这种方法可行。
英文:
I have an app (epazote) that once starts runs forever but I want to test some values before it blocks/waits until <kbd>ctrl</kbd>+<kbd>c</kbd> is pressed or is killed.
Here is an small example: http://play.golang.org/p/t0spQRJB36
<!-- language: go -->
package main
import (
"fmt"
"os"
"os/signal"
)
type IAddString interface {
AddString(string)
}
type addString struct{}
func (self *addString) AddString(s string) {
fmt.Println(s)
}
func block(a IAddString, s string) {
// test this
a.AddString(s)
// ignore this while testing
block := make(chan os.Signal)
signal.Notify(block, os.Interrupt, os.Kill)
for {
signalType := <-block
switch signalType {
default:
signal.Stop(block)
fmt.Printf("%q signal received.", signalType)
os.Exit(0)
}
}
}
func main() {
a := &addString{}
block(a, "foo")
}
I would like to know if is posible to ignore some parts of the code while testing, or how to test this cases, I have implemented an interface, in this case for testing AddString
that helped me to test some parts but have no idea of how to avoid the "block" and test.
Any ideas?
Update: Putting the code inside the loop Addstring
in another function works but only for testing that function, but If I want to do a full code coverage, I still need to check/test the blocking part, for example how to test that is properly behaving when receiving <kbd>ctrl</kbd>+<kbd>c</kbd> or a kill -HUP
, I was thinking on maybe creating a fake signal.Notify
but don't know how to overwrite imported packages in case that could work.
答案1
得分: 11
是的,这是可能的。将循环内的代码放在一个单独的函数中,并对该函数进行单元测试,而不包含循环。
英文:
Yes, it's possible. Put the code that is inside the loop in a separate function, and unit test that function without the loop.
答案2
得分: 7
将测试委托引入你的代码中。
将你的循环提取到一个函数中,该函数接受两个函数作为参数:onBeginEvent和onEndEvent。这两个函数的签名应包含以下内容:
- 你想要在测试用例中检查的状态
- 可选项:循环计数器(这样你可以标识每个循环)。这是可选的,因为实际的委托实现可以自行计算被调用的次数。
在循环开始时,调用OnBeginEvent(counter, currentState);然后你的代码执行正常的工作,在结束时调用OnEndEvent(counter, currentState)。假设你的代码已经改变了currentState。
在生产环境中,你可以使用函数委托的空实现或在循环中实现nil检查。
你可以使用这个模型来放置尽可能多的处理算法检查。假设你有5个检查点。现在你回过头来看,意识到这变得太困难了。你创建一个定义回调函数的接口。这些回调函数是改变服务行为的强大方法。再次回过头来,你会意识到接口实际上是你的"服务策略"
一旦你选择了这条路线,你就会想要以某种方式停止你的无限循环。如果你想在测试用例中进行严格的控制,你可以使用第三个返回值为true的函数委托来判断是否是退出循环的时机。共享变量是控制退出条件的一种选择。
这绝对是比单元测试更高级的测试方法,在复杂的服务中是必要的。
英文:
Introduce test delegates into your code.
Extract your loop into a function that takes 2 functions as arguments: onBeginEvent and onEndEvent. The functions signatures shall take:
- state that you want to inspect inside the test case
- optional: counter of loop number (so you can identify each loop). It is optional because actual delegate implementation can count number of times it was invoked by itself.
In the beginning of your loop you call OnBegingEvent(counter, currentState); than your code does its normal work and in the end you call OnEndEvent(counter, currentState); Presumably your code has changed currentState.
In production you could use an empty implementation of the function delegates or implement nil check in your loop.
You can use this model to put as many checks of your processing algorithms as you want. Let's say you have 5 checks. Now you look back at it and realize that's becoming too hard. You create an Interface that defines your callback functions. These callback functions are a powerful method of changing your service behavior. You step back one more time and realize that interface is actually your "service's policy"
Once you take that route you will want to stop your infinite loop somehow. If you want a tight control within a test case you could take a 3rd function delegate that returns true if it is time to quit from the loop. Shared variable is an option to control quit condition.
This is certainly a higher level of testing than unit testing and it is necessary in complex services.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论