MattHoneycutt / SpecsFor

SpecsFor is a light-weight Behavior-Driven Development framework that focuses on ease of use for *developers* by minimizing testing friction.
http://specsfor.com
MIT License
197 stars 70 forks source link

How do I configure the auto mocker? #120

Open psyren89 opened 5 years ago

psyren89 commented 5 years ago

I was previously using SpecsFor 5.0.1, in which I could do this:

public interface IBar : ISpecs
{
    IBaz Baz { get; set; }
}

public class Foo : Behavior<IBar>
{
    public override void SpecInit(IBar instance)
    {
        instance.MockContainer.Configure(cfg => cfg.For<IBaz>().Use(instance.Baz));
    }
}

After updating to SpecsFor 7.0.0, this is no longer available, and there is no documentation or summary of changes to consult regarding these breaking changes.

Where would I find the facility to configure the automocker's behaviour?

MattHoneycutt commented 5 years ago

Hi @psyren89! I apologize for not having the docs updated yet.

In SpecsFor 7, you can do the following:

public class Foo : Behavior<IBar>
{
    public override void SpecInit(IBar instance)
    {
        var mocker = (StructureMapAutoMocker<IBar>) instance.Mocker;

            mocker.MoqAutoMocker.Container.Configure(cfg =>
            {
                cfg.For<IBaz>().Use(instance.Baz)
            });
    }
}

I'm going to leave this issue open to remind me that we need to add an easier way of getting to this functionality.

bandoyer commented 5 years ago

I have the following:

public class when_performing_some_test : SpecsFor<SomeService>, INeedFakeContext
{
    ...
}

public interface INeedFakeContext : ISpecs
{
    FakeDbContext FakeContext { get; set; }
}

public class FakeContextProvider : Behavior<INeedFakeContext>
{
    public override void SpecInit(INeedFakeContext instance)
    {
        instance.FakeContext = new FakeDbContext();

        // *** This is the issue ***
        // *** instance.Mocker is an instance of SomeService so the cast fails ***
        var mocker = (StructureMapAutoMocker<INeedFakeContext>)instance.Mocker;
        mocker.MoqAutoMocker.Container.Configure(cfg => 
        { 
            cfg.For<IADAPContext>().Use(instance.FakeContext); 
        });
    }
}

As I mentioned in the comment, when it gets to the SpecInit method, instance.Mocker is unable to cast to INeedFakeContext. Am I missing something? I've been trying to upgrade to SpecsFor 7 (from 5) and ran into this issue.

Thanks for the help!

MattHoneycutt commented 5 years ago

Hmm, that sounds like a bug. I'll investigate and see what's going on.

MattHoneycutt commented 5 years ago

Ugh, I've really dropped the ball on supporting SpecsFor lately. If anyone still cares, here's the approach I'm considering for resolving this issue.

I'm planning to add a new extension method, GetMockContainer(), to each implementation of SpecsFor (StructureMap and Autofac for now). Calling this on an ISpecs instance will return the underlying automocking container for the library you're using.

Here's an implementation of the extension method for StructureMap:

    public static class StructureMapAutoMockerExtensions
    {
        public static AutoMockedContainer GetMockContainer(this ISpecs specs)
        {
            dynamic specsDynamic = specs;

            return specsDynamic.Mocker.MoqAutoMocker.Container;
        }
    }

@bandoyer, if you're still interested, you should be able to drop this in to your project, then update your spec like this:

public class FakeContextProvider : Behavior<INeedFakeContext>
{
    public override void SpecInit(INeedFakeContext instance)
    {
        instance.FakeContext = new FakeDbContext();

        // *** This is the issue ***
        // *** instance.Mocker is an instance of SomeService so the cast fails ***
        //var mocker = (StructureMapAutoMocker<INeedFakeContext>)instance.Mocker;
                var container = instance.GetMockContainer();
        container.Configure(cfg => 
        { 
            cfg.For<IADAPContext>().Use(instance.FakeContext); 
        });
    }
}

Let me know if that approach works for your use case and makes sense. If so, I'll get it added to the package.

ChrisMarinos commented 5 years ago

FWIW, I ran into a similar problem, and this fix worked for me. Since we're using Autofac, I switched to using the following variation of the above helper:

    public static class AutofacAutoMockerExtensions
    {
        public static AutoMock GetMockContainer(this ISpecs specs)
        {
            dynamic specsDynamic = specs;

            return specsDynamic.Mocker.InternalMocker;
        }
    }