在.NET Windows应用程序中,我有一个名为EmployeeManager的类。在实例化时,该类将雇员从尚未完成注册的数据库加载到列表中。我想在单元测试中使用employeemanager。但是,我不想涉及数据库。
根据我对这个场景的理解,我需要一个iemployeemanager接口,它只用于测试目的。这似乎不对,因为接口没有其他用途。但是,它将允许我创建一些employeemanager测试类,该类加载雇员而不涉及数据库。这样,我就可以指定其他来自数据库的值。
上面说的对吗?我需要嘲笑它吗?mocking(moq framework)似乎使用了很多代码来完成一些简单的事情,比如分配一个属性。我不明白重点。当我能从iBeaMeMaNeGER创建一个简单的测试类时,为什么要嘲笑我,这将提供我所需要的?

最佳答案

根据我对这个场景的理解,我需要一个iemployeemanager接口,它只用于测试目的。这似乎不对,因为接口没有其他用途。
很值得创建这个界面。还要注意,接口实际上有多种用途:
接口标识参与者提供的角色或职责。在这种情况下,接口标识EmployeeManager的角色和职责。通过使用一个接口,您可以防止对特定于数据库的内容的意外依赖。
界面减少了耦合。因为您的应用程序不依赖于EmployeeManager,所以您可以自由地交换它的实现,而无需重新编译应用程序的其余部分。当然,这取决于项目结构、程序集数量等,但它仍然允许这种类型的重用。
接口提高了可测试性。当您使用一个接口时,生成允许您的软件更容易测试的动态代理变得容易得多。
界面的作用力。好吧,我已经暗示过了,但值得再说一次。仅仅使用一个接口就应该让您考虑对象的角色和职责。接口不应该是厨房的水槽。接口表示角色和职责的内聚集。如果一个接口的方法没有内聚性,或者几乎不总是一起使用,那么一个对象很可能有多个角色。虽然不一定是坏的,但它意味着多个不同的接口更好。接口越大,就越难使其协变或反变,因此在代码中更具可塑性。
但是,它将允许我创建一些employeemanager测试类,该类在不涉及数据库的情况下加载employees….我不明白重点。当我能从iBeaMeMaNeGER创建一个简单的测试类时,为什么要嘲笑我,这将提供我所需要的?
正如one poster所指出的,听起来像是在创建存根测试类。模拟框架可以用来创建存根,但是它们的一个最重要的特征是允许测试行为而不是状态。现在让我们看一些例子。假设如下:

interface IEmployeeManager {
    void AddEmployee(ProspectiveEmployee e);
    void RemoveEmployee(Employee e);
}

class HiringOfficer {
    private readonly IEmployeeManager manager
    public HiringOfficer(IEmployeeManager manager) {
        this.manager = manager;
    }
    public void HireProspect(ProspectiveEmployee e) {
        manager.AddEmployee(e);
    }
}

当我们测试HiringOfficerHireEmployee行为时,我们感兴趣的是验证他是否正确地与员工经理沟通,是否将此透视图员工添加为员工。你经常会看到这样的东西:
// you have an interface IEmployeeManager and a stub class
// called TestableEmployeeManager that implements IEmployeeManager
// that is pre-populated with test data
[Test]
public void HiringOfficerAddsProspectiveEmployeeToDatabase() {
    var manager = new TestableEmployeeManager(); // Arrange
    var officer = new HiringOfficer(manager); // BTW: poor example of real-world DI
    var prospect = CreateProspect();
    Assert.AreEqual(4, manager.EmployeeCount());

    officer.HireProspect(prospect); // Act

    Assert.AreEqual(5, manager.EmployeeCount()); // Assert
    Assert.AreEqual("John", manager.Employees[4].FirstName);
    Assert.AreEqual("Doe", manager.Employees[4].LastName);
    //...
}

以上测试是合理的…但不好。这是一个基于州的测试。也就是说,它通过检查某个操作前后的状态来验证行为。有时这是测试事物的唯一方法;有时这是测试事物的最好方法。
但是,测试行为通常更好,这就是模拟框架的亮点:
// using Moq for mocking
[Test]
public void HiringOfficerCommunicatesAdditionOfNewEmployee() {
    var mockEmployeeManager = new Mock<EmployeeManager>(); // Arrange
    var officer = new HiringOfficer(mockEmployeeManager.Object);
    var prospect = CreateProspect();

    officer.HireProspect(prospect); // Act

    mockEmployeeManager.Verify(m => m.AddEmployee(prospect), Times.Once); // Assert
}

在上面我们测试了唯一真正重要的东西——招聘官告诉员工经理需要增加一个新员工(一次,而且只有一次……不过,在这种情况下,我其实不会费心检查计数。不仅如此,我还证实了我要求招聘经理雇佣的雇员是由雇员经理加入的。我已经测试了批判行为。我甚至不需要一个简单的测试存根。我的测验较短。实际行为更加明显——可以看到交互和验证对象之间的交互。
可以让存根测试类记录交互,但随后您将模拟模拟框架。如果要测试行为,请使用模拟框架。
正如另一张海报所提到的,依赖注入(di)和控制反转(ioc)非常重要。我上面的例子并不是一个很好的例子,但是两者都应该被仔细考虑和明智地使用。在writingsubject上有很多available
1-是的,思考仍然是可选的,但我强烈推荐;)。

关于c# - 我该如何 mock ?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7030667/

10-11 10:49