我正在测试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类获取有关数据库的连接字符串等的详细信息,以便将数据存储到..并且我想传入一个测试数据库而不是那里的生产数据库连接字符串。)
答案 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