另一个线程上的单元测试方法

本文关键字:单元 测试方法 线程 另一个 | 更新日期: 2023-09-27 18:07:04

public class Composer
{
    private Task _ComposerTask;    
    private ConcurrentQueue<IValue> _Values;   
    public bool IsConnected { get; }
    // Other dependencies
    private IClient _Client;
    private IWriter _Writer
    public Task async ConnectAsync()
    {
        this.IsConnected = await _Client.ConnectAsync();
        _ComposerTask = Task.Run(() => this.Start());
    }
    private void Start()
    {
        while(this.IsConnected)
        {
            IValue value;
            if(_Values.TryDequeue(out value) == false)
                continue;
            _Writer.Write(value);
        }
    }
    public void Send(IValue value)
    {
        _Values.Enqueue(value);
    }
}

成功连接后Composer类异步执行Start方法(在另一个线程上(。
Start方法检查值队列,如果值存在,则将其向前发送。

我在测试Send方法时遇到的问题。

[Test]
public void Send_ValidMessage_ExecuteWriteMethodWithGivenValue()
{
    // Arrange
    var fakeValue = Mock.Create<IValue>();
    var fakeWriter = Mock.Create<IWriter>();
    var fakeClient = Mock.Create<IClient>();
    Mock.Arrange(() => fakeClient.ConnectAsync().Returns(CompletedTask);
    var composer = new Composer(fakeClient, fakeWriter);
    // for (int i = 0; i < 10; i++)
    // {
    //     composer.Send(Mock.Create<IValue>());
    // }
    composer.ConnectAsync().Wait();
    // Act
    composer.Send(fakeValue);
    // Assert
    Mock.Assert(() => fakeWriter.Write(fakeValue), Occurs.Once());
}

带有注释for loop测试通过。但是,如果执行for loop并且内部队列在添加预期值之前甚至会填充 10 个值,则测试失败并显示消息:预期至少一次,但发生 0 次

据我了解,断言发生在值被另一个线程排队之前,但是如何测试这种行为?

另一个线程上的单元测试方法

你需要构建某种"连接"。这样的事情应该就足够了:

public Task JoinAsync()
{
  this.IsConnected = false;
  return _ComposerTask;
}

请注意,您还应该在生产代码中使用它。如果你的代码最终没有遵守_ComposerTask,那么Start抛出的任何异常都会被默默吞噬。

我想

出的解决方案是重新设计Composer类或更具体地将Send方法更改为异步:

public Task SendAsync(IValue value)
{
}

背后的想法是返回Task当给定的值在"后台"线程上向前组合时,它将完成。

单元测试只需要await,直到任务完成并断言正确执行。

[Test]
public async Task SendAsync_ValidMessage_ExecuteWriteMethodWithGivenValue()
{
    // Arrange
    var composer = TestFactory.GenerateComposer();
    // var tasks = new List<Task>();
    // for (int i = 0; i < 10; i++)
    // {
    //     tasks.Add(composer.SendAsync(Mock.Create<IValue>()));
    // }
    await composer.ConnectAsync();
    // Act
    await composer.SendAsync(fakeValue);
    // Assert
    Mock.Assert(() => fakeWriter.Write(fakeValue), Occurs.Once());
}

即使没有在for loop中添加额外的值,我最初的单元测试也没有成功。如果测试运行多次,则偶尔会失败。我认为原因是线程池的"不可预测"工作。

我仍然不确定如何处理需要在启动实例的整个生命周期内运行的Task,但这将是另一个问题。