Стоит ли использовать приведенный ниже код для модульного тестирования? - PullRequest
1 голос
/ 20 января 2011

Я новичок в модульном тестировании.И я не знаю, стоит ли тестировать код ниже.Вот пример метода, написанного на Delphi:

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;

Стоит ли использовать этот метод для модульного тестирования?Если да, какие его части нужно проверить?

Спасибо.

Ответы [ 4 ]

6 голосов
/ 20 января 2011

Проблема, с которой вы сталкиваетесь, заключается в том, как проверить ее, а не в том, следует ли ее проверять.

Это оболочка для ряда COM-вызовов, которые могут не работать по разным причинам. Эти возможные условия отказа COM являются наиболее важными аспектами для проверки этой процедуры. Но вы не можете легко спровоцировать неудачу в процедурах COM. Чтобы протестировать эти режимы сбоя COM, вам нужно использовать макет, и это довольно далеко от того места, где вы находитесь.

3 голосов
/ 21 января 2011

При модульном тестировании класса, который действует как интерфейс между вашим приложением и сторонним 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 вызывается с соответствующими аргументами, и заставляя условия сбояубедитесь, что ваш производственный код обрабатывает их правильно.

Вам не нужно спрашивать, должен ли производственный код проверяться.Ответ всегда да.(предупреждение: бесстыдная самореклама) Я недавно писал об этом в своем блоге .Иногда даже самый безобидный из всех операторов, оператор присваивания, работает не так, как ожидалось.

На самом деле теперь, когда он разбит, он начинает выглядеть как новый творческий класс, просто жаждущий вырваться.

3 голосов
/ 20 января 2011

Модульное тестирование - это обычно восходящий подход. Таким образом, вы бы начали модульное тестирование классов, которые используются в вашей функции. Убедившись, что все эти классы охвачены модульными тестами, вы можете создать модульный тест для своей функции CreateAudioClient. Модульный тест для этой функции, вероятно, очень прост, примерно так:

AudioClient := CreateAudioClient;
CheckNotNil (AudioClient);

Обратите внимание, что обычно вы проводите модульное тестирование интерфейса класса, а не тела функции или процедуры.

Надеюсь, это поможет.

Вопрос, стоит ли он того, зависит от ряда факторов:

  • Насколько легко построить для него юнит-тест? Сколько бы это было усилий?
  • Насколько важна эта часть? В зависимости от того, насколько важна эта часть, ответ может быть всегда «да, это определенно стоит модульного тестирования» - или нет.
  • Насколько вероятно, что это изменится? Если вы думаете, что этот метод может измениться где-то в будущем, то добавление модульных тестов позволит избежать ошибок позже.
0 голосов
/ 20 января 2011

это действительно зависит от того, как часто вы полагаете, что в этот метод будут внесены изменения ... если у вас уже есть какие-то тесты для этого устройства, тогда да, сделайте это, иначе это действительно ваше дело, но я не уверен, что этоХорошая идея - тестировать только этот метод из всего модуля, потому что он вызывает другие методы из других модулей.

В итоге, вам решать, как вы решите это сделать, лучше всего добавить тестирование ввесь проект, иначе я не вижу никакого значения в «частичном тестировании».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...