我想解决的问题是,如何在C#中测试两个依赖类。为了测试我正在使用NUnit和Moq。
假设我有一个自定义集合,它自动枚举其项目。集合中的值必须保持原始顺序,这就是为什么必须自动枚举的原因。以下代码显示了上述类的最简单示例:
public interface ICustomItem
{
int Id { get; set; }
ICustomCollection<ICustomItem> ParentCollection { get; set; }
}
public interface ICustomCollection<T> where T : ICustomItem
{
IEnumerable<T> Items { get; set; }
void Add(T t);
// And more of course...
}
public class CustomCollection<T> : ICustomCollection<T> where T : ICustomItem
{
public IEnumerable<T> Items { get; set; }
public void Add(T t)
{
// Some logic here...
t.Id = Items.Count(); // Generate Id
}
}
将项目添加到集合时,会生成新的Id并将其分配给CustomItem。 autonumerating的机制也应该包含在其他方法中,例如Remove(),但对于这个问题,我只留下了Add方法。
问题是,如何测试autonumerates是否正常工作?当mock作为param传递时,它不会在类中修改。我应该使用create-for-tests CustomItem类的简单实例测试该类吗?
TL;博士
换句话说,我希望能够在类中修改模拟。
答案 0 :(得分:1)
尝试在测试中使用该类,就像在生产代码中使用它一样。这将为您提供最有用的测试,因为您可以自由地重构类中的代码,而不会破坏甚至更改测试。该测试还将作为如何使用该类的示例。
首先,我不会使用Moq,而是使用您仅为测试实现的简短MockCustomItem
类。
然后使用添加一些值并断言结果是您所期望的。养成在每个测试中仅使用一个断言的习惯,如下所示。
[TestFixture]
public class GivenCustomCollectionWithTwoElements
{
private CustomCollection<MockCustomItem> _customCollection;
[SetUp]
public void SetUp()
{
_customCollection = new CustomCollection<MockCustomItem>();
_customCollection.Add(new MockCustomItem());
_customCollection.Add(new MockCustomItem());
}
[Test]
public void CheckLength()
{
Assert.That(_customCollection.Items, Is.EqualTo(2));
}
[Test]
public void CheckFirstItemId()
{
Assert.That(_customCollection.Items.ElementAt(0).Id, Is.EqualTo(0));
}
[Test]
public void CheckSecondItemId()
{
Assert.That(_customCollection.Items.ElementAt(1).Id, Is.EqualTo(1));
}
private class MockCustomItem : ICustomItem
{
public int Id { get; set; }
public ICustomCollection<ICustomItem> ParentCollection { get; set; }
}
}
一旦掌握了这一点,您还可以使用Moq以更少的样板代码创建更简洁的测试。在这种情况下,也可以使用NUnit参数化测试用例。
答案 1 :(得分:1)
在单元测试中,您只能测试您正在测试的单元。所以我说你应该模拟/伪造ICustomItem然后发送它,然后查看伪造的对象是否得到你期望的Id。
请阅读我的回答,了解有关同一主题Any ASP.NET (WebForm) apps which have good unit tests (CodePlex or anywhere)?
的更多信息我使用FakeItEasy作为模拟/假框架,但我想moq看起来非常相似,这是我的代码
[TestFixture]
public class CustomCollectionTests{
[Test]
public void Add_AddTwoItems_ItemsGetsConsecutiveIds() {
var customItem1 = A.Fake<ICustomItem>();
var customItem2 = A.Fake<ICustomItem>();
var cutomCollection = new CustomCollection<ICustomItem>();
cutomCollection.Add(customItem1);
cutomCollection.Add(customItem2);
Assert.AreEqual(1, customItem1.Id);
Assert.AreEqual(2, customItem2.Id);
}
}
public interface ICustomItem {
int Id { get; set; }
}
public interface ICustomCollection<T> where T : ICustomItem {
void Add(T t);
}
public class CustomCollection<T> : ICustomCollection<T> where T : ICustomItem {
public List<T> innerList = new List<T>();
public void Add(T t) {
// Some logic here...
innerList.Add(t);
t.Id = innerList.Count(); // Generate Id
}
}
修改强>
删除了未经测试的MOQ示例,该示例似乎无效。
答案 2 :(得分:0)
你是对的,因为模拟没有被修改。但是你应该能够在调用add方法之后验证mock,如下所示:
mock.VerifySet(x => x.Id = 42);
请记住在调用Add之前在Items属性中设置一些内容。当要求Count()时,那个东西应该返回42。这也可能是模拟。