Создавайте класс и автоматически инициализируйте зависимости с помощью FakeItEasy
Можно ли создать тестируемый класс с помощью FakeItEasy, где все зависимости, объявленные в конструкторе, автоматически инициализируются с помощью подделок?
Представьте себе класс:
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();
}
}
Затем я создаю все поддельные объекты в тестовой настройке и создаю свой тестируемый класс с помощью этих подделок:
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-мире, что при использовании 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();
}
}
и высмеиваемые классы автоматически инициализируются подделками. Есть ли эквивалентная процедура или функция в C# с FakeItEasy?
2 ответа
Я думаю, что вы хотите что-то вроде Автоматически вводить подделки в тестовом приспособлении с FakeItEasy. Ты используешь [Fake]
отмечать подделки для инъекций и [UnderTest]
отметить тип продукции для тестирования.
Мы действительно должны поместить это в документацию.
С другой стороны,
- AutoFixture имеет модуль https://www.nuget.org/packages/AutoFixture.AutoFakeItEasy,
- есть интеграция Autofac FakeItEasy, а также
- Интеграция Ninject FakeItEasy
Я видел «Автоматически внедрять подделки в текстовую фикстуру с помощью FakeItEasy», и моя первоначальная реакция была удивлена тем, что это отличалось от моего предубеждения, главным образом потому, что для этого нужны «навязчивые» изменения, которые атрибутируют тестовый код... но, возможно, это чрезмерная реакция.
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 и т. д. предлагают способы преодолеть это с подделкой почти всего по умолчанию, что вполне может быть тем, что вы хотите ... и вы можете переопределить это, если нет.]