通过Moq测试表达式树方法而不是使用表达式逻辑

时间:2012-01-11 18:53:49

标签: c# nunit moq expression-trees

问题:直接运行代码会创建正确的过滤,我的单元测试根本不会过滤(返回模拟存储库中的所有记录)。

我无法判断测试中的表达式逻辑是否搞砸了什么,或者是什么,但无论我设置的标准如何,模拟的存储库都不会在广告上过滤我可以支持“所有”记录。这100%来自调用它的服务层,但不是在测试中。

编辑:抱歉代码的格式化,我无法更好地了解它。

代码

public abstract class EFRepository<T> : IRepository<T> where T : BaseEFModel {

public IUnitOfWork UnitOfWork { get; set; }

private IDbSet<T> _objectset;
private IDbSet<T> ObjectSet
{
    get { return _objectset ?? (_objectset = UnitOfWork.Context.Set<T>()); }
}

public virtual IQueryable<T> WhereInternal(Expression<Func<T, bool>> expression)
{
    return ObjectSet.Where(expression);
} } 

实现:

public class DoNotSolicitRepo : EFRepository<DoNotSolicit>, IDoNotSolicitRepo {
private readonly RestUnitOfWork worker;

public DoNotSolicitRepo(RestUnitOfWork _worker)
{
    worker = _worker;
}

public IList<DNSContract> SelectWithCriteria(DNS_Search search)
{
    // create the where clause 
    Expression<Func<DoNotSolicit, bool>> whereClause = c => (
        (String.IsNullOrEmpty(search.FirstName) || c.FirstName.StartsWith(search.FirstName)) &&
        (String.IsNullOrEmpty(search.LastName) || c.LastName.StartsWith(search.LastName)) &&
        (String.IsNullOrEmpty(search.Address1) || c.Address1.Contains(search.Address1)) &&
        (String.IsNullOrEmpty(search.Address2) || c.Address2.Contains(search.Address2)) &&
        (String.IsNullOrEmpty(search.City) || c.City.Contains(search.City)) &&
        (String.IsNullOrEmpty(search.State) || c.State.Equals(search.State)) &&
        (String.IsNullOrEmpty(search.Zip5) || c.Zip.Equals(search.Zip5)) &&
        (String.IsNullOrEmpty(search.Phone) || c.Phone.Equals(search.Phone)) &&
        (String.IsNullOrEmpty(search.Email) || c.Email.Equals(search.Email))
        );

    using (var scope = worker)
    {
        scope.Register(this);
        var resultList = WhereInternal(whereClause).ToList();

        Mapper.CreateMap<DoNotSolicit, DNSContract>()
           .ForMember(dest => dest.PartnerCode, opt => opt.Ignore())
           .ForMember(dest => dest.PartnerDescription, opt => opt.Ignore())
           .ForMember(dest => dest.DoNotSolicitReason, opt => opt.Ignore())
           .ForMember(dest => dest.SaveDate, opt => opt.Ignore())
           .ForMember(dest => dest.InsertDT, opt => opt.Ignore());

         var returnObj = Mapper.Map<IList<DoNotSolicit>, IList<DNSContract>>(resultList);

         return returnObj.FriendlySaveDates();
    }
} }

测试

public abstract class BaseEFUnitFixture<T> where T : BaseEFModel {
protected Mock<EFRepository<T>> mockedEFRepo = new Mock<EFRepository<T>>();

public Mock<EFRepository<T>> MockedEFRepositiory()
{
    var t = new List<T>();

    mockedEFRepo.Setup(x => x.AddInternal(It.IsAny<T>())).Callback((T e) => t.Add(e));
    mockedEFRepo.Setup(x => x.AddInternal(It.IsAny<List<T>>())).Callback((IList<T> le) => t.AddRange(le));
    mockedEFRepo.Setup(x => x.AllInternal()).Returns(t.AsQueryable());
    mockedEFRepo.Setup(x => x.WhereInternal(It.Is<Expression<Func<T, bool>>>(y => y != null))).Returns(t.AsQueryable());

    return mockedEFRepo;
}

}

实现:

[TestFixture] public class DNSRepoTest : BaseEFUnitFixture<DoNotSolicit> {
private readonly List<DoNotSolicit> list = new List<DoNotSolicit>();

private class Search
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip5 { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
}

private Expression<Func<DoNotSolicit, bool>> SearchBuilder(Search search)
{
    //  same as repo logic
    Expression<Func<DoNotSolicit, bool>> whereClause = c => (
       (String.IsNullOrEmpty(search.FirstName) || c.FirstName.StartsWith(search.FirstName)) &&
       (String.IsNullOrEmpty(search.LastName) || c.LastName.StartsWith(search.LastName)) &&
       (String.IsNullOrEmpty(search.Address1) || c.Address1.Contains(search.Address1)) &&
       (String.IsNullOrEmpty(search.Address2) || c.Address2.Contains(search.Address2)) &&
       (String.IsNullOrEmpty(search.City) || c.City.Contains(search.City)) &&
       (String.IsNullOrEmpty(search.State) || c.State.Equals(search.State)) &&
       (String.IsNullOrEmpty(search.Zip5) || c.Zip.Equals(search.Zip5)) &&
       (String.IsNullOrEmpty(search.Phone) || c.Phone.Equals(search.Phone)) &&
       (String.IsNullOrEmpty(search.Email) || c.Email.Equals(search.Email))
       );

    return whereClause;
}

[TestFixtureSetUp]
public void Init()
{
    list.Add(new DoNotSolicit
                 {
                     DoNotSolicitID = 4,
                     FirstName = "nunit",
                     Origination = "testing"
                 });

    mockedEFRepo = MockedEFRepositiory();
    mockedEFRepo.Object.AddInternal(list);
}

[Test]
public void SelectWithCriteria_FirstNameMatch()
{
    var clause = SearchBuilder(new Search{FirstName = "test"});
    var results = mockedEFRepo.Object.WhereInternal(clause).ToList();

    Assert.IsNotNull(results);
    Assert.IsTrue(results.Count < mockedEFRepo.Object.AllInternal().Count());
    Assert.IsTrue(results.Count > 0);
} }

1 个答案:

答案 0 :(得分:1)

你的方法总体上是错误的。你做什么嘲笑你测试的课 - 这是不正确的。你应该只模拟你正在测试的类外部的东西 - 因为模拟的功能几乎取代了带有空存根的模拟对象的功能。如果它被嘲笑它不起作用或工作,因为模拟已配置。

我认为没有理由要测试模拟类,因为在这种情况下,您测试的不是类功能/代码,而是您配置模拟的方式。

很难理解您希望测试方法测试的代码是什么。我建议进行依赖注入以分离实际的数据存储库(可以用特定的数据/方法模拟)和包含逻辑的类(比如选择第一个匹配)。在逻辑类的构造函数中传递模拟的存储库,并测试它断言预期的行为。