我有以下界面:
unit uICodec;
interface
uses UITypes,uTPLb_CryptographicLibrary ;
type
ICodec = interface
['{B1858F24-5B76-4468-8BD5-55684EA43CCD}']
procedure EncryptString(const Plaintext: string; var CipherText_Base64:
ansistring);
procedure DecryptString(var Plaintext: string; const CipherText_Base64:
ansistring);
function RCGL:TCryptographicLibrary;
procedure WCGL(const c:TCryptographicLibrary);
property CryptoLibrary:TCryptographicLibrary read RCGL write WCGL;
function RSCI:string;
procedure WSCI(const c:string);
property StreamCipherId:string read RSCI write WSCI;
function RBCI:string;
procedure WBCI(const c:string);
property BlockCipherId:string read RBCI write WBCI;
function RCMI:string;
procedure WCMI(const c:string);
property ChainModeId:string read RCMI write WCMI;
function RPWD:string;
procedure WPWD(const c:string);
property Password:string read RPWD write WPWD;
end;
与其相关的模拟类,其中函数/过程不执行任何操作:
unit uMockTPWDDBManager;
interface
uses classes,uTPLb_CryptographicLibrary,uICodec,UITypes;
type
TMockCodec1 = class(TInterfacedObject,ICodec)
// constructor Create (Aowner: TComponent);
procedure EncryptString(const Plaintext: string; var CipherText_Base64:
ansistring);
procedure DecryptString(var Plaintext: string; const CipherText_Base64:
ansistring);
private
function RCGL:TCryptographicLibrary;
procedure WCGL(const c:TCryptographicLibrary);
function RSCI:string;
procedure WSCI(const c:string);
function RBCI:string;
procedure WBCI(const c:string);
function RCMI:string;
procedure WCMI(const c:string);
function RPWD:string;
procedure WPWD(const c:string);
end;
....
function TMockCodec1.RBCI: string;
begin
end;
...
initialization
GlobalContainer.RegisterComponent<TMockCodec1>.Implements<ICodec>;
end.
当我尝试使用以下代码运行测试时,出现错误
procedure TestTPWDDBManager.SetUp;
begin
GlobalContainer.Build;
FCodec1:=ServiceLocator.GetService<ICodec>;//EUnsatisfiedDependencyException with message 'Unsatisfied dependency for the service "ICodec"
我仔细检查过,ICodec中的所有程序/功能都与TMockCodec1中的相同。
我哪里错了?
[UPDATE]
我遵循大卫的建议,我想,我发现了这个错误。我的.dpr如下:
uses DUnitTestRunner,
uMockTPWDDBManager in 'uMockTPWDDBManager.pas',
uTCodec1 in '..\uTCodec1;.pas';
单元uMockTPWDDBManager具有以下初始化:
...
initialization
GlobalContainer.RegisterComponent<TCodec1>.Implements<ICodec>;
end.
和单元uTCodec1具有以下初始化:
initialization
GlobalContainer.RegisterComponent<TMockCodec1>.Implements<ICodec>;
当我从.dpr中删除uTCodec1时,错误消息消失了。
我认为同一个接口的两个初始化不能在同一个.dpr中共存。
我说错了吗?
[SSCCE]
不幸的是,我只能将单位数减少到3个。
以下是.dpr
program TPWDDBManager;
{
Delphi DUnit Test Project
-------------------------
This project contains the DUnit test framework and the GUI/Console test runners.
Add "CONSOLE_TESTRUNNER" to the conditional defines entry in the project options
to use the console test runner. Otherwise the GUI test runner will be used by
default.
}
{$IFDEF CONSOLE_TESTRUNNER}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
DUnitTestRunner,
TestuTPWDDBManager in 'TestuTPWDDBManager.pas',
uTPWDDBManager in '..\uTPWDDBManager.pas';
{$R *.RES}
begin
DUnitTestRunner.RunRegisteredTests;
end.
这是第一个单位:
unit uTPWDDBManager;
interface
uses IniFiles,uICodec,UITypes;
type
ICodec = interface
['{B1858F24-5B76-4468-8BD5-55684EA43CCD}']
procedure EncryptString(const Plaintext: string; var CipherText_Base64:
ansistring);
procedure DecryptString(var Plaintext: string; const CipherText_Base64:
ansistring);
end;
IInsertPassword = interface
['{B197F2EE-8C65-4E59-897F-F69E6E8D252F}']
function ShowModal: Integer;
function GetOldPWD : String;
function GetNewPWD : String;
end;
IMessageDlg = interface
['{5A527174-50D4-4BB9-8E9F-6A9926B2893C}']
function MessageDlg(const Msg: string; DlgType: TMsgDlgType; Buttons:
TMsgDlgButtons;HelpCtx: Longint): Integer;
end;
IPWDDBManager = interface
['{51C993FE-D96A-4419-AB80-00D65E16C6F8}']
function GetDBPWD (const Key : string; var Reset : Boolean): string;
function ChangePWD (OldKey, NewKey : string): string;
procedure Reset;
end;
TPWDDBManager=class(TIniFile,IPWDDBManager)
private
FRefCount: Integer;
protected
function QueryInterface(const IID: TGUID; out Obj): Integer; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
constructor Create(const FileName: string; Fcodec: ICodec ;
meggagedlg: IMessageDlg);virtual;
function GetDBPWD (const Key: string; var Reset: Boolean): string;
function ChangePWD (OldKey, NewKey : string): string;
procedure Reset;
property RefCount: Integer read FRefCount;
end;
implementation
uses Dialogs, SysUtils,Spring.container;
{ TPWDDBManager }
function TPWDDBManager.ChangePWD (OldKey, NewKey : string): string;
begin
end;
constructor TPWDDBManager.Create(const FileName: string; Fcodec: ICodec ;
meggagedlg: IMessageDlg);
begin
end;
function TPWDDBManager.GetDBPWD (const Key : string; var Reset : Boolean): string;
begin
result:='';
end;
function TPWDDBManager.QueryInterface(const IID: TGUID; out Obj): Integer;
const
E_NOINTERFACE = $80004002;
begin
if GetInterface(IID, Obj) then
Result := 0
else
Result := E_NOINTERFACE;
end;
procedure TPWDDBManager.Reset;
begin
end;
function TPWDDBManager._AddRef: Integer;
begin
end;
function TPWDDBManager._Release: Integer;
begin
end;
end.
这是第二个:
unit TestuTPWDDBManager;
{
Delphi DUnit Test Case
----------------------
This unit contains a skeleton test case class generated by the Test Case Wizard.
Modify the generated code to correctly setup and call the methods from the unit
being tested.
}
interface
uses TestFramework,IniFiles,classes,UITypes,uTPWDDBManager;
type
tcodec1=class(tcodec)
end;
TMockCodec1 = class(TInterfacedObject,ICodec)
procedure EncryptString(const Plaintext: string; var CipherText_Base64:
ansistring);
procedure DecryptString(var Plaintext: string; const CipherText_Base64:
ansistring);
end;
TMockInsertPassword1 = class(TInterfacedObject,IInsertPassword )
constructor create (Aowner: TComponent);
function ShowModal: Integer;
function GetOldPWD : String;
function GetNewPWD : String;
end;
TMockMessagedlg = class(TInterfacedObject,IMessageDlg)
function MessageDlg(const Msg: string; DlgType: TMsgDlgType; Buttons:
TMsgDlgButtons;HelpCtx: Longint): Integer;
end;
// Test methods for class TPWDDBManager
TestTPWDDBManager = class(TTestCase)
strict private
FCodec1: ICodec;
FPWDDBManager: IPWDDBManager;
Inifile:TInifile;
mockmessagedlg:IMessageDlg;
ReturnValue: string;
public
procedure SetUp; override;
procedure TearDown; override;
published
procedure TestGetPWD;
end;
implementation
uses SysUtils,Spring.container,Spring.Services;
procedure TestTPWDDBManager.SetUp;
begin
GlobalContainer.Build;
FCodec1:=ServiceLocator.GetService<ICodec>;
mockmessagedlg:=ServiceLocator.GetService<IMessageDlg>;
FPWDDBManager :=TPWDDBManager.Create(ChangeFileExt((ParamStr(0)),'.ini'),
FCodec1,mockmessagedlg);
end;
procedure TestTPWDDBManager.TearDown;
begin
Inifile.Free;
end;
procedure TestTPWDDBManager.TestGetPWD;
var
reset: boolean;
begin
//// this instruction deletes a .ini file ///
DeleteFile(ChangeFileExt((ParamStr(0)),'.ini'));
ReturnValue := FPWDDBManager.GetDBPWD('a',reset);
CheckEqualsString(ReturnValue,'');
// TODO: Validate method results
end;
procedure TMockCodec1.DecryptString(var Plaintext: string;
const CipherText_Base64: ansistring);
begin
Plaintext:=StringReplace(string(CipherText_Base64),'encripted','decripted',[
rfReplaceAll,rfIgnoreCase]);
end;
procedure TMockCodec1.EncryptString(const Plaintext: string;
var CipherText_Base64: ansistring);
begin
CipherText_Base64:=ansistring(StringReplace(Plaintext,'decripted','encripted',[
rfReplaceAll,rfIgnoreCase]));
end;
{ TMockInsertPassword1 }
constructor TMockInsertPassword1.create(Aowner: TComponent);
begin
end;
function TMockInsertPassword1.GetNewPWD: String;
begin
result:='new pwd decripted';
end;
function TMockInsertPassword1.GetOldPWD: String;
begin
result:='old pwd encripted';
end;
function TMockInsertPassword1.ShowModal: Integer;
begin
result:=1;
end;
{ TMockMessagedlg }
function TMockMessagedlg.MessageDlg(const Msg: string; DlgType: TMsgDlgType;
Buttons: TMsgDlgButtons; HelpCtx: Integer): Integer;
begin
result:=-1;
if Msg='File ' +
'E:\Delphi\Projects\Components\Tests\TPWDDBManager\TestFolder\Win32\Debug\.ini doesn''t exists' then
result:=1
else if Msg='File ' +
'E:\Delphi\Projects\Components\Tests\TPWDDBManager\TestFolder\Win32\Debug\.ini doesn''t exists' then
result:=2
end;
initialization
// Register any test cases with the test runner
RegisterTest(TestTPWDDBManager.Suite);
GlobalContainer.RegisterComponent<TMockCodec1>.Implements<ICodec>;
GlobalContainer.RegisterComponent<tcodec1>.Implements<ICodec>;
GlobalContainer.RegisterComponent<TMockInsertPassword1>.Implements<IInsertPassword>;
GlobalContainer.RegisterComponent<TMockMessagedlg>.Implements<IMessageDlg>;
end.
我可以编译源代码,但是当我运行测试时,我收到错误消息EUnsatisfiedDependencyException,并显示消息“对服务的不满意依赖”ICodec“
如果我删除了类tcodec1并删除了GlobalContainer.RegisterComponent<tcodec1>.Implements<ICodec>;
然后没有更多错误消息。
是因为你不能在同一个项目中注册多个类并引用相同的接口吗?
答案 0 :(得分:0)
如果您对同一服务类型进行多次注册,则应使用不同的名称注册这些注册。否则,如果您调用Resolve / GetService,容器将无法决定返回哪一个。
我想为单元测试提供的另一个建议是:不要在那里使用容器,而是手动连接所需的依赖项(即模拟)。在你的例子中它甚至没有意义,因为你没有让容器构建你正在测试的系统(所以它可以注入你正在嘲笑的任何依赖)。
如果您使用中继中的最新版本,则在注册多个未命名服务时不应再出现错误。但是,它将返回服务的最后一个注册组件(或者您调用的最后一个组件.AsDefault on)。这种情况最有可能发生变化,因此我的第一个建议仍然存在(如果您有多个,则使用命名注册)。