Entity Framework related objects

51 views
Skip to first unread message

Greg Kuper

unread,
Nov 7, 2017, 2:57:13 PM11/7/17
to NSubstitute
I have a question regarding using NSubstitute with EF 6.0.

I would like to run some test against some BLL methods that contain nested object.

For example: I setup the test method with something like.

I am sorry if this is a basic question but I have only been working with NSubstitute for a few days.  


        var data = lstOrderTypes.AsQueryable();
            
        var mockSet = Substitute.For<IObjectSet<order>, IQueryable<order>>();
        ((IQueryable<order>)mockOrder).Provider.Returns(data.Provider);
        ((IQueryable<order>)mockOrder).Expression.Returns(data.Expression);
        ((IQueryable<order>)mockOrder).ElementType.Returns(data.ElementType);
        ((IQueryable<order>)mockOrder).GetEnumerator().Returns(data.GetEnumerator());



        var repo = Substitute.For<IMyEntities>();

        repo.order.Returns(mockOrder);

        //setup product_lines

        var prlData = lstProductLInes.AsQueryable();
        var prlMockSet = Substitute.For<IObjectSet<product_line>, IQueryable<product_line>>();

        ((IQueryable<product_line>)prlMockSet).Provider.Returns(prlData.Provider);
        ((IQueryable<product_line>)prlMockSet).Expression.Returns(prlData.Expression);
        ((IQueryable<product_line>)prlMockSet).ElementType.Returns(prlData.ElementType);
        ((IQueryable<product_line>)prlMockSet).GetEnumerator().Returns(prlData.GetEnumerator());

        repo.product_line.Returns(prlMockSet);


I would like to test something like:
var test= order.GetAll(true);
var nestedResults = test.Count(o => o.product_line.prl_key.ToString() =="8BFC7549-336E-496B-B9C7-CE830A2047F1");
 Assert.AreEqual(nestedResults, 2);

I think there may be a When -- Do statement that I can write that when the order is referenced for a product line it 
either looks up to my originallist or to the mock (prlMockSet).

I have tried to do something like:
mockSet.All(x => x.product_line)
            .When(x => x.GetType(product_line) = Arg.Any<product_line>())
            .Do(x => prlMockSet.Where(s => s.prl_key == mock.oty_only_prl_key)); 

but that does not work.

Any help with this would be greatly appreciated.

Greg

David Tchepak

unread,
Nov 7, 2017, 6:46:28 PM11/7/17
to nsubs...@googlegroups.com
Hi Greg,

I haven't used EF so not sure I can be of much help sorry. I would have guessed that you could set up the required information in the `lstOrderTypes` and `lstProductLInes` data?
Maybe try StackOverflow?

The `When..Do` line won't work for a few reasons: I think .All returns a bool so you won't be able to configure that result with NSubstitute; also the .Do statement doesn't produce any action (it creates an IEnumerable<product_line> but doesn't assign or use it). I'm guessing that you may need to use .Returns instead to link a mock order with a mock product line (if those objects are mocks created via NSubstitute).

I know this next bit will probably not help, but feel it is worth putting out there for consideration: Lots of people seem to using mocking with EF (for example: https://github.com/scott-xu/EntityFramework.Testing), but personally I try to avoid mocking types I don't own[1] like the EF types. If possible I'd try to connect to an in-memory DB (or even a real, local DB if this can be done reliably). If I need a fake/testable replacement for the EF stuff and I can't use the real thing, then I'd probably try implementing my own simulated version by hand. Mocking can be useful for poking a few basic responses into objects and checking calls were made, but for more detailed, simulated behaviour I think we need to switch to coding up our own classes (ideally with their own tests that check for conformance with the real system's behaviour). The aim is to get some confidence our code is going to work in the production app; if our test replaces the main parts of the logic with mocks then we are not getting much assurance it will work once we go back to using real EF objects.

Regards,
David




--
You received this message because you are subscribed to the Google Groups "NSubstitute" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nsubstitute+unsubscribe@googlegroups.com.
To post to this group, send email to nsubs...@googlegroups.com.
Visit this group at https://groups.google.com/group/nsubstitute.
For more options, visit https://groups.google.com/d/optout.

Greg Kuper

unread,
Nov 8, 2017, 10:00:15 AM11/8/17
to NSubstitute
Thank you so much David.  I have been struggling with the idea with mocking vs's a known data source.  I have considered using xml files to fill a database like SQL Server LocalDb with data that I know is static and can be repeatable.  I am new to testing frameworks even though I have been a developer for 20 years.  Probably a bit behind so I appreciate your response.  I read your article regarding "don't mock types you don't own" and it was very helpful.  I have found in my R&D of what we would like to do that the amount of work to actually mock EF objects and create the related objects may be quite a lot of maintenance work.  

When you mention that I could try and link the two mocks together I am curious how that would be done?  If Order is mocked with mockOrder and product line is mocked with prlMockSet can a link the two mock sets together?  

Do I link them together on the key that would join them or is there another way?  

Thanks for your response.

Greg
To unsubscribe from this group and stop receiving emails from it, send an email to nsubstitute...@googlegroups.com.

David Tchepak

unread,
Nov 8, 2017, 6:30:09 PM11/8/17
to nsubs...@googlegroups.com
Sometimes it is worth incurring a lot of maintenance work if the tests are really valuable. If they aren't of much value then the choice becomes much simpler. :)  With the copious amounts of advice on "best practices" for testing around (my own included ;) ), it is very easy to lose track of the main aim: checking our code works as we expect. If mocking EF fulfils this aim then go for it! If there are easier, more efficient ways that equally fulfil this aim, then do those. :)

In terms of linking mocks together, I don't think this will apply to EF, but normally with NSubstitute we'd do something like this:

    var order = Substitute.For<IOrder>();
    var productLine1 = Substitute.For<IProductLine>();
    var productLine2 = Substitute.For<IProductLine>();
    order.Key = "ABC";
    productLine1.OrderKey = "ABC";
    productLine2.OrderKey = "ABC";
    order.Products().Returns(new [] { productLine1, productLine2 });

Then you could do something like:

    AssertEqual(2, order.Products.Count());

Note that `.Count()` here is calling real code over a real list, it is only the data behind it that we are faking out. With EF I think it is going to be calling some real code when you do something like `orders.GetAll().Count()`, and we need to make sure we have setup the underlying data with the correct values so that the real code will work correctly with it.

In your case you may not have to go to these lengths: you have code like "var data = lstOrder.AsQueryable();". If you have setup your `lstOrder` and `lstProductLInes` objects with the correct data then the EF code should work with the EF mocking described here: https://stackoverflow.com/questions/21069986/nsubstitute-dbset-iqueryablet

(So I'm imagining code a bit like:

    var lstProductLines = new List<ProductLine> { new ProductLine { OrderKey = "ABC", ProductId = "123" }, new ProductLine { OrderKey = "ABC", ProductId = "456" } };

Note there is no mocking for this bit, it is just setting up real objects with the expected data.)


 
To unsubscribe from this group and stop receiving emails from it, send an email to nsubstitute+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages