我如何对这种业务逻辑进行单元测试?

时间:2009-04-07 13:07:20

标签: asp.net-mvc unit-testing moq

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

(伪代码)

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或匿名信息。这就像我想要第一个单元测试说

  • moq up a authenticated user
  • 通话方法
  • 测试结果是否为RedirectToActionView
  • 测试持久化的foo对象是否包含moq'd用户ID。

想法?

更新

常见的建议是我嘲笑fooService。我目前正在使用Dependency Injection和Moq,所以可以告诉我如何使用Moq?我不确定DI在这里是多么重要,不过???

6 个答案:

答案 0 :(得分:3)

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

你如何嘲笑你的_fooService?您可以实现自己的“测试”版本(遵循与真实版本相同的界面),也可以使用模拟框架。无论您使用哪种方法,上面的代码都需要使用_fooService的给定实现进行配置(通常在构建时 - 请参阅dependency injection以获取有关此方法的更多信息)

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

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

  1. 使用用户信息装饰域对象
  2. 坚持更新逻辑
  3. 根据成功/失败确定要渲染的下一个视图
  4. 如果你提取另一个对象(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);
    
    }
    

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

您是否可以在测试中访问foo?我假设它是班上的一个领域。是否有公共吸气剂?如果不是,你可能不得不使用Reflection(我假设在asp.net中可用,但我不太熟悉它)

答案 5 :(得分:0)

您希望使用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的任何服务的模拟版本

HTH