有人能告诉我如何测试这种方法

时间:2015-11-12 18:53:26

标签: c# asp.net-mvc visual-studio unit-testing async-await

我是测试和.net的绝对初学者,我需要尽快为学校项目测试这种方法。

这是代码:

// GET: Books/Archive
public async Task<ActionResult> Archive()
{
    var usersBooks = await _bookManager.GetArchiveBooks(Guid.Parse(HttpContext.User.Identity.GetUserId()));

    var result = usersBooks.Select(ConvertArchiveBookToViewModel).ToList();

    return View(new ArchiveViewModel
        {
            Books = result
        });
}

任何答案都非常有用,谢谢你们:)

1 个答案:

答案 0 :(得分:3)

首先要做的事情......你需要模仿_bookManager作为这种方法的依赖。

_bookManager来自哪里?据推测,它是一个阶级财产。所以它应该提供一些使用模拟的方法。您应该可能使用构造函数注入,但如果您不熟悉在ASP.NET MVC中连接依赖项,那么它现在可能会变得有点过于复杂。注射性也应该起作用。像这样:

public class MyController
{
    private SomeType _bookManager;
    public SomeType BookManager
    {
        get { return _bookManager; }
        set { _bookManager = value; }
    }

    public async Task<ActionResult> Archive()
    {
        // your method
    }
}

据推测,该类中的其他地方也有代码,否则在使用之前初始化_bookManager。您将要稍微修改该逻辑,以便它不会覆盖任何提供的模拟。一种通常适合我的模式是使用属性本身甚至是类的内部属性,并在属性中进行惰性初始化。像这样:

public class MyController
{
    private SomeType _bookManager;
    public SomeType BookManager
    {
        get
        {
             if (_bookManager == null)
                 _bookManager = new SomeType();
             return _bookManager;
        }
        set { _bookManager = value; }
    }

    public async Task<ActionResult> Archive()
    {
        // IMPORTANT: Use "BookManager" instead of "_bookManager"
    }
}

这里的想法是,如果您为BookManager提供模拟(或任何依赖项实现),那么代码将使用它。否则它将使用您当前正在使用的任何内容。

现在您的类已设置为允许使用模拟,您需要创建一个模拟。有许多模拟库可用。我个人使用RhinoMocks。

模拟的目的是提供预期的,定义的行为。这是因为......

  

您正在测试Archive()。您测试BookManager.GetArchiveBooks()

使用您选择的模拟库,在您的测试中,您将设置SomeType的实例(或显然称为您的类型)以返回GetArchiveBooks()的已定义和预期结果。根据该结果,您可以预测您正在测试的方法的结果,并验证它是否产生了完全相同的结果。

从广义上讲,您的测试看起来像这样:

// arrange
var bookManager = MockRepository.GenerateMock<SomeType>();
// TODO: configure the object to return a known result from GetArchiveBooks()
var controller = new MyController();
controller.BookManager = bookManager;

// act
var result = await controller.Archive();

// assert
// TODO: inspect the result to ensure it contains what you expect

对于您选择的模拟库,请查看一些示例来设置&#34; stub&#34;对于被调用的方法(在本例中为GetArchiveBooks())。

为了检查结果,首先您要在调试器中逐步完成此测试并查看result实际拥有的内容。一个视图结果有很多属性,我不知道它们在我的头顶。但是如果你在调试器中检查它,你应该能够在其中一个属性中找到你的模型,以及你可能想要的其他东西。 (取决于您希望在此测试中断言的内容。)

此处的目标是确保返回的模型完全基于模拟依赖项的已知行为,您期望它是什么。如果是,则该方法通过测试。

编辑:我刚刚注意到方法中的第二个依赖项:

HttpContext.User.Identity.GetUserId()

现代ASP.NET MVC实现可能也提供了一些模拟HttpContext的有用方法,尽管我也不熟悉这一点。最糟糕的情况是你只是暴露另一个可注入的属性来模拟它。像这样:

private string _userID;
public string UserID
{
    get
    {
        if (string.IsNullOrWhiteSpace(_userID))
            _userID = HttpContext.User.Identity.GetUserId();
        return _userID;
    }
    set { _userID = value; }
}

然后在您的操作方法中,您将使用该属性而不是直接调用HttpContext。在你的测试中,作为&#34;安排&#34;的一部分。一步,你会提供一个模拟字符串。这很简单:

controller.UserID = "testUser";

正如您在此处所看到的,可测试性是所有关于依赖关系管理。每个可测试的代码片段都应该与任何依赖关系隔离,无论它们有多小。 (例如从HttpContext获取用户ID。)"Invert" those dependencies以允许代码提供信息,而不是让可测试代码负责获取信息。