3

我有一个方法可以接收一个对象并将其保存到数据库中。但是,在我保存对象之前,我执行以下操作...

(伪代码)

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(...);

该代码工作正常,但我不确定如何编写两个单元测试以获得成功。a) 用户使用有效数据进行身份验证 b) 用户未使用有效数据进行身份验证。

我不确定该怎么做的原因是,两种情况都返回相同的 RedirectToAction(..) 视图对象。所以我可以成功测试..但它没有告诉我保存的对象是否包含经过身份验证的用户 ID 或匿名信息。就像我想要第一个单元测试说

  • 起订量为经过身份验证的用户
  • 调用方法
  • 测试结果是否为 RedirectToActionView
  • 测试持久化的 foo 对象是否包含 moq 的用户 ID。

想法?

更新

常见的建议是我模拟 fooService。我目前正在使用依赖注入和起订量,所以有人可以告诉我如何使用起订量吗?不过,我不确定DI在这里有多重要???

4

6 回答 6

3

我会模拟该_fooService对象,并测试它作为测试的一部分接收到的内容。这样,您的周围代码保持不变并且不受影响,并且通过检查_fooService接收到的内容,您可以断言行为是否符合预期。在这种情况下,返回对象不感兴趣。

你怎么嘲笑你的_fooService?您可以实现自己的“测试”版本(遵循与真实世界版本相同的接口),或使用模拟框架。无论您使用哪种方法,您的上面的代码都需要配置给定的实现_fooService(通常在构造时 - 请参阅依赖注入以获取有关其工作原理的更多信息)

于 2009-04-07T13:13:58.900 回答
1

您可以模拟 _fooService.Save(foo) 并检查提供的 foo。

于 2009-04-07T13:15:24.280 回答
1

也许您发现很难测试对象,因为您在一个方法中发生了多个活动。

这里的总体主题是控制器逻辑。

  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 项服务的 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);

}
于 2009-04-09T20:31:17.123 回答
0

您想使用 DI 将 fooservice 正确实现到您的对象中,因此在测试时您可以这样做;

(使用最小起订量)

[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 的任何服务的模拟版本

高温高压

于 2009-04-08T11:48:27.773 回答
0

您仍然可以引用您的对象。在您的单元测试结束时,您不能只断言 foo.AnonEmail 或 UserId 的值是什么吗?

单元测试不应该触及外部源,(这是一个集成测试)所以如果你走这条路,你应该模拟你的数据源,然后通过你的模拟进行测试。

于 2009-04-07T13:15:28.353 回答
0

您可以在测试中访问 foo 吗?我假设这是课堂上的一个领域。有公共吸气剂吗?如果不是,您可能必须使用反射(我假设它在 asp.net 中可用,但我不太熟悉它)

于 2009-04-07T13:15:44.560 回答