当前位置: 首页 > 面试题库 >

如何测试不太可能的并发场景?

逄嘉熙
2023-03-14
问题内容

例如,这样的地图访问:

func (pool *fPool) fetch(url string) *ResultPromise {
    pool.cacheLock.RLock()
    if rp, pres := pool.cache[url]; pres {
        pool.cacheLock.RUnlock()
        return rp
    }
    pool.cacheLock.RUnlock()
    pool.cacheLock.Lock()
    if rp, pres := pool.cache[url]; pres {
        pool.cacheLock.Unlock()
        // Skip adding url if someone snuck it in between RUnlock an Lock
        return rp
    }
    rp := newPromise()
    pool.cache[url] = rp
    pool.cacheLock.Unlock()
    pool.c <- fetchWork{rp, url}
    return rp
}

在此,第二if条件的内容不包括在内。但是,通过放置断点,最终无法进入该块。

该示例不是人为设计的,因为:

  1. 如果我们跳过RLock,则在主要读取工作负载时,地图将被不必要地锁定。
  2. 如果我们跳过第二个if,则pool.c <- fetchWork{rp, url}对于同一密钥,最昂贵的工作(在这种情况下,是由处理)可能会发生多次,这是不可接受的。

问题答案:

覆盖该分支的一种方法是模拟pool.cacheLock.Lock(),模拟版本可以将插入url到地图中。因此,在此调用之后再次检查,将找到并执行将进入第二条if语句的主体。

使用界面模拟

模拟的一种方法pool.cacheLock.Lock()是创建pool.cacheLock一个接口,在测试中,您可以设置一个模拟值,该Lock()方法的方法将“脏插入”到映射中。

这是使用以下接口的代码的简化版本pool.cacheLock

type rwmutex interface {
    Lock()
    RLock()
    RUnlock()
    Unlock()
}

type fPool struct {
    cache     map[string]string
    cacheLock rwmutex
}

func (pool *fPool) fetch(url string) string {
    pool.cacheLock.RLock()
    if rp, pres := pool.cache[url]; pres {
        pool.cacheLock.RUnlock()
        return rp
    }
    pool.cacheLock.RUnlock()
    pool.cacheLock.Lock()
    if rp, pres := pool.cache[url]; pres {
        pool.cacheLock.Unlock()
        // Skip adding url if someone snuck it in between RUnlock an Lock
        return rp
    }
    rp := url + "~data"
    pool.cache[url] = rp
    pool.cacheLock.Unlock()
    return rp
}

它的正常用法是:

pool := fPool{
    cache:     map[string]string{},
    cacheLock: &sync.RWMutex{},
}
fmt.Println(pool.fetch("http://google.com"))

还有一个测试用例将触发第二个主体if

type testRwmutex struct {
    sync.RWMutex // Embed RWMutex so we don't have to implement everything
    customLock   func()
}

func (trw *testRwmutex) Lock() {
    trw.RWMutex.Lock()
    if trw.customLock != nil {
        trw.customLock()
    }
}

func TestFPoolFetch(t *testing.T) {
    trw := &testRwmutex{RWMutex: sync.RWMutex{}}
    pool := &fPool{
        cache:     map[string]string{},
        cacheLock: trw,
    }

    exp := "http://google.com~test"
    trw.customLock = func() {
        pool.cache["http://google.com"] = exp
    }

    if got := pool.fetch("http://google.com"); got != exp {
        t.Errorf("Expected: %s, got: %s", exp, got)
    }
}

通过使用功能字段进行模拟

模拟的另一种方法pool.cacheLock.Lock()是将该功能“外包”给一个函数类型的字段,该测试可以替换为一个函数,该函数除了调用此函数外,还进行“脏插入”。

再次简化示例:

func NewFPool() *fPool {
    mu := &sync.RWMutex{}
    return &fPool{
        cache:     map[string]string{},
        cacheLock: mu,
        lock:      mu.Lock,
    }
}

type fPool struct {
    cache     map[string]string
    cacheLock *sync.RWMutex
    lock      func()
}

func (pool *fPool) fetch(url string) string {
    pool.cacheLock.RLock()
    if rp, pres := pool.cache[url]; pres {
        pool.cacheLock.RUnlock()
        return rp
    }
    pool.cacheLock.RUnlock()
    pool.lock()
    if rp, pres := pool.cache[url]; pres {
        pool.cacheLock.Unlock()
        // Skip adding url if someone snuck it in between RUnlock an Lock
        return rp
    }
    rp := url + "~data"
    pool.cache[url] = rp
    pool.cacheLock.Unlock()
    return rp
}

正常用法是:

pool := NewFPool()
fmt.Println(pool.fetch("http://google.com"))

