使用Moq验证列表

5

给定调用代码

List<Person> loginStaff = new List<Person>(); 

loginStaff.add(new Person{FirstName = "John", LastName = "Doe"});

this._iViewLoginPanel.Staff = loginStaff;

如何验证是否存在一个名为“John Doe”的员工,并且至少有一个员工被设置?目前,我看到的所有示例都非常基本,只使用了 It.IsAny 或 Staff = 一些基本类型,但没有实际验证列表等复杂类型中的数据。

我的断言代码如下:

this._mockViewLoginPanel.VerifySet(x=> x.Staff = It.IsAny<List<Person>>());

这段代码只检查了给setter的类型,但没有检查列表本身的大小或项目。我尝试做出以下更改:

        this._mockViewLoginPanel.VerifySet(
           x =>
           {
               List<string> expectedStaffs = new List<string>{"John Doe", "Joe Blow", "A A", "Blah"};
               foreach (Person staff in x.Staff)
               {
                   if (!expectedStaffs.Contains(staff.FirstName + " " + staff.LastName))
                       return false;
               }
               return true;
           });

但这告诉我lambda语句体无法转换为表达式树。然后我想到将语句体放入函数中并运行该函数,但在运行时我得到以下错误:

System.ArgumentException: 表达式不是属性设置器调用。

更新: 考虑到前两个答案使用assert,我尝试了这种方法,但发现即使将Staff设置为非空列表,它仍然显示为空。所以这就是完整的测试样例。

[TestMethod]
public void When_The_Presenter_Is_Created_Then_All_CP_Staff_Is_Added_To_Dropdown()
{
    this._mockViewLoginPanel = new Mock<IViewLoginPanel>();

    PresenterLoginPanel target = new PresenterLoginPanel(this._mockViewLoginPanel.Object);

    this._mockViewLoginPanel
        .VerifySet(x => x.Staff = It.IsAny<List<Person>>());

    Assert.AreEqual(5, this._mockViewLoginPanel.Object.Staff.Count);
}

在PresenterLoginPanel的构造函数中的某个位置

public PresenterLoginPanel
{
    private IViewLoginPanel _iViewLoginPanel;

    public PresenterLoginPanel(IViewLoginPanel panel) 
    { 
        this._iViewLoginPanel = panel;
        SomeFunction();
    }

    SomeFunction() {
        List<Person> loginStaff = new List<Person>(); 

        loginStaff.add(new Person{FirstName = "John", LastName = "Doe"});

        this._iViewLoginPanel.Staff = loginStaff;
    }
}

当我调试到下一行时,this._iViewLoginPanel.Staff为空,这就是导致assert中的空异常。


你是否将构造函数参数赋值给类变量? - Michael Shimmins
是的,我正在将参数this._mockViewLoginPanel.Object分配给变量this._iViewLoginPanel。 - Joe
你能将实现这个功能的构造函数代码行粘贴过来吗? - Michael Shimmins
忽略我之前关于引用/值的说法 - 那不是问题所在。请给我几分钟,我会更新一些更多的信息。 - Michael Shimmins
是的,太棒了!SetupAllProperties 是关键。我一直在尝试严格和宽松模式,但没有深入研究。结果发现,我不需要设置严格行为,我可以使用宽松行为并设置所有属性。测试过了,可以正常工作。重新将您设为被接受的答案。 - Joe
显示剩余3条评论
3个回答

14

不必使用模拟对象的方法,您可以使用NUnit方法来断言模拟对象的内容。

将列表分配给对象并验证其已设置后,使用断言来检查特定内容,例如项目数和第一个对象是否与您期望的相匹配。

Assert.That(this._mockViewLoginPanel.Object.Staff.Length, Is.EqualTo(1));
Assert.That(this._mockViewLoginPanel.Object.Staff[0], Is.Not.Null);
Assert.That(this._mockViewLoginPanel.Object.Staff[0], Is.EqualTo(loginStaff[0]));

编辑

经过进一步调查,这归结为模拟行为。属性StaffPerson没有被正确设置。

请将它们设置好,将您的模拟创建更改为:

var _mockViewLoginPanel = new Mock<IViewLoginPanel>(MockBehavior.Strict);
_mockViewLoginPanel.SetupAllProperties();

这里是一个完整的演示代码清单:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public interface IViewLoginPanel
{
    IList<Person> Staff { get; set; }
}

public class PresenterLoginPanel {

    private IViewLoginPanel _iViewLoginPanel;

    public PresenterLoginPanel(IViewLoginPanel panel) 
    { 
        _iViewLoginPanel = panel;
        SomeFunction();
    }

    public void SomeFunction() 
    {
        List<Person> loginStaff = new List<Person>(); 

        loginStaff.Add(new Person{FirstName = "John", LastName = "Doe"});

        _iViewLoginPanel.Staff = loginStaff;
    }

    public IViewLoginPanel ViewLoginPanel
    {
        get { return _iViewLoginPanel; }
    }
}

[TestFixture]
public class PresenterLoginPanelTests
{
    [Test]
    public void When_The_Presenter_Is_Created_Then_All_CP_Staff_Is_Added_To_Dropdown()
    {
        var _mockViewLoginPanel = new Mock<IViewLoginPanel>(MockBehavior.Strict);
        _mockViewLoginPanel.SetupAllProperties();

        PresenterLoginPanel target = new PresenterLoginPanel(_mockViewLoginPanel.Object);

        _mockViewLoginPanel.VerifySet(x => x.Staff = It.IsAny<List<Person>>());

        Assert.AreEqual(5, _mockViewLoginPanel.Object.Staff.Count);
    }

}

facepalms 当然,我太专注于学习moq了,而没有足够地关注基本单元测试 :) - Joe
实际上,我接受得太快了,当我使用你的代码时,它会给我一个对象空引用,因为Staff属性没有被保存。(我进行了调试以确保它被设置了,但是在设置后立即检查,值仍然为空) - Joe
我注意到你有 _iViewLoginPanel_mockViewLoginPanelStaff 被设置在 _iViewLoginPanel 上,而 Assert 是在 _mockViewLoginPanel 上进行的。第一次我错过了这个问题。它们应该是同一个对象吗? - Michael Shimmins
我已经更新了我的问题并提供了更多细节,基本上mockViewLoginPanel是传递到presenterViewLoginPanel中的模拟对象。iViewLoginPanel是presenterViewLoginPanel的一个字段,因此在这种情况下它将具有mockViewLoginPanel。 - Joe

8
您可以使用Moq本身来轻松完成这一操作(即使您没有对预期结果对象的引用) - 只需使用It.Is(..)方法即可:
_mockViewLoginPanel.VerifySet(x => x.Staff = It.Is<List<Person>>(staff => staff.Count == 5));
_mockViewLoginPanel.VerifySet(x => x.Staff = It.Is<List<Person>>(staff => staff[0].FirstName == "John"));

1

这个检查确保员工数量应该大于0,至少有一个项目不为空,并且至少有一个项目的名字为Joe。如果您想比较对象,则需要添加比较器。

Assert.AreNotEqual(this._mockViewLoginPanel.Object.Staff.Count, 0);
Assert.AreNotEqual(this._mockViewLoginPanel.Object.Staff.All(x => x == null), true);
Assert.AreEqual(this._mockViewLoginPanel.Object.Staff.Any(x => x.FirstName == "Joe"), true);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接