在.net中为未测试的类注入依赖项
本文关键字:注入 依赖 测试 net | 更新日期: 2023-09-27 18:03:25
我正在测试一个依赖于DataBaseConfiguration类的StoreManager类的功能。
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类来测试storedvalues。我有一个像这样的基本测试用例。
[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类获取有关数据库的连接字符串等的详细信息,以便将数据存储到其中)。我想在一个测试数据库,而不是生产数据库连接字符串那里)。
要在测试中排除依赖关系,最常见的方法是使用手写存根或mock框架(即Moq或RhinoMocks)。
另外,您必须允许StoreManager
类的用户传递DataBaseConfiguration
依赖项,否则您无法在不更改代码的情况下将其存根。你现在所做的构造函数注入是一种常见的做法,也是一种干净的方法(如果你在有很多依赖的情况下使用IOC容器,这会变得更方便),也见这里。
如果我理解正确的话,您只是想测试StoreManager
实际上存储了您传递给它的值,您对StoreManager
的行为及其与其依赖性DataBaseConfiguration
的相互作用感兴趣-现在您通过查询数据存储本身来进行验证。
考虑到这一点,让我们通过一个使用RhinoMocks的简单示例-我唯一改变的是将DataBaseConfiguration
类中的Store
方法定义为虚拟方法,以便RhinoMocks可以覆盖它。
//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));
此测试验证在没有任何其他依赖项的DataBaseConfiguration
类上调用了Store方法—它测试StoreManager
类的行为。该测试不涉及DB,也不影响任何其他类。
我不确定我是否理解在生产代码中使用mock框架的问题——mock框架仅在您的测试项目中使用,不需要引用它或对生产代码本身进行任何代码更改。
使用手写的存根,你可以"手动"做同样的断言:定义一个测试存根,存储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
:
string storeValue = "testname";
DataBaseConfigurationTest dbConfigStub = new DataBaseConfigurationTest();
StoreManager sm = new StoreManager(dbConfigStub);
sm.Store(storeValue);
Assert.AreEqual(1, dbConfigStub.TimesCalled);
Assert.AreEqual(storeValue, dbConfigStub.LastNameStored);