创建 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]
标记要测试的生产类型。
我们确实应该将其放入文档中。
或者,
- AutoFixture 有一个 AutoFixture.AutoFakeItEasy 模块,
- 有Autofac FakeItEasy integration,还有
- Ninject FakeItEasy integration
我看到了 '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 等提供了通过默认伪造几乎所有内容来克服这个问题的方法,这很可能是您想要的......如果不是,您可以覆盖它。]
是否可以使用 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]
标记要测试的生产类型。
我们确实应该将其放入文档中。
或者,
- AutoFixture 有一个 AutoFixture.AutoFakeItEasy 模块,
- 有Autofac FakeItEasy integration,还有
- Ninject FakeItEasy integration
我看到了 '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 等提供了通过默认伪造几乎所有内容来克服这个问题的方法,这很可能是您想要的......如果不是,您可以覆盖它。]