IService[]
). But what if you need to get one specific service from a container? To do this, Unity provides the ability to give objects names. For example, to implement an analogue of this code here.var svc10 = new MyService(10);<br/>
var svc15 = new MyService(15);<br/>
you need to register just the “nominal” mappings, or rather:var uc = new UnityContainer();<br/>
//
uc.RegisterType<IService, MyService>( "ten" ,<br/>
new InjectionConstructor( new InjectionParameter(10)))<br/>
.RegisterType<IService, MyService>( "fifteen" ,<br/>
new InjectionConstructor( new InjectionParameter(15)));<br/>
//
var _10 = uc.Resolve<IService>( "ten" );<br/>
var _15 = uc.Resolve<IService>( "fifteen" );<br/>
Also, it is possible to get all registered mappings. This is done using the ResolveAll()
function:foreach (IService svc in uc.ResolveAll<IService>() )<br/>
svc.DoSomething();<br/>
public class HelperClass<br/>
{<br/>
public void DoSomething()<br/>
{<br/>
Console.WriteLine( "Doing something" );<br/>
}<br/>
}<br/>
public class SomeService<br/>
{<br/>
[Dependency]<br/>
public HelperClass MyHelperClass { get; set; }<br/>
public void Go() { MyHelperClass.DoSomething(); }<br/>
}<br/>
<br/>
â‹®<br/>
<br/>
var uc = new UnityContainer();<br/>
var ss = Activator.CreateInstance<SomeService>();<br/>
Console.WriteLine(ss.MyHelperClass == null ); // True
Having received the object as ready, the DI mechanism was not applied, but this is not a problem - it can be obtained post-factum using the BuildUp()
:var uc = new UnityContainer();<br/>
// ""
var ss = Activator.CreateInstance<SomeService>();<br/>
Console.WriteLine(ss.MyHelperClass == null ); // True
var ss2 = uc.BuildUp<SomeService>(ss) ;<br/>
Console.WriteLine(ss2.MyHelperClass == null ); // False
ss2.Go();<br/>
Console.WriteLine(ReferenceEquals(ss, ss2)); // True
As you can see from the code, the BuildUp()
method “builds” on an existing object those dependencies that would have been created if the object would have issued an IoC container. Our example also demonstrates that when a new object is added, it is not created — the old one is used.Resolve()
method changes. In particular, if our service implements IService
, then we can still do a mapping from IService
to an existing object, and later issue the same copy without any problems. To do this, we register an existing object using the RegisterInstance()
method:var uc = new UnityContainer();<br/>
var ss = Activator.CreateInstance<SomeService>();<br/>
uc.BuildUp<SomeService>(ss);<br/>
// IService ss
uc.RegisterInstance<IService>(ss); <br/>
var svc = uc.Resolve<IService>();<br/>
svc.Go();<br/>
public interface IAdder<br/>
{<br/>
int Add( int first, int second);<br/>
}<br/>
public sealed class AdderService : IAdder<br/>
{<br/>
public int Add( int first, int second)<br/>
{<br/>
return first + second;<br/>
}<br/>
}<br/>
public class MyApp<br/>
{<br/>
private IAdder adder;<br/>
public MyApp(IAdder adder)<br/>
{<br/>
this .adder = adder;<br/>
}<br/>
public int AddAndMultiply( int first, int second, int third)<br/>
{<br/>
return adder.Add(first, second) * third;<br/>
}<br/>
}<br/>
The following test is an integration test, since specific copies of two classes are involved in it - AdderService
and MyApp
:[Test]<br/>
public void NotAUnitTest()<br/>
{<br/>
var uc = new UnityContainer();<br/>
uc.RegisterType<IAdder, AdderService>();<br/>
Assert.AreEqual(20, uc.Resolve<MyApp>().AddAndMultiply(2, 3, 4));<br/>
}<br/>
In the working code, by the way, the container would be configured exactly as shown above. But when testing, we can replace AdderService
different, “test” implementation. The easiest option is to write some FakeAdderService
which simply returns a constant:public class FakeAdderService : IAdder<br/>
{<br/>
public int Add( int first, int second)<br/>
{<br/>
return 5; // !
}<br/>
}<br/>
Now we change the container and - hurray! - we have a full unit test:[Test]<br/>
public void UnitTestWithFake()<br/>
{<br/>
var uc = new UnityContainer();<br/>
uc.RegisterType<IAdder, FakeAdderService >();<br/>
Assert.AreEqual(20, uc.Resolve<MyApp>().AddAndMultiply(2, 3, 4));<br/>
}<br/>
Of course, our fake - on a particularly flexible - because it returns exactly the value that we expect when you enter (that is, in the test we write “2, 3” and in fake we return the number 5). And if suddenly we need to use other values ​​in another test, what to do then? Make another fake object? Or do we make our fake parameterizable and configure its parameters in a container?IService
object is automatically substituted into it, which would return exactly the value we need:var uc = new UnityContainer();<br/>
var svc = Isolate.Fake.Instance<AdderService>();<br/>
Isolate.WhenCalled(() => svc.Add(0, 0)).WillReturn(5);<br/>
uc.RegisterInstance<IAdder>(svc);<br/>
Assert.AreEqual(20, uc.Resolve<MyApp>().AddAndMultiply(2, 3, 4));<br/>
Above, I created a fake object using Typemock (you can read about it here ), set the behavior for calling the Add()
method (always return 5), and added it to the container. Thus, he received a complete, easily configurable unit test.Source: https://habr.com/ru/post/63568/
All Articles