4

I want to test that a method adds a record to a DBSet under a certain condition:

public static void AddTriggeredInternalTransactions(IDataContext dc, ExternalTransaction transaction)
{
    if (transaction [meets condition A])
        dc.InternalTransactions.CreateInverseTransaction(transaction);

    // do other stuff under other conditions
}

private static void CreateInverseTransaction(this IDbSet<InternalTransaction> transactions, ExternalTransaction from)
{
    var internalTransaction = transactions.Create();
    from.CopyToInternalTransaction(internalTransaction);
    transactions.Add(internalTransaction);
}

Now, I already have tests for CopyToInternalTransaction(). I just need to call AddTriggeredInternalTransactions() and verify that [condition A] results in the new record being added).

I started with http://msdn.microsoft.com/en-us/data/dn314429.aspx, then used other Google and StackOverflow searches. Before tackling my "real" test, I'm trying to do a simple test just to verify whether a record has been added to a dataset, but I'm stuck on this. Can anyone point out my flaw(s)?

var internals = new Mock<DbSet<InternalTransaction>>();
var theData = new List<InternalTransaction>().AsQueryable();

internals.As<IQueryable<InternalTransaction>>().Setup(m => m.Provider).Returns(theData.Provider);
internals.As<IQueryable<InternalTransaction>>().Setup(m => m.Expression).Returns(theData.Expression);
internals.As<IQueryable<InternalTransaction>>().Setup(m => m.ElementType).Returns(theData.ElementType);
internals.As<IQueryable<InternalTransaction>>().Setup(m => m.GetEnumerator()).Returns(theData.GetEnumerator());

var mockDC = new Mock<IDataContext>();
mockDC.Setup(q => q.InternalTransactions).Returns(internals.Object);
mockDC.Setup(q => q.InternalTransactions.Create()).Returns(new InternalTransaction());
mockDC.Setup(q => q.InternalTransactions.Add(It.IsAny<InternalTransaction>()));

var it = mockDC.Object.InternalTransactions.Create();
it.Id = "123";
mockDC.Object.InternalTransactions.Add(it);

internals.Verify(e => e.Add(It.Is<InternalTransaction>(d => d.Id == "123")), Times.Once());
//or:  Assert.Equal(1, mockDC.Object.InternalTransactions.Count());

This test fails with: Expected invocation on the mock once, but was 0 times: e => e.Add(It.Is(d => d.Id == "123")) No setups configured. No invocations performed.

The Assert statement, if used instead, fails with a NotImplementedException: "The member IQueryable.Provider has not been implemented on type DbSet~1Proxy_1 which inherits from DbSet1. Test doubles for DbSet1 must provide implementations of methods and properties that are used."

James in Indy
  • 2,804
  • 2
  • 25
  • 25
  • 1
    Does the verify pass with It.IsAny()? If so, check the type of Id. If not, maybe you need to Setup the Add method on your internals mock rather than mockDC. – Adam47 Jul 07 '14 at 15:09
  • It does not pass--I get a slightly different error of "expected invocation on the mock at least once, but was never performed". I replaced the line "mockDC.Setup(q => q.InternalTransactions.Add(It.IsAny()));" with "internals.Setup(q => q.Add(It.IsAny()));" - same result – James in Indy Jul 07 '14 at 15:57
  • One thing - if InternalTransactions is a property, you'll need to [use `SetupGet`](http://stackoverflow.com/q/12141799/314291). And as per Adam, I believe the `Create` and `Add` setups may need to be done on `internals`, not on `mockDC`. – StuartLC Jul 07 '14 at 15:58
  • SetupGet on the mockDC or on internals? – James in Indy Jul 07 '14 at 16:26
  • Wait... this seems to have done something. I changed the Setup of Create() from mockDC to internals and the test passes! I need to investigate further to see if this is truly working, but I think we're on the right track. – James in Indy Jul 07 '14 at 16:34

1 Answers1

1

After comments by Adam and Stuart plus further experimentation, I was able to reduce my test case to the following working test case:

var internals = new Mock<DbSet<InternalTransaction>>();
var mockDC = new Mock<IDataContext>();

mockDC.Setup(q => q.InternalTransactions).Returns(internals.Object);
internals.Setup(q => q.Create()).Returns(new InternalTransaction());

var transaction = new ExternalTransaction { [set criteria for Condition A] };

SomeBusinessObject.AddTriggeredInternalTransactions(mockDC.Object, transaction);

// verify the Add method was executed exactly once
internals.Verify(e => e.Add(It.IsAny<InternalTransaction>()), Times.Once());
James in Indy
  • 2,804
  • 2
  • 25
  • 25