【问题标题】:Async UnitTests setup in c#c# 中的异步单元测试设置
【发布时间】:2015-10-20 22:45:38
【问题描述】:

我的每个单元测试都需要一个登录用户,这迫使我在测试SetUp 中进行异步调用(登录)。

我找不到方法来完成这项工作,我要么得到空指针异常,要么设置无效签名。

public async void SetUp() {}

这使得我所有的测试都失败了,可能是因为我没有登录。

public async Task SetUp() {}

忽略我的所有测试,因为设置的签名无效。

而且我不想在每次测试中都复制我的 X 行设置,因为它们都完全相同,而且……这就是设置的目的。

我错过了什么?这似乎是一个微不足道的问题。

这是我现在所拥有的,为了展示一些东西

CreateTicketViewModel _viewModel;

        [SetUp()]
        public async void SetUp()    //I have tried using Task instead of void
        {

            IUserService userService = Dependency.Instance.Resolve<IUserService>();
            await userService.LoginAsync(this.UserName, this.Password);

            _viewModel = Dependency.Instance.Resolve<CreateTicketViewModel>();
        }

        [TearDown()]
        public void TearDown()
        {
            _viewModel = null;  // I have tried removing this
        }

        [Test()]
        public void Initialization()
        {
            // If I put what's in SetUp here and add "async" before void,
            // it works just fine

            Assert.IsNotNull(_viewModel);
            Assert.IsNotNull(_viewModel.Ticket);
        }

【问题讨论】:

  • 我已经阅读了很多关于在 SUT 中没有 async void 的博客文章,我没有,但是这些博客文章都没有谈论测试本身。我有设计问题吗?
  • 你不能让你的测试方法异步而不是设置方法吗?或者您甚至可以使 SetUp 方法同步。不过建议使用前者。
  • 只有断言的异步测试会显示警告。如果可能的话,我希望避免这种情况。
  • 我不明白你的代码是如何工作的。它只是在Setup方法中使用IUserService
  • 我正在使用 UserService 登录(在测试的情况下使用硬编码数据),但这隐藏在这里。然后通过依赖注入创建我的视图模型,它需要登录到它的构造函数中。

标签: c# unit-testing asynchronous async-await nunit


【解决方案1】:

根据您使用的单元测试框架,async 设置可能无法被框架正确处理。

在 NUnits 的情况下,我认为它还不支持异步设置方法。

所以你应该在你的设置中做的是同步等待登录完成:

userService.LoginAsync(this.UserName, this.Password).Wait();

编辑: 看起来这是一个未解决的问题https://github.com/nunit/nunit/issues/60

MSTests 也是如此。

【讨论】:

    【解决方案2】:

    你能不能让你的 SetUp 方法根本不异步并写这个?

    new Task(() => userService.LoginAsync(this.UserName, this.Password)).RunSynchronously()
    

    【讨论】:

    • 当我这样做时,我的 userService 的内容在测试开始时仍然为空,因此失败。请注意,在 LoginAsync 方法后面还有更多异步调用。
    • 虽然我喜欢同步运行登录的想法
    【解决方案3】:

    不支持异步设置,但支持异步测试方法。你可以让你的测试方法异步而不是设置方法。

    [TestFixture]
    public class AsyncSetupTest
    {
        private Task<CreateTicketViewModel> viewModelTask;
    
        [SetUp()]
        public void SetUp()
        {
            viewModelTask = Task.Run(async () =>
            {
                IUserService userService = Dependency.Instance.Resolve<IUserService>();
                await userService.LoginAsync(this.UserName, this.Password);
    
                return Dependency.Instance.Resolve<CreateTicketViewModel>();
            });
        }
    
        [Test()]
        public async Task Initialization()
        {
            CreateTicketViewModel viewModel = await viewModelTask;
    
            Assert.IsNotNull(viewModel);
            Assert.IsNotNull(viewModel.Ticket);
        }
    }
    

    想法是,我们不是在Setup 方法中完成所有设置工作,而是创建一个Task 表示设置完成并在Test 方法中等待它。

    这样您就不会重复所有设置逻辑。但只是从所有测试方法中的Task 中提取 ViewModel。

    【讨论】:

    • 这会起作用,但仍然需要我在每个测试中都有一个重复的行;但实际上它已经比我做的更好了。谢谢你。另一方面,Dan Dinu 的回答提供了一个“按原样”工作的解决方案,能够在我的测试中只使用我的断言,这意味着我的设置中的代码和代码块更小。
    • @Zil 可以。但是阻塞异步代码从来都不是一个好主意。 You could get a deadlockAlso refer this.
    • 我同意生产代码,但我确实希望我的测试在登录过程中在这里等待。您认为在这种特定情况下有那么糟糕吗?知道我在这里没有进行任何其他异步调用(而不是在登录本身内部)
    • 对于测试也许阻塞是好的。但是如果你安装了一些自定义的同步上下文,even unit tests can deadlock.(修改评论添加链接)
    猜你喜欢
    • 1970-01-01
    • 2019-08-02
    • 1970-01-01
    • 1970-01-01
    • 2012-07-15
    • 1970-01-01
    • 1970-01-01
    • 2021-03-25
    • 2011-11-07
    相关资源
    最近更新 更多