【问题标题】:c# unit test child method calls parametersc#单元测试子方法调用参数
【发布时间】:2019-08-07 16:41:51
【问题描述】:

这是我的c# 代码。

我不确定我应该如何验证 Get 方法是否使用正确的参数调用。

public class ClassToTest
{    
    public IList<Products> GetProducts(string categoryId)
    {
        var items = _service.Get(new Category { id = categoryId, flag = true });
        return items;
    }
}

例如它是用 Category 类对象调用的,参数 flag 作为 trueid 作为传递。

【问题讨论】:

  • 如果service.Get 不使用外部资源,不要模拟它,设置正确的数据并检查提供的类别的产品并返回标志。请注意,此时使用内存数据库,您不需要模拟存储库,只需模拟数据提供者。

标签: c# unit-testing testing nunit tdd


【解决方案1】:

有了FakeItEasy和依赖倒置原则,就这么简单:

你的班级略有变化:

public class ClassToTest
{
    private readonly IService _service;

    public ClassToTest(IService service) =>
        _service = service;

    public IList<Products> GetProducts(string categoryId) =>
        _service.Get(new Category { id = categoryId, flag = true });
}

public interface IService
{
    IList<Products> Get(Category category);
}

测试:

[Test]
public void Test()
{
    // Arrange
    string categoryId = "categoryId";
    IService service = A.Fake<IService>();
    ClassToTest sut = new ClassToTest(service);

    // Act
    sut.GetProducts(categoryId);

    // Assert
    A.CallTo(() => service.Get(A<Category>.That.Matches(i => i.id == categoryId && i.flag)))
     .MustHaveHappened();
}

[Test]
public void Test()
{
    // Arrange
    string actualId = null;
    bool? actualFlag = null;

    string categoryId = "categoryId";
    IService service = A.Fake<IService>();
    A.CallTo(() => service.Get(A<Category>.Ignored)).Invokes((Category category) =>
      {
          actualId = category.id;
          actualFlag = category.flag;
      });

    ClassToTest sut = new ClassToTest(service);

    // Act
    sut.GetProducts(categoryId);

    // Assert
    Assert.AreEqual(categoryId, actualId);
    Assert.AreEqual(true, actualFlag);
    // optionaly, because it obviously happened
    A.CallTo(() => service.Get(A<Category>.Ignored))
                          .MustHaveHappened();
}

【讨论】:

    【解决方案2】:

    对不起,我不会做cmets,所以我必须写一个答案。希望我理解正确,你想做什么。

    “新”使您的代码难以测试。您应该使用依赖倒置原则并从外部提供 Category 对象。你不应该在你的方法中创建一个“新”。然后,您将与您的 Category 对象实现绑定在一起。您应该始终针对接口而不是具体实现进行编程。

    如果您将 Category 对象作为方法的参数,您可以轻松地测试您的方法。这是示例。我没有使用 Category 类的接口。您还可以例如 IFactory 接口为您提供 Category 对象的具体实现。它使测试更容易。 在示例中使用NSubstitute

    public IList<Person> GetProducts(Category category)
    {
        var items = expressionClass.GetCategory(category);
        return items;
    }
    
    [TestMethod]
    public void Test2()
    {
        var expressionClass = Substitute.For<IExpresionClass>();
        var testClass = new TestClass(expressionClass);
        var category = new Category { flag = true, id = "55" };
        var list = testClass.GetProducts(category);
    
        expressionClass.Received().GetCategory(Arg.Is(category));
    }
    

    【讨论】:

      【解决方案3】:

      其他答案是正确的,依赖倒置原则将帮助您测试此代码。我将通过在您的测试类中模拟 _service 来测试此代码。您可以使用适当的模拟框架或通过提供服务接口的模拟实现来做到这一点。这是两个选项的工作示例。

      被测代码

      这是我们正在尝试测试的代码。有几点需要注意:

      • 我们通过将服务传递给ClassToTest 构造函数来应用依赖倒置原则。
      • 我在Category 类中实现了一个Equals 方法。这样可以更轻松地测试此代码。
      public class ClassToTest
      {
          private readonly IService _service;
      
          public ClassToTest(IService service)
          {
              _service = service;
          }
      
          public IList<Products> GetProducts(string categoryId)
          {
              var items = _service.Get(new Category { id = categoryId, flag = true });
              return items;
          }
      }
      
      public interface IService
      {
          IList<Products> Get(Category category);
      }
      
      public class Category
      {
          public string id;
          public bool flag;
      
          public override bool Equals(object obj)
          {
              Category c = obj as Category;
      
              if (c == null)
              {
                  return false;
              }
      
              return c.id == id && c.flag == flag;
          }
      }
      
      public class Products
      {
      }
      

      使用起订量进行测试

      我首选的模拟框架是 Moq,但我相信其他框架也适用于这个用例。这是使用 Moq 的测试:

      [Test]
      public void MoqTest()
      {
          // Arrange.
          var mockService = new Mock<IService>();
          var target = new ClassToTest(mockService.Object);
      
          // Act.
          target.GetProducts("test");
      
          // Assert.
          var expectedCategory = new Category {id = "test", flag = true};
          mockService.Verify(t => t.Get(expectedCategory));
      }
      

      没有模拟框架的测试

      如果您不想,则无需使用适当的模拟框架。您可以提供自己的模拟实现:

      public class MockService : IService
      {
          public Category CategoryReceived { get; private set; }
      
          public IList<Products> Get(Category category)
          {
              CategoryReceived = category;
              return new List<Products>();
          }
      }
      

      然后像这样测试你的代码:

      [Test]
      public void PoorMansTest()
      {
          // Arrange.
          var mockService = new MockService();
          var target = new ClassToTest(mockService);
      
          // Act.
          target.GetProducts("test");
      
          // Assert.
          var expectedCategory = new Category { id = "test", flag = true };
          Assert.That(mockService.CategoryReceived, Is.EqualTo(expectedCategory));
      }
      

      【讨论】:

        猜你喜欢
        • 2011-03-10
        • 2023-03-06
        • 1970-01-01
        • 1970-01-01
        • 2012-09-10
        • 1970-01-01
        • 2014-11-08
        • 2015-05-31
        • 1970-01-01
        相关资源
        最近更新 更多