12

新しいmvc3プロジェクトのコンセプトタイプコードの基本的な証明をしようとしています。RavenDBでMoqを使用しています。

アクション:

public ActionResult Index(string id)
{
    var model = DocumentSession.Query<FinancialTransaction>()
        .Where(f => f.ResponsibleBusinessId == id);
    return View(model);
}

テスト:

private readonly Fixture _fixture = new Fixture();

[Test]
public void Index_Action_Returns_List_Of_FinancialTransactions_For_Business([Random(0, 50, 5)]int numberOfTransactionsToCreate)
{
    // Arrange
    var session = new Mock<IDocumentSession>();
    var financialController = new FinancialController { DocumentSession = session.Object };

    var businessId = _fixture.CreateAnonymous<string>();
    var transactions = _fixture.Build<FinancialTransaction>()
        .With(f => f.ResponsibleBusinessId, businessId)
        .CreateMany(numberOfTransactionsToCreate);

    // Mock
    var ravenQueryableMock = new Mock<IRavenQueryable<FinancialTransaction>>();
    ravenQueryableMock.Setup(x => x.GetEnumerator()).Returns(transactions.GetEnumerator);
    ravenQueryableMock.Setup(x => x.Customize(It.IsAny<Action<Object>>()).GetEnumerator()).Returns(() => transactions.GetEnumerator());

    session.Setup(s => s.Query<FinancialTransaction>()).Returns(ravenQueryableMock.Object).Verifiable(); 

    // Act
    var actual = financialController.Index(businessId) as ViewResult;

    // Assert
    Assert.IsNotNull(actual);
    Assert.That(actual.Model, Is.InstanceOf<List<FinancialTransaction>>());

    var result = actual.Model as List<FinancialTransaction>;
    Assert.That(result.Count, Is.EqualTo(numberOfTransactionsToCreate));
    session.VerifyAll();
}

問題は.Where(f => f.ResponsibleBusinessId == id)にあるように見えます。モックされたIRavenQueryableから、FinancialTransactionsのリストを返すので、.Where()はそれに基づいてフィルタリングすると思います。しかし、それはIQueryableなので、列挙するときに、式をすべて1つとして実行しようとしていると思います。

確認するために、アクションのクエリを次のように変更しました。

var model = DocumentSession.Query<FinancialTransaction>()
    .ToList()
    .Where(f => f.ResponsibleBusinessId == id);

これによりテストに合格できますが、すべてのレコードを列挙してからフィルタリングするため、理想的ではありません。

これでMoqを動作させる方法はありますか?

4

2 に答える 2

9

コメントで述べたように、テストでRavenDBAPIをあざけるべきではありません。

RavenDBは、InMemoryモードのおかげで、ユニットテストを優れた方法でサポートしています。

[Test]
public void MyTest()
{
    using (var documentStore = new EmbeddableDocumentStore { RunInMemory = true })
    {
        documentStore.Initialize();

        using (var session = documentStore.OpenSession())
        {
            // test
        }
    }
}
于 2012-04-13T09:05:43.420 に答える
5

他の人が述べているように、メモリ内/埋め込みモードでうまくいくことができれば、統合テストに最適です。しかし、私の意見では、単体テストには十分な速さや簡単さではありません。

このような場合にIRavenQueryableの「偽物」(標準のLINQのラッパー)を提供するSamRitchieによるブログ投稿を見つけましたIQueryableIRavenQueryableRavenの新しいバージョン(現在は2.5)がインターフェースにいくつかの追加のメソッドを提供しているため、彼は少し時代遅れです。私は現在、これらの新しいメソッドを使用していません(TransformWith、、 )。そのため、今のところ、以下のコードを怠惰に残しました。AddQueryInputSpatialNotImplementedException

私がこれに基づいた元のコードと使用例については、Samの投稿を参照してください。

public class FakeRavenQueryable<T> : IRavenQueryable<T> {
    private readonly IQueryable<T> source;

    public FakeRavenQueryable(IQueryable<T> source, RavenQueryStatistics stats = null) {
        this.source = source;
        this.QueryStatistics = stats;
    }

    public RavenQueryStatistics QueryStatistics { get; set; }

    public Type ElementType {
        get { return typeof(T); }
    }

    public Expression Expression {
        get { return this.source.Expression; }
    }

    public IQueryProvider Provider {
        get { return new FakeRavenQueryProvider(this.source, this.QueryStatistics); }
    }

    public IRavenQueryable<T> Customize(Action<IDocumentQueryCustomization> action) {
        return this;
    }

    public IRavenQueryable<TResult> TransformWith<TTransformer, TResult>() where TTransformer : AbstractTransformerCreationTask, new() {
        throw new NotImplementedException();
    }

    public IRavenQueryable<T> AddQueryInput(string name, RavenJToken value) {
        throw new NotImplementedException();
    }

    public IRavenQueryable<T> Spatial(Expression<Func<T, object>> path, Func<SpatialCriteriaFactory, SpatialCriteria> clause) {
        throw new NotImplementedException();
    }

    public IRavenQueryable<T> Statistics(out RavenQueryStatistics stats) {
        stats = this.QueryStatistics;
        return this;
    }

    public IEnumerator<T> GetEnumerator() {
        return this.source.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator() {
        return this.source.GetEnumerator();
    }
}

public class FakeRavenQueryProvider : IQueryProvider {
    private readonly IQueryable source;

    private readonly RavenQueryStatistics stats;

    public FakeRavenQueryProvider(IQueryable source, RavenQueryStatistics stats = null) {
        this.source = source;
        this.stats = stats;
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression) {
        return new FakeRavenQueryable<TElement>(this.source.Provider.CreateQuery<TElement>(expression), this.stats);
    }

    public IQueryable CreateQuery(Expression expression) {
        var type = typeof(FakeRavenQueryable<>).MakeGenericType(expression.Type);
        return (IQueryable)Activator.CreateInstance(type, this.source.Provider.CreateQuery(expression), this.stats);
    }

    public TResult Execute<TResult>(Expression expression) {
        return this.source.Provider.Execute<TResult>(expression);
    }

    public object Execute(Expression expression) {
        return this.source.Provider.Execute(expression);
    }
}
于 2014-04-14T16:57:56.637 に答える