2011-01-20 43 views
1

我是单元测试新手。我不知道是否值得对以下代码进行单元测试。这里是用德尔福编写的示例方法:下面的代码是否值得单元测试?

function TCoreAudio.CreateAudioClient: IAudioClient; 
var 
    MMDeviceEnumerator: IMMDeviceEnumerator; 
    MMDevice: IMMDevice; 
    MixFormat: PWaveFormatEx; 
    AudioClient: IAudioClient; 
    HR: HResult; 
begin 
    Result := nil; 

    if CheckWin32Version(6, 0) then // The Core Audio APIs were introduced in Windows Vista. 
    begin 
    HR := GetInstance().CoCreateInstance(CLSID_MMDeviceEnumerator, nil, CLSCTX_ALL, 
     IMMDeviceEnumerator, MMDeviceEnumerator); 
    if Failed(HR) then 
     Exit; 
    HR := MMDeviceEnumerator.GetDefaultAudioEndpoint(eRender, eConsole, MMDevice); 
    if Failed(HR) then 
     Exit; 
    HR := MMDevice.Activate(IAudioClient, CLSCTX_ALL, nil, AudioClient); 
    if Failed(HR) then 
     Exit; 
    HR := AudioClient.GetMixFormat(MixFormat); 
    if Failed(HR) then 
     Exit; 
    HR := AudioClient.Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 0, 0, MixFormat, nil); 
    CoTaskMemFree(MixFormat); 
    if Failed(HR) then 
     Exit; 

    Result := AudioClient; 
    end; 
end; 

该方法是否值得单元测试?如果是,需要测试哪些部分?

谢谢。

回答

3

单元测试通常是自下而上的方法。所以你会开始单元测试在你的函数中使用的类。在确认所有这些类都被单元测试覆盖之后,您可以为您的函数CreateAudioClient创建一个单元测试。此功能的单元测试可能是很容易的,这样的事情:

AudioClient := CreateAudioClient; 
CheckNotNil (AudioClient); 

注意,通常单元测试类的接口,而不是一个函数或过程的主体。

希望有所帮助。

如果它是值得的问题,取决于多种因素:

  • 有多容易建立一个单元测试呢?它会是多少努力?
  • 这部分有多重要?根据这部分的重要性,答案可能总是“是的,这绝对值得单元测试” - 或者不。
  • 它有多大可能改变?如果您认为这种方法可能在未来的某个地方发生变化,那么添加单元测试可避免以后引入错误。
+0

但是您提出的示例单元测试(AudioClient:= CreateAudioClient; CheckNotNil(AudioClient);)将在运行Windows XP计算机时失败,并且如果您在Windows Vista及更高版本上运行则会失败。 – CodeSnake 2011-01-20 14:32:20

+0

@CodeSnake这是你需要写入测试的东西。 – 2011-01-20 15:51:30

0

这真的取决于你多长时间相信这种方法会发生变化......如果你已经有一些测试它的单位,那么是的,否则这是真的取决于你,但我是不确定从整个单元测试这个方法是个好主意,因为它调用了其他单元的其他方法。底线,它取决于你如何选择这样做,最好的方法是将测试添加到整个项目中,否则我不能在“部分测试”中看到任何值。

6

您面临的问题是如何测试它,而不是测试它。

这是许多COM调用的包装,可能由于许多不同的原因而失败。这些可能的COM故障条件是测试此例程最重要的方面。但是你不能轻易地引发COM例程失败。为了测试这些COM失败模式,你需要使用一个模拟,这是一个很大的飞跃。

3

当单元测试作为应用程序和第三方API(甚至是系统API)之间的接口的类时,您想测试该类调用并正确响应API。如果没有某种方式来检测传递给API的内容并返回适当的响应,则无法做到这一点。

在您的情况下,您正在进行一系列调用以获取IAudioClient。我会说你做得太多了。在一个函数中有一个以上的条件是一个条件太多(我想我只是把自己弄糊涂了)。我会把它分成几个功能,你可以单独测试。

function TCoreAudio.CreateAudioClient: IAudioClient; 
var 
    MMDeviceEnumerator: IMMDeviceEnumerator; 
    MMDevice: IMMDevice; 
    MixFormat: PWaveFormatEx; 
    AudioClient: IAudioClient; 
begin 
    Result := nil; 
    if IsVista then 
    try 
     MMDeviceEnumerator := GetMMDeviceEumerator; 
     MMDevice := GetMMDevice(MMDeviceEnumerator); 
     AudioClient := GetAudioClient(MMDevice); 
     MixFormat := GetMixFormat(AudioClient); 
     InitializeAudioClient(AudioClient, MixFormat); 
     Result := AudioClient; 
    except 
     //Handle exception 
    end; 
end; 

function TCoreAudio.IsVista: boolean; 
begin 
    Result := CheckWin32Version(6, 0); 
end; 

function TCoreAudio.GetMMDeviceEnumerator: IMMDeviceEnumerator; 
begin 
    HR := GetInstance().CoCreateInstance(CLSID_MMDeviceEnumerator, nil, CLSCTX_ALL, 
     IMMDeviceEnumerator, Result); 
    if Failed(HR) then 
     raise Exception.Create('Failed to create device enumerator'); 
end; 

function TCoreAudio.GetMMDevice(ADeviceEnumerator: IMMDeviceEnumerator): IMMDevice; 
begin 
    HR := MMDeviceEnumerator.GetDefaultAudioEndpoint(eRender, eConsole, Result); 
    if Failed(HR) then 
     raise Exception.Create('Failed to retrieve device'); 
end; 

function TCoreAudio.GetAudioClient(ADevice: IMMDevice): IAudioClient; 
begin 
    HR := MMDevice.Activate(IAudioClient, CLSCTX_ALL, nil, Result); 
    if Failed(HR) then 
     raise Exception.Create('Failed to retrieve audio client'); 
end; 

function TCoreAudio.GetMixFormat(AAudioClient: IAudioClient): PWaveFormatEx 
begin 
    HR := AudioClient.GetMixFormat(Result); 
    if Failed(HR) then 
     raise Exception.Create('Failed to retrieve mix format'); 
end; 

procedure TCoreAudio.InitializeAudioClient(AAudioClient: IAudioClient, AMixFormat: PWaveFormatEx); 
begin 
    HR := AudioClient.Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 0, 0, AMixFormat, nil); 
    CoTaskMemFree(MixFormat); 
    if Failed(HR) then 
     raise Exception.Create('Audio client failed to initialize'); 
end; 

现在你可以提供一个模拟/假/存根各项功能,确保API被调用合适的参数,并迫使故障情况,以确保您的生产代码是妥善处理它们。

您不需要问产品代码是否应该测试。答案总是肯定的。 (警告:无耻的自我推销)我最近写了关于我blog。有时候,即使是所有陈述中最无害的一点,即赋值语句,也无法按预期工作。

事实上,现在它已经分解成它看起来像一个新的创建类,只是渴望爆发。