.NET中的依赖注入,用于未测试的类

时间:2011-07-24 15:56:35

标签: c# unit-testing dependency-injection nunit

我正在测试StoreManager类的功能,该类依赖于DataBaseConfiguration类。

public class StoreManager {
  private DataBaseConfiguration dbConfig;
  public void Store(string name) {
    dbConfig.Store(name);
  }
  //other methods here
}

StoreManager类存储到数据库,我可以测试此方法是否正常工作的唯一方法是从数据库进行查询。我有另一个班级正在制作......

public class QueryManager {
private DataBaseConfiguration dbConfig;
public string Query(QueryExpression expr) {
    //query logic
    string name = "somename";
    return name;
}}

尽管我只关心测试我的StoreManager类,但我觉得我需要使用QueryManager类来测试存储值。 所以我有一个像这样的基本测试用例...

[TestFixture]
public class StoreManagerTest {
[TestFixtureSetup]
public void Setup() {
    DatabaseConfiguration dbConfig = new DatabaseConfiguration(/*test database details*/);
    StoreManager sm = new StoreManager(dbConfig);
    QueryManager qm = new QueryManager(dbConfig);
}

[Test]
public void TestStore_ValidStore() {
    sm.Store("testname");
    string queryResult = qm.Query(new QueryExpression("query_expr"));
    Assert.AreSame(queryResult, "testname");
}}

如您所见,除了ClassUnderTest(StoreManager)之外,QueryManager类还依赖于DatabaseConfig。

我在StoreManager类中没有很多逻辑,它只是委托DataBaseConfig类来存储(实际上存在更多涉及存储的类,而不是实际存储数据的DataBaseConfig ..但是为了简单起见,我们这样说..)

我想知道是否有更好的方法来处理此测试而根本不涉及QueryManager? 还有一种更好的方法将依赖于DataBaseConfiguration注入到StoreManager类中(考虑到DataBaseConfiguration类获取有关数据库的连接字符串等的详细信息,以便将数据存储到..并且我想传入一个测试数据库而不是那里的生产数据库连接字符串。)

1 个答案:

答案 0 :(得分:5)

要在测试中使用依赖项,最常见的方法是使用手写存根或模拟框架(即Moq或RhinoMocks)。

此外,您必须允许StoreManager类的用户传入DataBaseConfiguration依赖项,否则您无法在不更改代码的情况下将其删除。你现在正在做的构造函数注入是常见的做法,也是一种干净的方法(如果你有很多依赖项时使用IOC容器会变得更方便),另见here

如果我理解正确,您只想测试StoreManager实际存储您传递给它的值,您对StoreManager的行为及其与其依赖项的交互感兴趣{ {1}} - 现在您可以通过查询数据存储本身来进行验证。

考虑到让我们使用RhinoMocks进行一个简单的例子 - 我唯一改变的是将DataBaseConfiguration类中的Store方法定义为虚拟,以便RhinoMocks可以覆盖它。

DataBaseConfiguration

此测试验证是否在//Arrange string storeValue = "testname"; var dbConfigMock = MockRepository.GenerateMock<DataBaseConfiguration>(); dbConfigMock.Expect(x => x.Store(storeValue)); StoreManager sm = new StoreManager(dbConfigMock); //Act sm.Store(storeValue); //Assert dbConfigMock.AssertWasCalled(x => x.Store(storeValue)); 类上调用了Store方法,没有任何其他依赖项 - 它测试DataBaseConfiguration类的行为。此测试不会触及数据库,也不会影响任何其他类。

修改

我不确定我是否理解在生产代码中使用模拟框架的问题 - 模拟框架仅用于您的测试项目,不需要对其进行引用或需要对代码进行任何更改生产代码本身。

使用手写存根,您可以“手动”执行相同的断言:定义一个测试存根,存储多少次以及调用StoreManager的值{同样这需要Store()方法声明为虚拟,因此可以覆盖它:)

Store

现在在测试中使用此测试存根而不是“真实”public class DataBaseConfigurationTest : DataBaseConfiguration { public int TimesCalled { get; set; } public string LastNameStored { get; set; } public DataBaseConfigurationTest() { TimesCalled = 0; } public override void Store(string name) { TimesCalled++; LastNameStored = name; } }

DataBaseConfiguration