创建 class 并使用 FakeItEasy 自动初始化依赖项

Create class and auto-initialize dependencies with FakeItEasy

是否可以使用 FakeItEasy 创建一个 class 被测对象,其中在构造函数中声明的所有依赖项都自动使用 fakes 初始化?

想象一下 class:

public class Inserting
{
    public Inserting(
        ITransactionService transactionService,
        ISharedData sharedData)
    {
        TransactionService = transactionService;
        SharedData = sharedData;
    }

    public ITransactionService TransactionService { get; }

    public ISharedData SharedData { get; }

    public void Enter()
    {
        TransactionService.StartTransaction();
    }
}

然后我在测试设置中创建所有假对象,并用这些假对象构建我的 class 待测对象:

public class InsertingTest
{
    private Inserting _inserting;
    private ISharedData _fakeSharedData;
    private ITransactionService _fakeTransactionService;        

    [SetUp]
    public void SetUp()
    {
        _fakeTransactionService = A.Fake<ITransactionService>();
        _fakeSharedData = A.Fake<ISharedData>();

        _inserting = new Inserting(_fakeTransactionService, _fakeSharedData);
    }

    [Test]
    public void TestEnter()
    {
        // Arrange

        // Act
        _inserting.Enter();

        // Assert
        A.CallTo(() => _fakeTransactionService.StartTransaction().MustHaveHappened();
    }
}

但我在 Java-world 中看到,当使用 Mockito 和 Dagger 2 时,你可以这样做:

public class PhoneDialer {
    private Activity activity;
    private PhoneCallListener phoneCallListener;

    @Inject
    public PhoneDialer(Activity activity, PhoneCallListener phoneCallListener) {
        this.activity = activity;
        this.phoneCallListener = phoneCallListener;
    }
}

public class PhoneDialerTest {
    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock
    PhoneCallListener phoneCallListener;

    @Mock
    Activity activity;

    @InjectMocks
    PhoneDialer dialer;

    @Test
    public void test_dialer() throws Exception {
        // Arrange

        // Act
        dialer.callNumber("abc");

        // Assert
        Mockito.verify(phoneCallListener, times(1)).startCall();
    }
}

和模拟的 classes 会自动用假货初始化。在 C# 中是否有与 FakeItEasy 等效的过程或函数?

我想你想要类似的东西 Automatically inject fakes in test fixture with FakeItEasy。您使用 [Fake] 标记要注入的假货,并使用 [UnderTest] 标记要测试的生产类型。

我们确实应该将其放入文档中。

或者,

我看到了 'Automatically inject fakes in text fixture with FakeItEasy',我最初的反应是惊讶它与我的预想不同,主要是因为它需要 'intrusive' 改变测试代码的属性......但也许这是反应过度。

FakeAttribute 和 UnderTestAttribute 确实对您的测试(和系统)设计施加了潜在的良好结构约束...

[FWLIW,在谷歌搜索之前,我曾想象过以下内容:

    containerBuilder.RegisterAsFakeCallingBaseType<SystemUnderTest>();

您可以使用 Autofac 的注册源执行类似的操作。

using Autofac;
using Autofac.Core;
using Autofac.Core.Activators.Delegate;
using Autofac.Core.Lifetime;
using Autofac.Core.Registration;
using FakeItEasy;
using Xunit;

    public interface IDependOnSomething { }
    public class IImplementThat : IDependOnSomething { }

    public class CanIResolveIt
    {
        public CanIResolveIt(IDependOnSomething it)
        {
        }
    }

    public class FakeRegistrationSourceTest
    {
        [Fact]
        public void BasicTest()
        {
            var container = new ContainerBuilder();
            container.RegisterTypes<IImplementThat>().As<IDependOnSomething>();
            container.RegisterSource(new FakeRegistrationSource<CanIResolveIt>());
            var c = container.Build();
            var theFake = c.Resolve<CanIResolveIt>();
            Assert.NotNull(theFake);
        }
    }

    public class FakeRegistrationSource<T> : IRegistrationSource
        where T : class
    {
        public bool IsAdapterForIndividualComponents => false;

        public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
        {
            var swt = service as IServiceWithType;
            if (swt == null || !typeof(T).IsAssignableFrom(swt.ServiceType)) // TODO: is this the right way around?
            {
                return Enumerable.Empty<IComponentRegistration>();
            }

            var registration = new ComponentRegistration(
              Guid.NewGuid(),
              
              new DelegateActivator(swt.ServiceType, (context, @params) =>
              {
                  List<object> v = new List<object>();
                  foreach (ParameterInfo p in typeof(T).GetConstructors().Single().GetParameters())
                  {
                      v.Add(context.Resolve(p.ParameterType));
                  }

                  return A.Fake<T>(that => that.CallsBaseMethods().WithArgumentsForConstructor(v));
              }),
              new CurrentScopeLifetime(),
              InstanceSharing.None,
              InstanceOwnership.OwnedByLifetimeScope,
              new[] { service },
              new Dictionary<string, object>());

            return new IComponentRegistration[] { registration };
        }
    }

这种方法的主要优点是当它们只有一个构造函数时(从多个构造函数中智能地选择将是一个我不打算解决的明显挑战...)

一个明显的缺点是每次你想要伪造的东西时都要显式注册。 AutoFake 等提供了通过默认伪造几乎所有内容来克服这个问题的方法,这很可能是您想要的......如果不是,您可以覆盖它。]