【问题标题】:How to execute C# wpf commands via a unit test如何通过单元测试执行 C# wpf 命令
【发布时间】:2021-06-10 23:00:04
【问题描述】:

我对一般的单元测试和 C# 相当陌生,我正在尝试验证一旦命令有趣,集合具有正确数量的对象。 所以流程应该是:命令执行 -> 方法将对象添加到集合 -> 测试检查集合是否有对象。 当我尝试这个时,测试会抛出错误“System.InvalidOperationException:调用线程必须是 STA,因为许多 UI 组件都需要这个。”

由于该命令在正常操作中运行良好,我认为我设置测试的方式存在一些不正确的地方。我尝试在测试方法中添加 [STAThread] 属性,但仍然出现相同的错误。

我要测试的 ViewModel

namespace Space
{
    public class SampleViewModel : ObservableObject, IPageViewModel
    {        
        private string _id;
        private string _initials;
        private ObservableCollection<ISampleObject> _sampleModels;
        private ICommand _validateId;
        private IdValidator _idValidator;
      
        public ViewModel(string initials)
        {
            Initials = initials;            
        }

        public string Name
        {
            get { return "Sample"; }
        }

        public string Id
        {
            get
            {
                return _id;
            }
            set
            {
                if (value != _id)
                {
                    _id = value;
                    OnPropertyChanged("Id");
                }
            }
        }

        public ObservableCollection<ISampleObject> SampleModels
        {
            get
            {
                if (_sampleModels == null)
                {
                    _sampleModels = new ObservableCollection<ISampleObject>();
                    OnPropertyChanged("SampleModels");
                }
                return _sampleModels;
            }
            set
            {

            }
        }

        public IdValidator IdValidator
        {
            get
            {
                if (_idValidator == null)
                {
                    _idValidator = new IdValidator();
                    OnPropertyChanged("IdValidator");
                }
                return _idValidator;
            }
            set { }
        }

        public ICommand ValidateIdCommand
        {
            get
            {
                if (_validateId == null)
                {
                    _validateId = new RelayCommand(
                        param => ValidateId(AliquotId),
                        Predicate => AliquotId != ""
                    );
                }
                return _validateId;
            }
        }

        /**
         * <summary> This method checks the entered sample ID and attempts to categorise it 
         * and call the correct follow-up methods.
         * </summary>
         **/        
        private void ValidateId(string Id)  // potentailly this should return a bool on successful/unsuccessful validation to make testing easier.
        {
            if (Id != null)
            {   
                if (IdValidator.IsValidId(Id))
                {                    
                    switch (IdValidator.ValidateId(AliquotId))
                    {
                        case IdType.Sample:
                            GetSample(Id);
                            break;                        
                        default:
                            break;
                    }
                }
                else
                {
                    MessageBox.Show("Scanned Code not recognised", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

private void GetSample(string id)
        {
            SampleProvider sampleProvider = new SampleProvider();
            
            SampleModel sampleModel = (SampleModel)sampleProvider .GetById(id);
            if (!sampleModel.IsEnumPlate)
            {
                SampleModels.Add(sampleModel);
            }            
        }

    }
}

测试代码

namespace Space.Tests
{
    [TestClass()]
    public class SampleViewModelTests
    {
        ViewModel viewModel = new ViewModel("string") { 
            Id = "ID string"
        };

        [TestMethod()]
        public void ViewModelTest_SampleModel_Retrieved()
        {
            viewModel.ValidateIdCommand.Execute(viewModel.Id);
            Assert.IsTrue(viewModel.SampleModels.Count > 0);
        }
    }
}
'''

【问题讨论】:

  • 您没有从单元测试中运行ICommand。您应该直接从 UnitTest 调用 RelayCommand 的主体。个人感觉ICommand甚至不应该在ViewModel层。
  • 你不应该在视图模型中调用MessageBox.Show。您可能会抛出异常,或者使用从用户通知方法中抽象出来的服务接口。

标签: c# wpf unit-testing


【解决方案1】:

调用Execute 是调用命令的方式:

viewModel.ValidateIdCommand.Execute(viewModel.Id);

到目前为止一切顺利。

正如@KlausGütter 所说,您应该模拟对MessageBox.Show 的调用以使其正常工作。

一种常见且简单的方法是创建一个实现接口的对话服务:

public interface IDialogService
{
    void ShowError(string text, string caption);
}

public class DialogService : IDialogService
{
    public void ShowError(string text, string caption) =>
        MessageBox.Show(text, caption, MessageBoxButton.OK, MessageBoxImage.Error);
}

...并用接口注入视图模型:

private readonly IDialogService _dialogService;
public ViewModel(string initials, IDialogService dialogService)
{
    Initials = initials;
    _dialogService = dialogService ?? throw new ArgumentNullException(nameof(dialogService));
}


private void ValidateId(string Id)  // potentailly this should return a bool on successful/unsuccessful validation to make testing easier.
{
    if (Id != null)
    {
        if (IdValidator.IsValidId(Id))
        {
            switch (IdValidator.ValidateId(AliquotId))
            {
                case IdType.Sample:
                    GetSample(Id);
                    break;
                default:
                    break;
            }
        }
        else
        {
            _dialogService.Show("Scanned Code not recognised", "Error");
        }
    }
}

【讨论】:

  • 谢谢你,原来是 Mouse.OverrideCursor 也导致了这个问题。我也必须为此做一些类似的事情。
猜你喜欢
  • 1970-01-01
  • 2010-10-07
  • 1970-01-01
  • 2011-04-29
  • 1970-01-01
  • 2013-12-12
  • 2023-04-09
  • 2011-04-25
  • 2015-02-23
相关资源
最近更新 更多