How to check a class that accepts a factory?

I am trying to test a class that takes factory ( Func<T> ) and I use Moq and AutoFixture.

What is the best way to configure the "environment" to find out if the factory is used and how many times and what methods have been used for returned instances?

I am currently doing Mock'ing T and Injecting a Func<T> , which counts the number of returned Mock instances:

 public class SystemUnderTest { public SystemUnderTest(Func<IMyClass> c) { try { var c1 = c(); c1.Name="n1"; c1.Send(); } catch(Exception){ var c2 = c(); c2.Name="n2"; c2.Send(); } } } private Mock<IMyClass> MockFactory() { var m = new Mock<IMyClass>(); m.SetupProperty(mc=>mc.Name); _returnedStubs.Add(m); return m; } [Test] public void TestSomething() { var fixture = new Fixture(); fixture.Inject(()=>MockFactory().Object) var sut = fixture.CreateAnonymous<SystemUnderTest>(); Assert.That(_returnedStubs.Count,Is.Equal(1)); _returnedStubs[0].Verify(m=>m.Send(),Times.Exactly(1)); _returnedStubs[0].Verify(m=>m.Name = "n1"); } 

But it seems to me that this is not good / ugly. And I'm sure the instance variable in the test class is dangerous.

+4
source share
2 answers

Since AutoFixture is able to create anonymous delegates when asked to create an anonymous instance of SystemUnderTest , it will also automatically provide an anonymous Func<IMyClass> , which in turn returns an anonymous IMyClass instance when called.

This means that given this scenario:

 public class SystemUnderTest { public SystemUnderTest(Func<IMyClass> c) { try { var c1 = c(); // ... } catch (Exception) { var c2 = c(); // ... } } } 

following code:

 var fixture = new Fixture(); var sut = fixture.CreateAnonymous<SystemUnderTest>(); 

assigns the variables c1 and c2 anonymous IMyClass instances. In addition, if you configure AutoFixture to work as a self-similar container , for example, using AutoMoqCustomization , these anonymous IMyClass instances will also be Moq IMyClass :

 var fixture = new Fixture(); fixture.Customize(new AutoMoqCustomization()); var sut = fixture.CreateAnonymous<SystemUnderTest>(); 

However, this information, although useful, does not really help you in your particular case, since you need to keep the mock objects returned by the Func<IMyClass> factory method in your test to customize their behavior and make some statements about how they interacted with.

The best solution, in my opinion, is to change the implementation of the factory method from Func<IMyClass> to the interface . Thus, you can create a fake factory that returns different mocks of the IMyClass interface when the Create method is called several times in the sequence :

So, given this example:

 public interface IFactory<T> { T Create(); } public class SystemUnderTest { public SystemUnderTest(IFactory<IMyClass> factory) { try { var c1 = factory.Create(); // ... } catch (Exception) { var c2 = factory.Create(); // ... } } } 

You can configure the test script as follows:

  // Given var c1 = new Mock<IMyClass>(); var c2 = new Mock<IMyClass>(); // TODO: setup the behavior of the mock objects var factory = new Mock<IFactory<IMyClass>>(); factory.Setup(s => s.Create()).ReturnsInOrder(c1.Object, c2.Object); // When var fixture = new Fixture(); fixture.Inject(() => factory.Object) var sut = fixture.CreateAnonymous<SystemUnderTest>(); // Then // TODO: verify the expectations on the mock objects 

Note that ReturnsInOrder is a custom extension method that uses the Callback method in Moq to return various values ​​from the hatched method when it is called several times in a row.

+7
source

One of the best ways is to create your own function and pass it on, and then set up expectations or specify there.

 int numberOfTimesUsed = 0; myObject.Func = (x) => { numberOfTimesUsed++; Assert.IsNotNull(x); // checks if x passed was not null }; ... Assert.AreEqual(2, numberOfTimesUsed); // checks if called twice 
+1
source

Source: https://habr.com/ru/post/1403075/


All Articles