如何编写nUnit/Moq来测试通用扩展方法?

4
我有以下通用扩展方法,可以从ObjectContext中删除所有EntityObjects。
public static void DeleleAllObjects<TEntity>(this ObjectContext context)
        where TEntity : EntityObject
    {
        var objectSet = context.CreateObjectSet<TEntity>();
        objectSet.ToList().ForEach(e => objectSet.DeleteObject(e));
    }

我对TDD和使用nUnit/Moq都比较新手...但我不确定从哪里开始为这个方法编写测试?


我认为你首先需要创建 [TestFixtureSetup] 来创建上下文对象,并预定义一定数量的对象集。然后在此函数调用之后,您可以断言 objectset.count 为0。希望这可以帮助到您。 - Mullaly
1个回答

5
我希望这可以帮到您:
[TestFixture]
public class ExtensionTest
{
    public class FakeEntity : EntityObject
    {

    }

    [Test]
    public void DeleteAllObjects()
    {
        //arrange
        var objectsToDelete = new List<FakeEntity>
            {
                new FakeEntity(),
                new FakeEntity()
            };
        var mockContext = new Mock<ObjectContext>();
        var mockObjectSet = new Mock<ObjectSet<FakeEntity>>();
        mockObjectSet.Setup(x => x.ToList()).Returns(objectsToDelete);
        mockContext.Setup(x => x.CreateObjectSet<FakeEntity>()).Returns(mockObjectSet.Object);

        //act
        mockContext.Object.DeleteAllObjects<FakeEntity>();

        //assert
        mockContext.Verify(x => x.CreateObjectSet<FakeEntity>(), Times.Once());
        mockObjectSet.Verify(x => x.ToList(), Times.Once());
        mockObjectSet.Verify(x => x.DeleteObject(It.IsAny<FakeEntity>()), Times.Exactly(2));

    }
}

假设您模拟的所有类型(上下文和对象集)都声明了调用方法为virtual或类为abstract。模拟接口通常限制较少。

此外,如果您想更加严格地进行断言以确保确实首先使用第一个实例调用DeleteObject,然后再使用第二个实例,而不是在第一个实例上两次调用,则可以更改测试的这一部分。但是这应该作为一个相当不错的起点。

总结:

这个特定的测试只应该真正测试您扩展方法内的代码。也就是说,它应该只确保您调用了CreateObjectSet<>(),获取列表,然后对每个对象调用DeleteObject

它不应该关心DeleteObject()是否实际上更改了ObjectSet(事实上它不会,因为它是一个模拟)。这应该是DeleteObject()方法的测试责任,但是由于我假设它实际上是一个EF方法,所以您不应该为第三方组件编写测试。


在CreateObjectSet上进行了非虚拟(在VB中可重写)成员的无效设置......它是非虚拟的:( - BlueChippy
1
我明白了,那么你最好的选择可能是从上下文中提取一个接口,并让你的扩展方法应用于该接口。在这里查看更多信息:http://stackoverflow.com/a/1894066/1373170,以及这里:http://blog.voltje.be/2012/11/19/mocking-entity-framework-using-objectcontext-and-database-first/ 如果您正在使用EF Code First,则可以参考这里:http://romiller.com/2012/02/14/testing-with-a-fake-dbcontext/ - Pablo Romeo
嗯...不确定我是否想要创建一个仓库,只是为了创建一个可以测试的单一方法?在测试中添加一个本地数据库并向其中添加/删除项目可能会更简单? - BlueChippy
1
好的。实际上,您需要接口来测试任何东西,例如在这种情况下依赖于控制器。我目前太习惯将单元测试作为我的流程的一部分,因此我会额外努力使我的代码可测试,不仅仅是针对扩展方法,而是针对所有内容。 - Pablo Romeo

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