还有一个测试用例将触发第二个主体if

func TestFPoolFetch(t *testing.T) {
    pool := NewFPool()
    oldLock := pool.lock

    exp := "http://google.com~test"
    pool.lock = func() {
        oldLock()
        pool.cache["http://google.com"] = exp
    }

    if got := pool.fetch("http://google.com"); got != exp {
        t.Errorf("Expected: %s, got: %s", exp, got)
    }
}

二。使用一个简单的test标志

这里的想法是,为了支持简单测试,您可以test在实现中构建一个简单标志fPool(例如可以是的字段fPool),并且您要测试的代码会故意检查该标志:

type fPool struct {
    cache     map[string]string
    cacheLock *sync.RWMutex
    test      bool
}

func (pool *fPool) fetch(url string) string {
    pool.cacheLock.RLock()
    if rp, pres := pool.cache[url]; pres {
        pool.cacheLock.RUnlock()
        return rp
    }
    pool.cacheLock.RUnlock()
    pool.cacheLock.Lock()
    if rp, pres := pool.cache[url]; pres || pool.test {
        pool.cacheLock.Unlock()
        // Skip adding url if someone snuck it in between RUnlock an Lock
        return rp
    }
    rp := url + "~data"
    pool.cache[url] = rp
    pool.cacheLock.Unlock()
    return rp
}

现在,如果您想测试2nd的主体if,您要做的就是:

func TestFPoolFetch(t *testing.T) {
    pool := NewFPool()
    pool.test = true

    exp := ""
    if got := pool.fetch("http://google.com"); got != exp {
        t.Errorf("Expected: %s, got: %s", exp, got)
    }
}


 类似资料:
  • 主要内容:如何编写测试场景,测试场景的特征测试场景是测试用例的详细文档,涵盖了线性语句中软件应用程序的端到端功能。线性语句被视为一种场景。测试场景是可测试需求的高级分类。这些要求根据模块的功能进行分组,并从用例中获取。 在测试场景中,由于许多相关的测试用例,因此存在详细的测试过程。在执行测试场景之前,测试人员必须考虑每个场景的测试用例。 在测试场景中,测试人员需要将自己置于用户的位置,因为他们在用户的角度下测试软件应用程序。场景的准备是最

  • 我正试图在Maven中基于cucumber标记运行一套JUnitCucumber特性。我可以让它们按顺序运行,但不能并行运行。我找到了一个名为小胡瓜的软件包,它可以帮我解决这个问题。 然而,我无法让测试运行,我看了github上的示例maven项目,它在Cucumber.class运行良好,但在西葫芦上抛出一个错误。 任何帮助将不胜感激。 我已在pom中包括以下内容 我是在召唤我的跑步者。 我得到

  • 由来 很多时候,我们需要简单模拟N个线程调用某个业务测试其并发状况,于是Hutool提供了一个简单的并发测试类——ConcurrencyTester。 使用 ConcurrencyTester tester = ThreadUtil.concurrencyTest(100, () -> { // 测试的逻辑内容 long delay = RandomUtil.randomLong(

  • Google Apps Script支持将事件传递给触发函数的触发器。不幸的是,开发环境将允许您在没有参数传递的情况下测试函数,因此您无法以这种方式模拟事件。如果您尝试,您会得到一个错误,如: 未定义e。 或者 TypeError:无法读取属性*从未定义 (其中未定义) 人们可以将事件视为可选参数,并使用JavaScript中是否有更好的方法来执行可选函数参数的任何技术将默认值插入触发器函数。但这

  • 问题内容: 我一直在研究Linux内核的某些部分,并找到了如下调用: 要么 我找到了它们的定义: 我知道它们是为了优化,但是它们如何工作?使用它们可以预期多少性能/尺寸下降?至少在瓶颈代码中(当然在用户空间中)值得麻烦(并且可能会失去可移植性)。 问题答案: 它们提示编译器发出指令,这些指令将导致分支预测偏向跳转指令的“可能”一侧。如果预测正确,这将是一个巨大的胜利,这意味着跳转指令基本上是免费的

  • 一、前言 大家好,我是鬼仔。这几天鬼仔精心准备了字节不同岗位的面经汇总!需要的同学可以收藏下,希望大家都能拿到满意的offer~ 【后端开发专场】你不能错过的字节技术岗面经汇总 【算法岗专场】你不能错过的字节技术岗面经汇总 这篇是字节测试开发岗专场,听说只要看了这个面经汇总的牛友们秋招都会顺顺利利哟~~ 插播条广告:鬼仔最近也在帮牛友们内推字节提前批,有意愿投递的牛友可以找鬼仔内推哦,鬼仔为同学们