当前位置: 首页 > 知识库问答 >
问题:

无限轮询方法的单元测试

嵇浩淼
2023-03-14

我有一个方法,当应用程序启动时在新任务中启动,当它结束时停止。该方法每半秒执行一些逻辑。我不知道该如何测试它——有线索吗?

public async Task StartPoll(CancellationToken token)
{
  while(!token.IsCancellationRequested)
  {
    try 
    {
       var dict = dependecy.getDictionary();
       var model = Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(dict));
       udpDistributer.Send(model, model.Length, _distributionEndPoint);
    }
    finally
    {
      await Task.Delay(500, Token);
    }
  }
}

其中,udpDistributer是一个来自DI的新UdpClient(0),而\u distributionEndPoint是一个iEndpoint

基本上,我想检查逻辑是否有效,以及是否以适当的间隔发送了正确的数据。如何使用xunit实现这一点?

编辑

我已经走到这一步了:

   // arrange
   ...
   cts = new CancellationTokenSource(timeout);

   //act
   await sut.StartPoll(cts.Token);

   // assert
   mockUdpDistributer.Verify(x => x.Send(expectedModel, expectedModel.length,
             It.IsAny<IPEndPoint>()), Times.Exactly(expectedTimes));

但是,这会导致测试失败,出现TaskCanceledException

共有3个答案

空慈
2023-03-14

UdpClient如果可能,应该包装在抽象中,StartPoll应该返回任务

要测试是否实现了所需的行为,请使用令牌在已知时间后取消,并检查IUdpClient的次数。已调用发送

下面是一个过于简化的示例

[TestFixture]
public class MyTestClass {
    
    [Test]
    public async Task MyTestMethod() {
        //Arrange
        int expectedTimes = 3; 
        var timeout = TimeSpan.FromMilliseconds(1800);
        CancellationTokenSource cts = new CancellationTokenSource();
        var mockUdpDistributer = new  Mock<IUdpClient>();
        var sut = new MyClass(mockUdpDistributer.Object);

        //Act
        Task start = sut.StartPoll(cts.Token);
        await Task.Delay(timeout);
        cts.Cancel();

        //Assert
        mockUdpDistributer
            .Verify(x => 
                x.Send(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<IPEndPoint>())
                , Times.AtLeast(expectedTimes)
            );
    }
}

使用抽象客户端

public interface IUdpClient {
    void Send(byte[] datagram, int bytes, System.Net.IPEndPoint endPoint);
}

可以被嘲笑并注射到被测试者体内

class MyClass {
    private readonly IUdpClient udpDistributer;
    private readonly IPEndPoint _distributionEndPoint;

    public MyClass(IUdpClient udpDistributer) {
        this.udpDistributer = udpDistributer;
    }

    public async Task StartPoll(CancellationToken token) {
        while (!token.IsCancellationRequested) {
            try {
                object dict = new object();//<-- dependecy.getDictionary();
                byte[] model = Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(dict));
                udpDistributer.Send(model, model.Length, _distributionEndPoint);
            } finally {
                await Task.Delay(500, token);
            }
        }
    }
}
司徒茂实
2023-03-14

此留档异步编程单元测试异步代码可能正是您正在寻找的。

实际上,它说的是使用异步测试方法并等待函数:

[TestMethod]
public async Task CorrectlyFailingTest()
{
  await SystemUnderTest.FailAsync();
}

还要尽量避免像这样的签名者private async void StartPoll。最好总是从异步代码返回Task

作为如何测试调用的一部分,我还假设udpDistributer是实际实现的接口。因此,您可以模拟/计数/设置模拟接口,以确保其按预期工作。使用Moq模拟单元测试的异步方法

要使测试在某一点停止,您可以使用取消令牌取消异步任务后一段时间

笪栋
2023-03-14

您可以验证udpDistributer。Send()(我假设您已经注入了,如果没有,那么就这样做)在您需要它的时间内被调用适当的次数,然后取消令牌。

 类似资料:
  • 问题内容: 我有以前具有大量方法的类,因此我将此方法的工作细分为“辅助”方法。 这些辅助方法声明为强制执行封装- 但是我想对大型公共方法进行单元测试。是否也可以对辅助方法进行单元测试,好象其中的一个失败,而调用它的公共方法也会失败,这样我们就可以确定为什么失败了? 另外,为了使用模拟对象测试这些对象,我需要将其可见性从私有更改为受保护,这是否可取? 问题答案: 一种方法是省略测试并将其放在同一程序

  • 测试失败,因为为。为什么不以阻塞的方式在ViewModel中运行块? 我知道,如果将其转换为返回对象的方法,则可以通过调用获取对象,或者可以返回并调用。但是,我想通过将我的ViewModel方法保留为函数来实现这一点,有没有办法做到这一点呢?

  • 本文向大家介绍详解.Net单元测试方法,包括了详解.Net单元测试方法的使用技巧和注意事项,需要的朋友参考一下 1、测试异常 可以直接对方法进行异常测试,也可以对模拟对象进行异常测试,但是,对模拟对象进行异常测试,很少用,所以,这里就介绍对方法的异常测试。请看如下代码,当用户名为空的时候,抛出异常。 例如 测试代码如下 2、测试返回值 这里用到了一个LastCall的一个类,比较常用,一些辅助测试

  • 问题内容: 阅读并学习单元测试,尝试理解下面的文章,该文章解释了静态函数调用的困难。 我不清楚这个问题。我一直认为静态函数是在类中舍入实用函数的一种好方法。例如,我经常使用静态函数调用进行初始化,即: //阅读这篇文章后,我现在的目标是… 但是,我为此课程编写的几十个测试是相同的。我什么都没改变,他们仍然都过去了。难道我做错了什么? 该帖子的作者指出: 静态方法的基本问题是它们是过程代码。 我不知

  • 我已经开始考虑在我的项目中围绕一些业务逻辑添加一些单元测试。 我想测试的第一个方法是服务层中的一个方法,它返回给定节点的子节点列表。 该方法如下所示: 我想象这样的测试方法是提供一个假树结构,然后测试提供节点是否返回正确的子节点。 ssdsContext是一个对象上下文。 我已经看到可以为提取和接口如何模拟ObjectContext或ObjectQuery 我还读到,as Entity Frame

  • 我实现了一个新类Holder。目标如下: 三个领域。字段1和2是整数,字段3是布尔值 这给我留下的问题是有50个可能的构造函数选项。5 X 5 X 2。这是不明智的或可扩展的(添加第6种类型,我必须编写一个巨大的更改),所以我使用了构建器模式: 这真的很有效。我有5个用于字段1初始化的生成器方法,5个用于字段2初始化的生成器方法,还有一个用于布尔值。设置字段后,调用build()方法,该方法使用格