Если вы не можете высмеивать создание TFoo1
, то вы не можете высмеивать TFoo1
.Прямо сейчас, TFoo2
отвечает за создание всех экземпляров TFoo1
, но если это не основная цель TFoo2
, то это действительно усложнит юнит-тестирование.
Одно из решений, как вы предложили, передать TFoo2
любых TFoo1
необходимых ему экземпляров.Это может усложнить весь ваш текущий код, который уже вызывает TFoo2
методы.Другой способ, который немного более удобен для юнит-тестирования, - предоставить factory для TFoo1
.Фабрика может быть такой же простой, как указатель на функцию, или может быть целым классом.В Delphi метаклассы также могут служить фабриками.Передайте фабрику TFoo2
, когда она будет построена, и когда TFoo2
потребуется экземпляр TFoo1
, он может вызвать фабрику.
Чтобы уменьшить изменения в остальной части вашего кода, вы можете сделать фабрикупараметр имеет значение по умолчанию в конструкторе TFoo2
.Тогда вам не нужно менять код приложения.Просто измените код модульного тестирования, чтобы предоставить заводской аргумент, отличный от заданного по умолчанию.
Что бы вы ни делали, вам нужно сделать виртуальный TFoo1.DoSomething1
, иначе насмешка будет бесполезной.
Используя метаклассы, ваш код может выглядеть следующим образом:
type
TFoo1 = class
procedure DoSomethign1; virtual;
end;
TFoo1Class = class of TFoo1;
TFoo2 = class
private
oFoo1 : TFoo1;
FFoo1Factory: TFoo1Class;
public
constructor Create(AFoo1Factory: TFoo1Class = nil);
end;
constructor TFoo2.Create;
begin
inherited Create;
FFoo1Factory := AFoo1Factory;
if not Assigned(FFoo1Factory) then
FFoo1Factory := TFoo1;
oFoo1 := FFoo1Factory.Create;
end;
Теперь ваш код для модульного тестирования может предоставить фиктивную версию TFoo1
и передать ее при создании TFoo2
:
type
TMockFoo1 = class(TFoo1)
procedure DoSomething1; override;
end;
procedure TMockFoo1.DoSomething1;
begin
// TODO: Pretend to access Web service
end;
procedure TestFoo2;
var
Foo2: TFoo2;
begin
Foo2 := TFoo2.Create(TMockFoo1);
end;
Многие примеры метаклассов дают базовому классу виртуальный конструктор , но это не является строго обязательным.Вам нужен только виртуальный конструктор, если конструктор нужно вызывать виртуально - если конструктор-потомок должен будет что-то делать с параметрами конструктора, которых базовый класс еще не делает.Если потомок (в данном случае TMockFoo1
) делает все то же самое, что и его предок, тогда конструктор не обязательно должен быть виртуальным.(Также помните, что AfterConstruction
уже виртуален, так что это еще один способ заставить потомка выполнять дополнительные операции без необходимости виртуального конструктора.)