Re: Auto mock dependency injection

96 views
Skip to first unread message

Alexander Groß

unread,
Aug 2, 2012, 1:30:04 AM8/2/12
to machin...@googlegroups.com
Have you tried Machine.Fakes? I think it will help you in reaching your goal.
https://github.com/machine/machine.fakes 

Machine.Fakes doesn't change MSpec's API delegates (Because, It), but provides The<T> and A<T> methods to access your mocks.

(Extending the API delegates would require to rewrite the runners as well.)

Alex
--
Alexander Groß
http://therightstuff.de/


2012/7/31 Josh Perry <jo...@6bit.com>
I've been looking at ways to make the setup of my test's a little less verbose, I've recently started using an automocking DI container (Ninject MoqMockingKernel) and it works really well for supplying dependencies to my SUT objects. 

Here's an example of a simple test using the automocking kernel (right now, provided by the base class):

    [Subject("Main Window")]
    public class When_first_opening_the_window : MockingBase
    {
        static MainViewModel _vm;
        static readonly IEnumerable<TestDirectory> _directories = new TestDirectory[]
        {
            new TestDirectory() {Id = 1, Name = "One"},
            new TestDirectory() {Id = 2, Name = "Two"},
            new TestDirectory() {Id = 2, Name = ""}
        };
            
        Establish context = () => {
            Kernel.GetMock<Data.IRepository<Model.TestDirectory>>()
                .SetupGet(x => x.Query).Returns(_directories.AsQueryable());
        };

        Because of = () => _vm = Kernel.Get<MainViewModel>();

        It should_show_the_list_of_directories_with_names = () => _vm.Directories.ShouldContainOnly(_directories.Where(x => !string.IsNullOrEmpty(x.Name)));

        It should_by_default_select_the_first_directory = () => _vm.SelectedDirectory.ShouldEqual(_directories.First());
    }

What I'm thinking would be interesting, and I'm not sure if anyone has worked on a feature like this yet, is to register a mocking provider with the test framework which would then inject mocks and instances automatically into the test methods.

Using this hypothetical construct, the above test class could look something like this.

    [Subject("Main Window")]
    public class When_first_opening_the_window
    {
        static MainViewModel _vm;
        static readonly IEnumerable<TestDirectory> _directories = new TestDirectory[]
        {
            new TestDirectory() {Id = 1, Name = "One"},
            new TestDirectory() {Id = 2, Name = "Two"},
            new TestDirectory() {Id = 2, Name = ""}
        };
            
        Establish<Mock<Data.IRepository<Model.TestDirectory>>> context = (repo) => {
            repo.SetupGet(x => x.Query).Returns(_directories.AsQueryable());
        };

        Because<MainViewModel> of = (vm) => _vm = vm;

        It should_show_the_list_of_directories_with_names = () => _vm.Directories.ShouldContainOnly(_directories.Where(x => !string.IsNullOrEmpty(x.Name)));

        It should_by_default_select_the_first_directory = () => _vm.SelectedDirectory.ShouldEqual(_directories.First());
    }

While in this particular test the static _vm variable assignmet may make sense for the clear intent of "on creation", what may work a little better is injecting the singletons directly into the assert phases, nixing the act phase in this case:

        It<MainViewModel> should_show_the_list_of_directories_with_names = (vm) => vm.Directories.ShouldContainOnly(_directories.Where(x => !string.IsNullOrEmpty(x.Name)));

        It<MainViewModel> should_by_default_select_the_first_directory = (vm) => vm.SelectedDirectory.ShouldEqual(_directories.First());

Basically the test framework, for each test class run, would provide a fresh Kernel instance with a lifetime similar to the scope of static test variables. Then when executing the different phases of the test, resolve each of the delegate dependencies using that kernel. The behavior of the Kernel used would dictate the behavior of instances provided (singleton, mock, etc...).

It would be nice for the testing code to somehow have access to the Kernel if needed; perhaps by adding it as a delegate parameter, I don't believe any DI containers have restrictions that would keep you from binding it to a type inside of itself.

This isn't anything I've even spiked on yet, thought I'd post here and see what others thought, if anyone would like to help, or what others out there are doing.

Josh

--
You received this message because you are subscribed to the Google Groups "machine_users" group.
To view this discussion on the web visit https://groups.google.com/d/msg/machine_users/-/S9JR-NLc4CoJ.
To post to this group, send email to machin...@googlegroups.com.
To unsubscribe from this group, send email to machine_user...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/machine_users?hl=en.

Reply all
Reply to author
Forward
0 new messages