【问题标题】:How to write Unit test for Controller method with two parameters and what scenarios are required to test?如何为具有两个参数的Controller方法编写单元测试以及需要测试哪些场景?
【发布时间】:2018-05-10 16:30:31
【问题描述】:

我是单元测试的新手。我想为删除控制器操作编写单元测试。我想使用 NSubstitute 来模拟所有依赖项。当前的实现使用 IRepository 接口来抽象出对底层数据源的调用。

控制器

public ActionResult Delete(string fileName, bool isPublic)
{
    try
    {
        repo.DeleteDocument(new PortalDocument
        {
            Path = fileName,
            IsPublic = isPublic
        });
    }
    catch (Exception e)
    {
        EventLog.Logger.LogCritical(e, e.Message);            
    }
    return RedirectToAction(IndexViewName);
}

存储库接口。

public interface IRepository<out T> where T: 
    CloudBlobContainer
{
    bool DeleteDocument(PortalDocument document);
}

PortalDocument 类

public class PortalDocument
{
    public bool IsPublic { get; set; }
}

提前致谢。

【问题讨论】:

  • 想想你期望action方法做什么。它应该在 repo 上调用 DeleteDocument,并且它应该返回一个重定向结果。测试它是否做了这两件事。您还可以进行另一个测试,其中您的模拟 repo 抛出异常,并确保它捕获并正确记录它,并返回重定向结果。
  • 嗨梅森,感谢您的评论一百万
  • 我已经尝试过其中一项测试来返回重定向的结果并且它正在工作,但是您能否通过一个示例向我展示如何测试此场景“抛出异常,并确保它捕获它并正确记录”。如果可能的话。

标签: c# asp.net-mvc xunit nsubstitute


【解决方案1】:

我认为您已经找到了快乐的路径场景,即测试没有例外时会发生什么。但是现在你需要测试不愉快的路径,当抛出异常时它的行为是否正常。

//arrange
var mockRepo = new Mock<DocumentRepository>(MockBehavior.Strict);
mockRepo.Setup(r => r.DeleteDocument(It.IsAny<PortalDocument>())).Throws(new Exception("test"));

var controller = new DocumentController(mockRepo.Object);

//act
var result = controller.Delete("filename", true);


//assert
//make sure result is a redirect result
//but how do I test that it got logged?

不幸的是,您会发现您实际上无法测试您记录的内容。为什么?因为你有一个静态方法调用。静态方法调用是不可测试的。相反,您应该关注dependency inversion principle

解决此问题的一种方法是使用可注入的东西来包装您的日志记录调用。

public interface ILogger
{
    void LogCritical(Exception exception);
}

public class EventLogLogger : ILogger
{
    public void LogCritical(Exception exception)
    {
        EventLog.Logger.LogCritical(exception, exception.Message); 
    }
}

那么你的单元测试就变成了:

//arrange
var mockRepo = new Mock<IDocumentRepository>(MockBehavior.Strict);
mockRepo.Setup(r => r.DeleteDocument(It.IsAny<PortalDocument>())).Throws(new Exception("test"));

var mockLogger = new Mock<ILogger>(MockBehavior.Strict);
mockLogger.Setup(l => l.LogCritical(It.IsAny<Exception>())).Verifiable;

var controller = new DocumentController(mockRepo.Object, mockLogger.Object);

//act
var result = controller.Delete("filename", true);


//assert
//make sure result is a redirect result
mockLogger.Verify();

注意,我在示例中使用Moq library 语法。您需要根据自己的模拟框架进行调整。

如果您不熟悉依赖注入,我强烈建议您观看Deep Dive Into Dependency Injection and Writing Quality Decoupled Code by Miguel Castro

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-25
    • 1970-01-01
    • 2010-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多