簡體   English   中英

Moq.Mock <T> 使用最小起訂量將表達式設置到模擬中會導致模擬設置不匹配

[英]Moq.Mock<T> setting up expressions into a Mock using MOQ results in mock setups not being matched

我正在嘗試模擬數據服務上下文,為此,我有一個方法可以接受

  • 表達式(謂詞),
  • 可選的字符串參數
  • 具有謂詞數組的參數。

當我嘗試模擬此方法時,MOQ總是返回一個

模擬上的所有調用都必須具有相應的設置。 TearDown:Moq.MockException:以下設置不匹配:IContext m => m.Retrieve(It.IsAny()})

接口/實現下面的代碼

public interface IContext
{
    IQueryable<T> Retrieve<T>(Expression<Func<T, bool>> predicate,
                                string entitySetName = null,
                                params Expression<Func<T, object>>[] eagerProperties);
}

public class Context : IContext
{
    private readonly DataServiceContext _context;

    public Context(DataServiceContext context)
    {
        this._context = context;
    }

    public IQueryable<T> Retrieve<T>(Expression<Func<T, bool>> predicate,
                                        string entitySetName = null,
                                        params Expression<Func<T, object>>[] eagerProperties)
    {
        DataServiceQuery<T> query = _context.CreateQuery<T>(entitySetName ?? "Default");
        return eagerProperties.Aggregate(query, (current, e) => current.Expand(e.ToString())).Where(predicate);
    }
}

下面是一個調用上述上下文方法的測試類

public class Test
{
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
    public string Prop3 { get; set; }
}

public class SomeController
{
    private IContext _context = new Context(
        new DataServiceContext(new Uri("http://whatever/someservice.svc/")));

    public IContext ServiceContext
    {
        get
        {
            return _context ??
                   (_context = new Context(new DataServiceContext(new Uri("http://whatever/someservice.svc/"))));
        }
        set
        {
            _context = value;
        }
    }

    public Test RetrieveSomeInformation()
    {
        IQueryable<Test> tests = _context.Retrieve<Test>
                                                (
                                                    //Param 1
                                                    t => t.Prop1 == "test" && 1 == 1,

                                                    //Param 2
                                                    "Test",

                                                    //Param 3
                                                    t => t.Prop1,
                                                    t => t.Prop2,
                                                    t => t.Prop3
                                                  );
        return tests.First();
    }
}

以下是MOQ失敗的實際測試,其中包含“模擬中的所有調用都必須具有相應的設置”。 看不出為什么地球上的設備無法匹配! 任何幫助,將不勝感激。

[TestFixture]
public class ControllerTests
{
    public MockRepository Repository { get; set; }
    protected Mock<IContext> MockContext { get; set; }
    public SomeController Controller;

    public List<Test> Tests;
    public Test Test;

    [SetUp]
    public void SetUp()
    {
        Test = new Test { Prop1 = "1", Prop2 = "2", Prop3 = "3" };
        Tests = new List<Test> { Test };

        Repository = new MockRepository(MockBehavior.Strict);

        MockContext = Repository.Create<IContext>();

        Controller = new SomeController { ServiceContext = MockContext.Object };
    }

    [TearDown]
    public void TearDown()
    {
        Repository.VerifyAll();
    }

    [Test]
    public void DetailProgramme_Test()
    {
        MockContext.Setup(m => m.Retrieve<Test>
                            (
                                //Param 1
                                It.IsAny<Expression<Func<Test, bool>>>(),

                                //Param 2
                                It.IsAny<string>(),

                                //Param 3
                                It.IsAny<Expression<Func<Test, object>>>()
                            )
                          ).Returns(Tests.AsQueryable());

        Test info = Controller.RetrieveSomeInformation();


        //myMock.Setup(r => r.Find(It.IsAny<Expression<Func<Person, bool>>>())).Returns(new List<Person>() { new Person() }.AsQueryable());
        Assert.IsTrue(info == Test);
    }
}

我相信這取決於您的設置...

//Param 3
It.IsAny<Expression<Func<Test, object>>>()

哪個與params數組不匹配。 嘗試...

//Param 3
It.IsAny<Expression<Func<Test, object>>[]>()

使用不帶.CallBack Moq的It.IsAny<> .CallBack強制您編寫測試未涵蓋的代碼。 相反,它完全允許任何查詢/表達式通過,從單元測試的角度來看,使您的模擬基本上毫無用處。

解決方案:您要么需要使用回調來測試表達式,要么需要更好地約束模擬。 任一種方法都是麻煩且困難的。 自從我一直在練習TDD以來,我就一直在處理這個問題。 最后,我召集了一個輔助類,使它更具表現力,並且減少了混亂。 這是一個可能的最終結果:

mockPeopleRepository
  .Setup(x => x.Find(ThatHas.AnExpressionFor<Person>()
    .ThatMatches(correctPerson)
    .And().ThatDoesNotMatch(deletedPerson)
    .Build()))
  .Returns(_expectedListOfPeople); 

以下是討論該博客並提供源代碼的博客文章: http : //awkwardcoder.com/2013/04/24/constraining-mocks-with-expression-arguments/

從中派生實例之后,便要設置Mock。 獲得對象后,將不會受到模擬更改的影響。 例:

object instance = mock.Object; // this version wont include what you have configured in the setup
mock.Setup(...);
object instance2 = mock.Object;  // get the latest version including whatever you have configured in the setup

修改代碼的一種簡便方法是從Setup方法中刪除實例化語句,並使您的Controller變得懶惰,例如:

public SomeController Controller = new Lazy<SomeController>(() => new SomeController() { ServiceContext = MockContext.Object });

這樣,只要您在設置后第一次使用控制器,就將始終使用最新版本。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM