Как мне провести модульное тестирование этой бизнес-логики? - PullRequest
3 голосов
/ 07 апреля 2009

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

(код псевдо)

if (IsAuthenticated)
{
   foo.UserId = AuthenticatedUser.Id;
}
else
{
   foo.AnonEmail = "Jon@World-Domination";
   foo.AnonName = "Jon Skeet";
}

try
{
    _fooService.Save(foo);
}
catch
{
    // Some view, with error stuff now added to 
    return View(...); ViewData.ModelState.
}

// all good, redirect to the proper next view.
return RedirectToAction(...);

Этот код работает нормально, но я не уверен, как написать два модульных теста для успеха. а) Пользователь аутентифицирован с действительными данными б) Пользователь не аутентифицирован с действительными данными.

Причина, по которой я не уверен, что делать, заключается в том, что оба сценария возвращают один и тот же объект представления RedirectToAction (..). Так что я могу успешно это проверить ... но он не говорит мне, содержит ли сохраненный объект аутентифицированный идентификатор пользователя или информацию anon. Как будто я хочу, чтобы первый юнит-тест сказал

  • moq up аутентифицированный пользователь
  • метод вызова
  • проверка, если результатом является RedirectToActionView
  • проверить, содержит ли сохраненный объект foo идентификатор пользователя moq'd.

мысли

Обновление

Распространенным предложением является издевательство над fooService. В настоящее время я использую Dependency Injection и Moq, так может ли кто-нибудь показать мне, как я буду использовать Moq? Я не уверен, насколько важен здесь DI ???

Ответы [ 6 ]

3 голосов
/ 07 апреля 2009

Я бы смоделировал объект _fooService и проверил бы, что он получает, как часть вашего теста. Таким образом, ваш окружающий код остается прежним и не тронутым, и проверяя, что получает _fooService, вы можете установить, соответствует ли поведение ожидаемому. В этом случае возвращаемый объект не представляет интереса.

Как ты смеешься над своим _fooService? Вы можете реализовать свою собственную «тестовую» версию (придерживаясь того же интерфейса, что и реальная версия), или использовать фальшивый фреймворк. Какой бы подход вы ни использовали, ваш приведенный выше код должен быть сконфигурирован с заданной реализацией _fooService (обычно в процессе строительства - см. внедрение зависимостей для получения дополнительной информации о том, как это может работать)

1 голос
/ 10 апреля 2009

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

Общая тема здесь - логика контроллера.

  1. Украсить объект домена информацией о пользователе
  2. Сохранить логику обновления
  3. Определение следующего представления для рендеринга на основе успеха / неудачи

Если вы извлекаете другой объект (IUserDecoratorService), тогда ваш код выглядит как

userService.UpdateUserInformation(foo);

try
{
    _fooService.Save(foo);
}
catch
{
    // Some view, with error stuff now added to 
    return View(...); ViewData.ModelState.
}

// all good, redirect to the proper next view.
return RedirectToAction(...);

Этот метод прост в тестировании, так как это 2 простых взаимодействия с двумя сервисами и решение о маршрутизации, которое вы уже можете проверить.

Теперь вам просто нужно написать тесты для вашего нового сервиса:

[Test]
public void ShouldDecorateWithUserIdForAuthenticatedUser()
{
    {setup authenticated user}
    :
    service.UpdateUserInformation(foo);

    Assert.AreEqual(expectedId, foo.UserId);
    Assert.IsNull(foo.AnonEmail);
    Assert.IsNull(foo.AnonEName);

}

[Test]
public void ShouldSpoofTheAllKnowingSkeetIfAnonymousUser()
{
    {setup anonymous user}
    :
    service.UpdateUserInformation(foo);

    Assert.AreEqual(UnassignedId, foo.UserId);
    Assert.AreEqual("Jon@World-Domination", foo.AnonEmail);
    Assert.AreEqual("Jon Skeet", foo.AnonName);

}
1 голос
/ 07 апреля 2009

Вы можете издеваться над _fooService.Save (foo) и проверять прилагаемый foo.

0 голосов
/ 08 апреля 2009

Вы хотите использовать DI для правильной реализации fooservice в вашем объекте, поэтому во время тестирования вы можете сделать это;

(с использованием Moq)

[TestMethod]
public void Jon_Skeet_Is_Saved_If_User_Not_Authenticated()
{
  bool jonWasSaved = false;
  var mockFooService = new Mock<IFooService>();
  mockFooService
       .Expect(x => x.Save(It.Is<Foo>(foo => foo.AnonName == "Jon Skeet")))
       .Callback(() => jonWasSaved = true;);

  FooManager instance = new FooManager(mockFooService.Object);
  Foo testFoo = new Foo();
  testFoo.UserId = 1; // this is not the authenticated id

  instance.baa(foo);

  Assert.IsTrue(jonWasSaved);
}

Возможно, вы также захотите передать имитационную версию любого сервиса, который вы используете для проверки AuthetnicatedUser.Id

НТН

0 голосов
/ 07 апреля 2009

Есть ли у вас доступ к foo в тесте? Я предполагаю, что это поле в классе. Есть ли публичный добытчик? Если нет, возможно, вам придется использовать Reflection (я полагаю, что это доступно на asp.net, но я не слишком знаком с ним)

0 голосов
/ 07 апреля 2009

у вас все еще есть ссылка на ваш объект. В конце вашего модульного теста, не могли бы вы просто утверждать, что значение было foo.AnonEmail или UserId?

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

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