gpt4 book ai didi

c# - 如何使用 NUnit 在 C# 中对线程安全通用列表进行单元测试?

转载 作者:太空狗 更新时间:2023-10-30 00:36:19 26 4
gpt4 key购买 nike

我问了一个关于构建自定义的问题Thread Safe Generic List现在我正在尝试对其进行单元测试,但我完全不知道该怎么做。由于锁定发生在 ThreadSafeList 类内部,因此我不确定如何在尝试模仿多次添加调用时使列表锁定一段时间。谢谢。

一次可以添加一个项目

[Test]
public void Can_add_one_item_at_a_time() //this test won't pass
{
//I am not sure how to do this test...
var list = new ThreadSafeList<string>();

//some how need to call lock and sleep inside list instance
//say somehow list locks for 1 sec

var ta = new Thread(x => list.Add("a"));
ta.Start(); //does it need to aboard say before 1 sec if locked

var tb = new Thread(x => list.Add("b"));
tb.Start(); //does it need to aboard say before 1 sec if locked

//it involves using GetSnapshot()
//which is bad idea for unit testing I think
var snapshot = list.GetSnapshot();
Assert.IsFalse(snapshot.Contains("a"), "Should not contain a.");
Assert.IsFalse(snapshot.Contains("b"), "Should not contain b.");
}

Snapshot_should_be_point_of_time_only

[Test]
public void Snapshot_should_be_point_of_time_only()
{
var list = new ThreadSafeList<string>();
var ta = new Thread(x => list.Add("a"));
ta.Start();

ta.Join();
var snapshot = list.GetSnapshot();

var tb = new Thread(x => list.Add("b"));
tb.Start();

var tc = new Thread(x => list.Add("c"));
tc.Start();

tb.Join();
tc.Join();

Assert.IsTrue(snapshot.Count == 1, "Snapshot should only contain 1 item.");
Assert.IsFalse(snapshot.Contains("b"), "Should not contain a.");
Assert.IsFalse(snapshot.Contains("c"), "Should not contain b.");
}

实例方法

public ThreadSafeList<T> Instance<T>()
{
return new ThreadSafeList<T>();
}

最佳答案

让我们看看您的第一个测试,Can_add_one_item_at_a_time

首先,您的退出条件没有意义。两项都应该添加,一次一项。所以您的测试当然会失败。

您也不需要制作快照;请记住,这是一个测试,在您的测试运行期间,不会有任何其他内容触及该列表。

最后但并非最不重要的一点是,您需要确保在所有线程实际完成之前不要尝试评估退出条件。最简单的方法是使用计数器和等待事件。这是一个例子:

[Test]
public void Can_add_from_multiple_threads()
{
const int MaxWorkers = 10;

var list = new ThreadSafeList<int>(MaxWorkers);
int remainingWorkers = MaxWorkers;
var workCompletedEvent = new ManualResetEvent(false);
for (int i = 0; i < MaxWorkers; i++)
{
int workerNum = i; // Make a copy of local variable for next thread
ThreadPool.QueueUserWorkItem(s =>
{
list.Add(workerNum);
if (Interlocked.Decrement(ref remainingWorkers) == 0)
workCompletedEvent.Set();
});
}
workCompletedEvent.WaitOne();
workCompletedEvent.Close();
for (int i = 0; i < MaxWorkers; i++)
{
Assert.IsTrue(list.Contains(i), "Element was not added");
}
Assert.AreEqual(MaxWorkers, list.Count,
"List count does not match worker count.");
}

现在这确实有可能 Add 发生得如此之快以至于没有两个线程会同时尝试这样做。 No Refunds No Returns 部分解释了如何插入有条件的延迟。我实际上会定义一个特殊的测试标志,而不是 DEBUG。在您的构建配置中,添加一个名为 TEST 的标志,然后将其添加到您的 ThreadSafeList 类中:

public class ThreadSafeList<T>
{
// snip fields

public void Add(T item)
{
lock (sync)
{
TestUtil.WaitStandardThreadDelay();
innerList.Add(item);
}
}

// snip other methods/properties
}

static class TestUtil
{
[Conditional("TEST")]
public static void WaitStandardThreadDelay()
{
Thread.Sleep(1000);
}
}

只要构建配置定义了 TEST 标志,这将导致 Add 方法在实际添加项目之前等待 1 秒。整个测试至少需要10秒;如果它完成得比这快,那就有问题了。

考虑到这一点,我将把第二个测试留给你。这是相似的。

关于c# - 如何使用 NUnit 在 C# 中对线程安全通用列表进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2338450/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com