Caliburn.Micro icon indicating copy to clipboard operation
Caliburn.Micro copied to clipboard

Support for auto-mocking of interfaces done automatically by container

Open tapika opened this issue 2 years ago • 3 comments

Normally when you want to perform unit testing - instead of using normal implementation - you use new Mock<interface> object instead.

SimpleContainer operates on real implementations obtained from real assembies, real interfaces.

Would it be possible to create such auto-mocking container so container itself would instantiate mocks as implementation requires ?

In theory if we have:

class Class1
{
     public Class1( Interface1 i1, Interface i2, Interface i3 );
}

Normally when doing normal implementation, you could write something like this:

var class1 = new Class1( IoC.Get<Interface1>(), IoC.Get<Interface2>(), IoC.Get<Interface3>() );
(*1)

(Or similar SimpleContainer calls)

When testing - you can either instantiate new Mock's for each interface and register to container (manual work), or container itself could automatically create new Mocks for each interface.

// If type not registered - automock it
MockingSimpleContainer cont ( (type) => { return new Mock<type>() };

var class1 = cont.New<Class1>();
This would have similar effect as in (*1) - only in unit test application, so everything would be auto-mocked.

This is of course pseudo code, in reality new Mock would require figuring out Mock constructor method and call it (see https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-examine-and-instantiate-generic-types-with-reflection)

Using this kind of approach could simplify test writing.

tapika avatar Feb 26 '22 09:02 tapika

What is to hinder that you register the Mock implementation in the container? It would make sense to have a composition root for testing. Also the SimpleContainer is just as it implies supposed to be simple that is why Caliburn.Micro can be extended to use other IoC containers. I am not sure it would be valuable to build in support for automatic Mocks.

KasperSK avatar Mar 07 '22 08:03 KasperSK

What is to hinder that you register the Mock implementation in the container?

That can be done as well. But why container cannot do this automagically ? I guess container has knowledge on interfaces which it supports, as well it can get also information on class which it needs to instantiate. The rest is more or less computable. Only thing which is missing is maybe interfaces caliburn does not have - then it could query via separate callback function what to do about them.

tapika avatar Mar 08 '22 18:03 tapika

https://github.com/tapika/swupd/blob/master/src/chocolatey.tests2/infrastructure.app/configuration/CommandContext.cs#L202

=> Instantiate mock container dynamically, mock type is determined at run-time.

https://github.com/tapika/swupd/blob/master/src/chocolatey.tests2/infrastructure.app/configuration/CommandContext.cs#L213

=> Potentially plug it under logger context.

CommandContext is such example of such dynamic mocking container, which I would like to see in context of IoC container as well.

Dynamically created mock can be used then later on by the test itself.

"container" side:

https://github.com/tapika/swupd/blob/master/src/chocolatey.tests2/infrastructure.app/configuration/CommandContext.cs#L60

client side:

https://github.com/tapika/swupd/blob/master/src/chocolatey.tests2/infrastructure.app/configuration/TestChocolateyOptionSet.cs#L134

Documentation on everything if someone wonders how everything ties together:

https://github.com/tapika/swupd/blob/master/docs/testing.md

(Irrelevant from this ticket perspective)

tapika avatar Mar 21 '22 17:03 tapika