c# – WPF MVVM Light单元测试ViewModels

我不是一个常规的MVVM模式,这基本上是我第一次玩它.

我曾经做过的(“正常”WPF)正在创建一个业务层和数据层(通常包含由一个服务或实体框架创建的实体)的视图.

现在经过一段折磨,我创建了一个MVVM Light的标准模板,并做到这一点:

定位:

public class viewmodelLocator
{
    static viewmodelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

        if (viewmodelBase.IsInDesignModeStatic)
        {
            SimpleIoc.Default.Register<IUserService,DesignUserService>();
        }
        else
        {
            SimpleIoc.Default.Register<IUserService,IUserService>();
        }

        SimpleIoc.Default.Register<Loginviewmodel>();
    }

    public Loginviewmodel Login
    {
        get
        {
            return ServiceLocator.Current.GetInstance<Loginviewmodel>();
        }
    }
}

登录viewmodel:

public class Loginviewmodel : viewmodelBase
{
    private readonly IUserService _userService;

    public RelayCommand<Object> LoginCommand
    {
        get
        {
            return new RelayCommand<Object>(Login);
        }
    }

    private string _userName;
    public String UserName
    {
        get { return _userName; }
        set
        {
            if (value == _userName)
                return;

            _userName = value;
            RaisePropertyChanged("UserName");
        }
    }

    /// <summary>
    /// Initializes a new instance of the Loginviewmodel class.
    /// </summary>
    public Loginviewmodel(IUserService userService)
    {
        _userService = userService;

        _closing = true;
    }

    private void Login(Object passwordBoxObject)
    {
        PasswordBox passwordBox = passwordBoxObject as PasswordBox;
        if (passwordBox == null)
            throw new Exception("PasswordBox is null");

        _userService.Login(UserName,passwordBox.SecurePassword,result =>
        {
            if (!result)
            {
                MessageBox.Show("Wrong username or password");
            }
        });
    }
}

绑定和命令工作正常,所以没有问题.商业模型课设计考试时间:

public class DesignUserService : IUserService
{
    private readonly User _testUser;
    private readonly IList<User> _users;

    public void Login(String userName,securestring password,Action<Boolean> callback)
    {
        var user = _users.FirstOrDefault(u => u.UserName.ToLower() == userName.ToLower());

        if (user == null)
        {
            callback(false);
            return;
        }

        String rawPassword = Security.ComputeHashString(password,user.Salt);
        if (rawPassword != user.Password)
        {
            callback(false);
            return;
        }

        callback(true);
    }

    public DesignUserService()
    {
        _testUser = new User
        {
            UserName = "testuser",Password = "123123",Salt = "123123"
        };

        _users = new List<User>
        {
            _testUser
        };
    }
}

UserData是一个静态类,它调用数据库(Entity Framework).

现在我有我的考试:

[TestClass]
public class Login
{
    [TestMethod]
    public void IncorrectUsernameCorrectPassword()
    {
        IUserService userService = new DesignUserService();

        PasswordBox passwordBox = new PasswordBox
        {
            Password = "password"
        };
        userService.Login("nonexistingusername",b => Assert.AreEqual(b,false));
    }
}

现在我的测试不是在viewmodel本身,而是直接到业务层.

基本上我有两个问题:

我在正确的道路上,还是我的模式实现有根本的缺陷?
>如何测试我的viewmodel?

解决方法

您的视图模型有一个值得测试的相关代码,这是Login方法.鉴于它是私有的,它应该通过LoginCommand进行测试.

现在可以问,当您已经对基础业务逻辑进行测试时,测试命令的目的是什么?目的是验证业务逻辑被调用并具有正确的参数.

如何做这样的测试?使用mock. FakeItEasy示例:

var userServiceFake = A.Fake<IUserService>();
var testedviewmodel = new Loginviewmodel(userServiceFake);

// prepare data for test
var passwordBox = new PasswordBox { Password = "password" };
testedviewmodel.UserName = "TestUser";

// execute test
testedviewmodel.LoginCommand.Execute(passwordBox);

// verify
A.CallTo(() => userServiceFake.Login(
    "TestUser",A<Action<bool>>.Ignored)
).MustHaveHappened();

这样您可以验证该命令是否按预期调用业务层.请注意,Action< bool>在匹配参数时被忽略 – 很难匹配Action< T>和Func< T>通常不值得.

几个注释:

>您可能需要重新考虑在视图模型中有消息框代码(这应该属于视图,视图模型应该请求或通知视图来显示弹出窗口).这样做也可以通过测试视图模型(例如,不需要忽略该Action参数)
>有些人测试INotifyPropertyChanged属性(在您的情况下为UserName) – 当属性值更改时会引发该事件.由于这是很多样板代码,因此建议使用工具/ library自动化此过程.>你确实想要有两组测试,一个用于视图模型(如上面的例子),另一个用于底层业务逻辑(原始测试).在MVVM中,虚拟机是一种额外的层,似乎没有什么用处 – 但这就是整体而言 – 在这里没有业务逻辑,而是重点关注数据重新排列/准备视图层.

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...