我的任务是从第三方供应商的SQLite数据表中提取所有行,从这些记录创建业务对象,并将新业务对象发送到另一个类。
的伪代码:
var databasePath = "%user profile%\application data\some3rdPartyVendor\vendor.sqlite"
var connection = OpenSqliteConnection(databasePath);
var allGizmoRecords = connection.Query(...);
var businessObjects = TransformIntoBizObjs(allGizmoRecords);
someOtherClass.HandleNewBizObjs(businessObjects);
我已经完成了所有工作。
我的问题是:如何编写此课程,以便它可以进行单元测试?
我应该:
还是有更好的想法?我正在使用C#,但这个问题似乎与语言无关。
答案 0 :(得分:2)
你可以非常轻松地注入一个只测试的Sqlite数据库,重构代码如下所示。但你如何断言结果?业务对象将传递给someOtherClass
。如果您注入ISomeOtherClass
,那么该类的操作也需要可见。这似乎有点痛苦。
public class KillerApp
{
private String databasePath;
private ISomeOtherClass someOtherClass;
public KillerApp(String databasePath, ISomeOtherClass someOtherClass)
{
this.databasePath = databasePath;
this.someOtherClass = someOtherClass;
}
public void DoThatThing()
{
var connection = OpenSqliteConnection(databasePath);
var allGizmoRecords = connection.Query(...);
var businessObjects = TransformIntoBizObjs(allGizmoRecords);
someOtherClass.HandleNewBizObjs(businessObjects);
}
}
[TestClass]
public class When_Doing_That_Thing
{
private const String DatabasePath = /* test path */;
private ISomeOtherClass someOtherClass = new SomeOtherClass();
private KillerApp app;
[TestInitialize]
public void TestInitialize()
{
app = new KillerApp(DatabasePath, someOtherClass);
}
[TestMethod]
public void Should_convert_all_gizmo_records_to_busn_objects()
{
app.DoThatThing();
Assert.AreEqual(someOtherClass.Results, /* however you're confirming */);
}
}
使用IRepository
会删除此类中的一些代码,允许您模拟IRepository
实现,或伪造一个仅用于测试。
public class KillerApp
{
private IRepository<BusinessObject> repository;
private ISomeOtherClass someOtherClass;
public KillerApp(IRepository<BusinessObject> repository, ISomeOtherClass someOtherClass)
{
this.repository = repository;
this.someOtherClass = someOtherClass;
}
public void DoThatThing()
{
BusinessObject[] entities = repository.FindAll();
someOtherClass.HandleNewBizObjs(entities);
}
}
[TestClass]
public class When_Doing_That_Thing
{
private const String DatabasePath = /* test path */;
private IRepository<BusinessObject> repository;
private ISomeOtherClass someOtherClass = new SomeOtherClass();
private KillerApp app;
[TestInitialize]
public void TestInitialize()
{
repository = new BusinessObjectRepository(DatabasePath);
app = new KillerApp(repository, someOtherClass);
}
[TestMethod]
public void Should_convert_all_gizmo_records_to_busn_objects()
{
app.DoThatThing();
Assert.AreEqual(someOtherClass.Results, /* however you're confirming */);
}
}
但这仍然感觉非常麻烦。有两个原因,1)Repository模式最近来自getting some bad press Ayende,他知道关于Repository的一两件事。 2)你在做什么编写自己的数据访问!?使用NHibernate和ActiveRecord!
[ActiveRecord] /* You define your database schema on the object using attributes */
public BusinessObject
{
[PrimaryKey]
public Int32 Id { get; set; }
[Property]
public String Data { get; set; }
/* more properties */
}
public class KillerApp
{
private ISomeOtherClass someOtherClass;
public KillerApp(ISomeOtherClass someOtherClass)
{
this.someOtherClass = someOtherClass;
}
public void DoThatThing()
{
BusinessObject[] entities = BusinessObject.FindAll() /* built-in ActiveRecord call! */
someOtherClass.HandleNewBizObjs(entities);
}
}
[TestClass]
public class When_Doing_That_Thing : ActiveRecordTest /* setup active record for testing */
{
private ISomeOtherClass someOtherClass = new SomeOtherClass();
private KillerApp app;
[TestInitialize]
public void TestInitialize()
{
app = new KillerApp(someOtherClass);
}
[TestMethod]
public void Should_convert_all_gizmo_records_to_busn_objects()
{
app.DoThatThing();
Assert.AreEqual(someOtherClass.Results, /* however you're confirming */);
}
}
结果是一个更小的类和一个业务对象和数据层,您可以更轻松地更改。而且您甚至不必模拟数据库调用,您可以配置和初始化ActiveRecord以使用测试数据库(内存中,甚至)。
答案 1 :(得分:1)
嗯,我认为唯一真正需要测试的是TransformIntoBizObjs,因为连接代码应该在其他地方编写/测试过。只需传递可能显示为Transform的内容,并查看是否弹出正确的内容就是您需要做的事情。
请记住测试Transform的所有用例,甚至可能不应该在函数调用中结束的可能奇怪的项目。永远不知道人们在他们的数据库中推了什么。
答案 2 :(得分:0)
控制反转(IoC)和依赖注入(DI)将大大有助于使您的代码更易于测试。有很多框架可以帮助你解决这个问题,但是为了你的目的,你不一定需要付出那么大的努力。
首先提取可能如下所示的界面:
Interface ISqlLiteConnection
{
public IList<GizmoRecord> Query(...);
}
完成后,您应该重构OpenSqlLiteConnection()方法以返回ISqlLiteConnection的实例,而不是具体的实现。要进行测试,只需创建一个实现您的接口的类,它可以模拟实际的数据库查询和具有确定结果的连接。
答案 3 :(得分:-1)
数据库很复杂,你需要测试你的查询代码,你需要针对一个真正的sqlite实例进行测试 - 否则你不能确定你没有遇到一些罕见的sqlite怪癖或bug。
并且由于测试查询的唯一方法是在真正的sqlite文件上运行它,并且在测试中包含这样的文件非常容易,添加另一层只是为了使它“更”可测试或者进行“纯粹的”单元测试。
只需确保将您能想到的所有奇怪边缘情况添加到示例文件中。