user1385713
user1385713

Reputation: 137

How to stub SingleOrDefault with Rhino mocks

I'm having a hard time understanding if I've got the right approach here. I want to test a repository. The repository is dependent on a DbContext. I want to be able to verify that the repository did not call a function Add on a property of type IDbSet which is a member of the DbContext.

I've tried two approaches. Verify with behaviour, and verify with state. Seems that verify with behaviour would be right because who knows what the stubbed state is doing in a fake object.

    public void VerifyBehaviour()
    {
        // Arrange
        var stubEntities = MockRepository.GenerateStub<IWsStatContext>();
        var stubManufcturers = MockRepository.GenerateStub<IDbSet<Manufacturer>>();
        var manufacturer = new Manufacturer() { Name = "Dummy" };
        var manufacturers = new List<Manufacturer>();
        manufacturers.Add(manufacturer);

        stubManufcturers.Stub(x => x.Local).Return(new System.Collections.ObjectModel.ObservableCollection<Manufacturer>(manufacturers));
        stubManufcturers.Stub(x => x.SingleOrDefault(m => m.Name == "Dummy")).Return(manufacturer);
        stubEntities.Manufacturers = stubManufcturers;

        // Act
        var sut = new EquiptmentRepository(stubEntities);
        sut.AddManufacturer(manufacturer);

        // Assert
        stubManufcturers.AssertWasNotCalled(x => x.Add(manufacturer));
    }


    public void VerifyState()
    { 
        // Arrange
        var stubEntities = MockRepository.GenerateStub<IWsStatContext>();
        var stubManufacturers = new InMemoryDbSet<Manufacturer>();
        var manufacturer = new Manufacturer() { Name = "Dummy" };
        stubManufacturers.Add(manufacturer);
        stubEntities.Manufacturers = stubManufacturers;

        // Act
        var sut = new EquiptmentRepository(stubEntities);
        sut.AddManufacturer(manufacturer);

        // Assert
        Assert.AreEqual(stubManufacturers.Count(), 1);
    }

Verify behaviour approach fails with NullReferenceExceptions around the stub of SingleOrDefault. So I find posts saying best to verify state and use a fake DbSet. But it feels wrong to be checking state of a fake object. What if the Add function was implemented differently than the real one (which it was originally and my test was passing even though my repository was broken).

Does anyone know how to stub the SingleOrDefault so I can check Add was called? I can't check Add was called on a non-rhinomocked stub.

Thanks

Upvotes: 3

Views: 2527

Answers (3)

Gene C
Gene C

Reputation: 2030

As stated in jimmy_keen's answer:

SingleOrDefault is an extension method defined on IEnumerable<T> (which IDbSet<T> implements). Being extension method means it is a static method. RhinoMocks (or any other free tool) cannot mock/stub static methods.

Rather than trying to 'stub' the extension method, try stubbing the underlying interface that the extension method is built upon: IEnumerable<T>

stubManufcturers.Stub( x => x.GetEnumerator() ).Return( new List<Manufacturer> { manufacturer }.GetEnumerator() );

By stubbing the behavior of GetEnumerator() when SingleOrDefault is called it will execute as expected against the fake enumeration and the test will be able to evaluate the behavior.

Upvotes: 3

k.m
k.m

Reputation: 31464

SingleOrDefault is an extension method defined on IEnumerable<T> (which IDbSet<T> implements). Being extension method means it is a static method. RhinoMocks (or any other free tool) cannot mock/stub static methods.

Unfortunatelly you don't have many options here: you'll either have to do state-based verification, or create hand made mock and set it up manually for your test (but this most likely will end up with state-based verification as once again - you can't really stub SingleOrDefault).

Edit: extract and override example:

Firstly, you need to extract problematic part of your class to separate method that will be later overridden. This problematic part is naturally interaction with IDbSet:

public class EquiptmentRepository
{
    public void Add(Manufacturer m)
    {
        // perform some local logic before calling IDbSet.Add
        this.AddToDbSet(m);
    }

    protected virtual AddToDbSet(Manufacturer m)
    {
        this.context.Manfuacturers.Add(m);
    }
}

Now, in your testable version of EquiptmentRepository you override AddToDbSet to make testing easier, for example by simply adding some state verification:

internal void TestableEquiptmentRepository: EquiptmentRepository
{
    internal List<Manufacturer> AddedManufacturers = new List<Manufacturer>();

    protected override void AddToDbSet(Manufacturer m)
    {
        // we're no longer calling DbSet.Add but kind of rolling
        // our own basic mock and tracking what objects were
        // add by simply adding them to internal list
        this.AddedManufacturers.Add(m);
    }
}

Whenever Add is called, passed manufacturer is going to be add to list if it would be added to DbSet in real case. In your test, you can simply verify your testable version of the class state:

[Test]
public void AddManufacturer_DoesNotAddExistingManufacturersToDbSet()
{
    // Arrange
    var stubEntities = MockRepository.GenerateStub<IWsStatContext>();
    var stubManufacturers = MockRepository.GenerateStub<IDbSet<Manufacturer>>();
    var manufacturer = new Manufacturer() { Name = "Dummy" };
    stubManufacturers.Add(manufacturer);
    stubEntities.Manufacturers = stubManufacturers;

    // Act
    var sut = new TestableEquiptmentRepository(stubEntities);
    sut.AddManufacturer(manufacturer);

    // Assert
    Assert.AreEqual(sut.AddedManufacturers.Count(), 0);
}

This way you can verify all the logic in EquiptmentRepository.Add without the need to interact with DbSet at all.

Upvotes: 1

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236268

You can't mock static methods with RhinoMocks or Moq (TypeMock can).

State verification on fake object actually verifies your mock, not your system under test.

There is a technique which can make your code testable (but I think price is too high). You will have to extract extension methods to interface, and replace usage of System.Linq.Enumerable extensions with your own:

var item = items.MyExtensions().SingleOrDefault();

BTW when I face with static method mocking, I usually do one of following things:

  • Pass result of static method execution. E.g. if I need current date in tested method, instead of calling DateTime.Today I pass current time as parameter Foo(DateTime date).
  • Wrap static calls in some non-static object. E.g. if I need to get some configuration settings, instead of calling ConfigurationManager.AppSettings["foo"], I create non-static class BarConfiguration which delegates all work to static ConfigurationManager (and implements interface IBar { int Foo { get; }).
  • Do not mock it. Looks like I'm testing something what I should not test (Enumerable extensions, Log, etc)

Consider not to unit-test your repositories. Integration testing for data access logic makes much more sense.

Upvotes: 0

Related Questions