在设置模拟到单元测试 WebAPI 帖子时遇到困难

本文关键字:遇到 WebAPI 设置 模拟 单元测试 | 更新日期: 2023-09-27 18:16:13

我正在尝试使用 MSTest 和 Moq 为将 json 从表单发布到数据库的实时系统设置单元测试。系统本身运行良好,但我的任务是尝试为它构建一些测试。我正在使用的视图中的 ajax 调用转到以下 HttpPost 方法之一的控制器:

[HttpPost]
public ActionResult Add(Request model)
{
    return ProcessRequest(model, UserAction.Create);
}

这导致了 WebAPI 控制器:

public int Post([FromBody]Request value)
    {
        try
        {
            var id = myRepository.AddRequest(value);
            foreach (var day in value.Days)
            {
                day.RequestId = id;
                myRepository.AddRequestDay(day);
            }
            return id;
        }
        catch
        {
            return -1;
        }
    }

在我的测试中,我认为使用TransactionScope是一个好主意,所以我实际上并没有在数据库中保存任何数据。如果有更好的方法,请启发我:

[TestMethod]
public void API_Request_Post()
{
    using (TransactionScope ts = new TransactionScope())
    {
        var jsonObject = //some json scraped from a test post
        var request = new Mock<HttpRequestBase>();
        //This is where I'm stuck. I can't find anything in Setup that lets me prep the Post body for when the controller gets to it.
        //request.Setup(x => x.InputStream).Returns(jsonObject);
        RequestController controller = new RequestController();
        //This is another point that I don't understand. I make the call for post happy with a reference to the model instead of the actual json?
        var result = controller.Post(new Models.Request() );
        Assert.IsTrue(result > -1);
    }
}

任何试图确定我需要将 json 提供给 HttpRequest 的哪一部分的帮助将不胜感激(帮助我理解帖子只是锦上添花(。

在设置模拟到单元测试 WebAPI 帖子时遇到困难

我希望我不是在告诉你一些你已经知道的事情,但看起来你可能在质疑从哪里开始?这是测试中最难的部分...

为了确保我们在同一页面上,知道要测试的内容的关键是描述方案,而对该方案进行单元测试的关键是隔离。

这意味着您希望隔离"被测试"类。

此外,如果您先编写测试,

然后再编写代码以使其通过,则代码更容易测试。您不是这种情况,因此这意味着您拥有的代码如果不更改它,可能无法测试。

最后,给定任何外部/第三方系统,除非您正在进行"探索性测试">,否则您不想测试第三方内容,即http发布/获取。相反,您希望测试代码及其性能。

假设你知道这一点或一切都有意义,那么,这部分也将是显而易见的。

Moq 或任何其他模拟框架旨在代表被测类与之协作的对象/服务,以便单独提供帮助。给定两个类,类 A 和类 B,其中类 A 作用于类 B,您希望在将类 B 提供给类 A 时伪造/模拟类 B,以便您可以断言/验证类 A 的行为是否符合您对给定场景的预期。乍一看这似乎很幼稚,但考虑到您也将对 ClassB 执行相同的操作,然后您有一套测试可以隔离它们正在测试的内容,这为您提供了很大的覆盖范围。

隔离的关键是注入,确保如果 ClassA 作用于 ClassB,你将 ClassB 传递给 ClassA 的构造函数,这样你就可以给它一个假的 B 类。

有些人不赞成更改代码以使其可测试,但我的论点是,如果您首先编写的代码是可测试的,那么您就不必更改它,因此请尝试重构而不是重新设计。

要测试的内容

因此,这意味着您将需要几个不同的场景,每个场景都有与您关心的内容隔离的每个测试。

良好测试的关键是弄清楚你想要测试什么,然后安排你的测试,以便清楚地知道你在做什么。

  • 测试类名不需要包含"Test";这是多余的。解释一下场景是什么;谁参与其中等。

  • 测试
  • 方法应该说明你关心测试的操作是什么;你处于什么状态,等等。

  • **
  • 在方法中**现在遵循"安排,行动,断言">(又名给定,何时,然后(方法:

  • 安排:在这里设置所有模拟或你需要的任何变量,包括你正在测试的一个类,比如你的真控制器,但假 myRepository 和假value

  • 行动:做实际动作,如Post()

  • 断言:证明你预期的行为发生了,比如当你给它一个四天的value时,那么你期望:

    • myRepository 被告知添加值
    • myRepository 被告知要添加四次
    • 一天

一个例子

由于我不完全确定测试的意图是什么,而且我不知道所有代码,我将举一个我认为会很好地相关的例子,并希望也能展示如何设置模拟(理想情况下,你会这样做!

此外,如果这真的是一个单元测试,你通常会争取每个断言/验证进行一次测试,这样你就不必调试测试,只需要调试失败的代码,但我在这里放了三个"简单"。

在此测试中,您将看到我:

  • 关心在开机自检中测试逻辑
  • 所以我创建了一个模拟存储库,仅用于验证它被调用,
  • 以及设置为在调用时做出适当响应的模拟请求,
  • 我将模拟的存储库传递给控制器的构造函数(通过注入隔离(
  • 然后我POST在实时控制器上与模拟协作者(存储库和请求(一起执行我关心的操作,
  • 然后我验证POST是否按预期执行/行为。

    [TestClass]
    public class GivenAValidRequestAndRepository(){
      [TestMethod]
      public void WhenWeReceiveAPostRequest(){
        //Arrange / Given
        var repository = new Mock<IRepository>();
        var request = new Mock<IRequest>();
        request.Setup ( rq => rq.ToString() )
               .Returns ( "This is valid json ;-)" );
        request.Setup ( rq => rq.Days )
               .Returns ( new List<IDay> {
                 "Monday",
                 "Tuesday",
                } );
       var controller = new RequestController( repository.Object );
    
       //Act / When
       int actual = controller.Post( request.Object );
    
       //Assert / Verify
       // - then we add the request to the repository
       repository.Verify( 
         repo => repo.AddRequest( request, Times.Once() );
       // - then we add the two days (from above setup) in the request to the repository
       repository.Verify( 
         repo => repo.AddRequestDays( It.IsAny<IDay>(), Times.Exactly( 2 ));
       // - then we receive a count indicating we successfully processed the request
       Assert.NotEqual( -1, actual );
      }
    }
    

关闭

你的目标不应该是让你的老板因为你写了测试而感到高兴。相反,努力进行有价值和富有表现力的测试,您将能够在未来保持。你不会让它变得完美(也不应该尝试(,只要确保你正在测试的东西增加价值。涵盖如果它们在代码中更改,您的测试将失败的内容,这表明您有错误。

我希望这有帮助,请回复评论/问题。