如何使用nHibernate为方法编写单元测试

时间:2013-09-29 12:36:49

标签: asp.net-mvc linq unit-testing nhibernate mocking

我正在尝试使用.net mvc 4和流畅的nhibernate创建应用程序。

我创建了ProductsFacade,它负责获取数据并将数据插入数据库。 方法GetProductsByPageAndCategory用于从数据库中获取记录页面。我想编写单元测试,检查分页是否运行良好。

这很难做到,因为分页必须在单QueryOver个查询中完成。我不能写单独的方法只获取数据,模拟它并编写单独的分页方法。所以我需要模拟数据库。我使用moq工具进行模拟。

也许有人可以提供一些如何做的提示?或者任何其他替代方法如何解决我的问题?

public class ProductFacade {
    //...

    public virtual IList<Product> GetProductsByPageAndCategory(
        string category,
        int pageNumber,
        int pageSize)
    {
        //IList<Product> products = ;
        var productsQuery = _session.QueryOver<Product>();
        if (category != null)
        {
            productsQuery.Where(p => p.Category == category);
        }

        IList<Product> products = productsQuery
            .OrderBy(p => p.Id)
            .Desc
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .List<Product>();

        return products;
    }

    //...
}

3 个答案:

答案 0 :(得分:2)

我也使用moq来模拟NHibernate会话,这是一个如何模拟NHibernate ISessionISessionFactory的非常简单的例子。

    var mockSession = new Mock<ISession>();
    mockSession.Setup(r => r.Get<ExampleEntity>(It.IsAny<int>()))
        .Returns(new ExampleEntity());

    var mockSessionFactory = new Mock<ISessionFactory>();
    mockSessionFactory.Setup(r => r.OpenSession())
        .Returns(mockSession.Object);

    var sessionFactory = mockSessionFactory.Object;
    // inject mockSessionFactory.Object to your business layer...

    // code where ever sessionFactory is used...
    // OpenSession now returns the mocked session
    using (var session = sessionFactory.OpenSession())
    {
        //Get of type ExampleEntity will always return whatever you define in your mock
        var rs = session.Get<ExampleEntity>(1);
    }

要对业务对象使用模拟,您必须以一种可以手动构建它的方式进行设计,以便它使用您的模拟工厂。

通常,如果您使用Unity注入,这很容易。通过统一,您的Business类的构造函数可能会占用会话或工厂或其他...在这种情况下,在您的单元测试中,您可以手动构建目标并将模拟传递给它...

答案 1 :(得分:2)

这是我的替代选择 - 不要模拟数据库。

在我们的测试设置中,每个开发人员的机器上都必须有一个具有给定名称的数据库(例如“CMS_AutoTests”)。测试运行时,它会与此数据库进行交互。

每次测试后运行的TearDown方法运行一个清除每个表的存储过程,因此每个测试都以一个空数据库开始。

答案 2 :(得分:2)

内存数据库中的

代码比嘲弄要少得多,更容易理解,更接近真实的东西。它还可以确保您的映射是正确的,因此无需额外的负载保存测试。

//for all tests

static Configuration config;
static ISessionFactory testSessionFactory;

config = Fluently.Configure()
    .Database(SQLiteConfiguration.Standard.InMemory().ShowSql().FormatSql())
    .Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Foo>())  // add your mappings here
    .BuildConfiguration();

testSessionFactory = config.BuildSessionFactory();

// in test
public ProductTests()
{
    session = sf.OpenSession();
    new SchemaExport(config).Execute(true, true, false, session.Connection, null);
}

private void InjectSampleData(params object[] objects)
{
    foreach (var obj in objects)
    {
        session.Save(obj);
    }
    session.Flush();
    session.Clear();
}

public void TestThis()
{
    InjectSampleData(
        new Product() { ... },
        new Product() { ... },
        new Product() { ... },
        new Product() { ... },
    );

    var products = new ProductFacade(session).GetProductsByPageAndCategory(...);

    // assert products match expected subcollection of Injected Data
}