测试驱动开发例子学习-Using TDD with ADO.NET

1. 连接字符串的测试驱动范例:
DataBase -SQL Server 2000:
1.Connecting to the Database
2.XML
应用程序的配置文件:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="Catalog.Connection"
value="server=(local);database=catalog;Trusted_Connection=true" />
</appSettings>
</configuration>

using System;
using System.Data;
using System.Data.SqlClient;
using NUnit.Framework;
[TestFixture]
public class SqlConnectionFixture
{
[Test]
public void ConnectionIsOpen()
{
//
使用配置文件定义连接字符串
//string connectionString =
// ConfigurationSettings.AppSettings.Get("Catalog.Connection");
//Assert.IsNotNull(connectionString);
SqlConnection connection =
new SqlConnection(
@"server=(local);database=catalog;Trusted_Connection=true");
connection.Open();
Assert.AreEqual(ConnectionState.Open,connection.State);
connection.Close();
}
}





2. 连接字符串的测试驱动优化范例:

以上代码可以替换为:
[TestFixture]
public class SqlConnectionFixture
{
private string connectionString;
[SetUp]
public void RetrieveConnectionString()
{
connectionString =
ConfigurationSettings.AppSettings.Get("Catalog.Connection");
}
[Test]
public void CanRetrieveConnectionString()
{
Assert.IsNotNull(connectionString);
}
[Test]
public void ConnectionIsOpen()
{
SqlConnection connection =
new SqlConnection(connectionString);
connection.Open();
Assert.AreEqual(ConnectionState.Open,connection.State);
connection.Close();
}
}

3. ArtistGateway实现数据访问层的一个范例
对数据表artist 的通用操作
using System;
using System.Data;
using System.Data.SqlClient;
 
namespace DataAccessLayer
{
 public class ArtistGateway
 {
 private SqlDataAdapter adapter; 
private SqlConnection connection;
 private SqlCommand command;
 private SqlCommandBuilder builder;
 
 public ArtistGateway(SqlConnection connection)
 {
 this.connection = connection;
 
 command = new SqlCommand(
 "select id,name from artist where id = @id",
 connection);
 command.Parameters.Add("@id",SqlDbType.BigInt);
 
 adapter = new SqlDataAdapter(command);
 builder = new SqlCommandBuilder(adapter);
 }
 
 public long Insert(
RecordingDataSet recordingDataSet,string artistName)
 {
 long artistId = 
GetNextId(recordingDataSet.Artists.TableName);
 
RecordingDataSet.Artist artistRow = 
recordingDataSet.Artists.NewArtist();
 artistRow.Id = artistId;
 artistRow.Name = artistName;
 recordingDataSet.Artists.AddArtist(artistRow);
 
 adapter.Update(recordingDataSet,
recordingDataSet.Artists.TableName);
 
 return artistId;
 }
 
 public RecordingDataSet.Artist 
FindById(long artistId,RecordingDataSet recordingDataSet)
 {
 command.Parameters["@id"].Value = artistId;
 adapter.Fill(recordingDataSet,
recordingDataSet.Artists.TableName);
 DataRow[] rows = recordingDataSet.Artists.Select(
 String.Format("id={0}",artistId));
 
if(rows.Length < 1) return null;
 return (RecordingDataSet.Artist)rows[0]; 
}
 
 public long GetNextId(string tableName)
 {
 SqlTransaction transaction = 
connection.BeginTransaction(
 IsolationLevel.Serializable,"GenerateId");
 
 SqlCommand selectCommand = new SqlCommand(
 "select nextId from PKSequence where tableName = @tableName",
connection,transaction);
 selectCommand.Parameters.Add("@tableName",
SqlDbType.VarChar).Value=tableName;
 
 long nextId = (long)selectCommand.ExecuteScalar();
 SqlCommand updateCommand = new SqlCommand(
 "update PKSequence set nextId = @nextId where tableName=@tableName",
 connection,transaction);
 updateCommand.Parameters.Add("@tableName",
SqlDbType.VarChar).Value=tableName;
 updateCommand.Parameters.Add("@nextId",
SqlDbType.BigInt).Value=nextId+1;
 updateCommand.ExecuteNonQuery();
 transaction.Commit();
 
 return nextId;
 }
4. ArtistFixture.cs 数据访问层代理类的测试用例
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using DataAccessLayer;
using NUnit.Framework;
 
[TestFixture]
public class ArtistFixture
{
 [Test]
 public void RetrieveArtistFromDatabase()
 {
 string artistName = "Artist";
 
 SqlConnection connection = 
new SqlConnection(
 ConfigurationSettings.AppSettings.Get(
"Catalog.Connection"));
 connection.Open();
 
 ArtistGateway gateway = new ArtistGateway(connection);
 long artistId = 
gateway.Insert(new RecordingDataSet(),artistName);
 
RecordingDataSet loadedFromDB = new RecordingDataSet();
 RecordingDataSet.Artist loadedArtist = 
gateway.FindById(artistId,loadedFromDB);
 
 Assert.AreEqual(artistId,loadedArtist.Id);
 Assert.AreEqual(artistName,loadedArtist.Name);
 
 gateway.Delete(loadedFromDB,artistId); //删除功能的驱动
connection.Close();
 }
}
5. 删除驱动所形成的实现代码:
public void Delete(RecordingDataSet recordingDataSet,long artistId)
{
RecordingDataSet.Artist loadedArtist =
FindById(artistId,recordingDataSet);
loadedArtist.Delete();
adapter.Update(recordingDataSet,
recordingDataSet.Artists.TableName);
}
6. 有了完整的测试用例,以及实现的代码,可以进行重构:
//首先优化测试的代码:
[TestFixture]
public class ArtistFixture
{
 private static readonly string artistName = "Artist";
 private SqlConnection connection;
 private ArtistGateway gateway; 
private RecordingDataSet recordingDataSet;
 private long artistId; 
 
 [SetUp]
 public void SetUp()
 {
 connection = new SqlConnection(
 ConfigurationSettings.AppSettings.Get(
 "Catalog.Connection"));
 connection.Open();
 
 recordingDataSet = new RecordingDataSet();
 gateway = new ArtistGateway(connection);
 
 artistId = gateway.Insert(recordingDataSet,artistName);
 }
 
 [TearDown]
 public void TearDown()
 {
 gateway.Delete(recordingDataSet,artistId);
 connection.Close();
 }
 
 [Test]
 public void RetrieveArtistFromDatabase()
 {
 RecordingDataSet loadedFromDB = new RecordingDataSet();
 RecordingDataSet.Artist loadedArtist = 
gateway.FindById(artistId,loadedArtist.Name); 
}
 
 [Test]
 public void DeleteArtistFromDatabase()
 {
 RecordingDataSet emptyDataSet = new RecordingDataSet();
 long deletedArtistId = gateway.Insert(emptyDataSet,"Deleted Artist");
 gateway.Delete(emptyDataSet,deletedArtistId);
 
 RecordingDataSet.Artist deleletedArtist = 
gateway.FindById(deletedArtistId,emptyDataSet);
 Assert.IsNull(deleletedArtist);
 }
}
7. 在重构的基础上加入更新方法:.
[Test]
public void UpdateArtistInDatabase()
{
 RecordingDataSet.Artist artist = recordingDataSet.Artists[0];
 artist.Name = "Modified Name";
 gateway.Update(recordingDataSet);
 
 RecordingDataSet updatedDataSet = new RecordingDataSet();
 RecordingDataSet.Artist updatedArtist = 
gateway.FindById(artistId,updatedDataSet);
 Assert.AreEqual("Modified Name",updatedArtist.Name);
}
//The following is the implementation of Update:
 public void Update(RecordingDataSet recordingDataSet)
 {
 adapter.Update(recordingDataSet,
 recordingDataSet.Artists.TableName);
 }
 
8. Genre Gateway 另一个测试驱动的例子(类似于artists)
[TestFixture]
public class GenreFixture
{
 private static readonly string genreName = "Rock";
 private SqlConnection connection;
 private GenreGateway gateway; 
private RecordingDataSet recordingDataSet;
 private long genreId; 
 
 [SetUp]
 public void SetUp()
 {
 connection = new SqlConnection(
 ConfigurationSettings.AppSettings.Get(
 "Catalog.Connection"));
 connection.Open();
 
 recordingDataSet = new RecordingDataSet();
 gateway = new GenreGateway(connection);
 
 genreId = gateway.Insert(recordingDataSet,genreName);
 }
 
 [TearDown]
 public void TearDown()
 {
 gateway.Delete(recordingDataSet,genreId);
 connection.Close();
 }
 
 [Test]
 public void RetrieveGenreFromDatabase()
 {
 RecordingDataSet loadedFromDB = new RecordingDataSet();
 RecordingDataSet.Genre loadedGenre = 
gateway.FindById(genreId,loadedFromDB);
 
 Assert.AreEqual(genreId,loadedGenre.Id);
 Assert.AreEqual(genreName,loadedGenre.Name); 
}
}
9. 实现部分Genre Gateway:
public class GenreGateway
 {
 private SqlDataAdapter adapter; 
private SqlConnection connection;
 private SqlCommand command;
 private SqlCommandBuilder builder;
 
 public GenreGateway(SqlConnection connection)
 {
 this.connection = connection;
 
 command = new SqlCommand(
 "select id,name from Genre where id = @id",SqlDbType.BigInt);
 
 adapter = new SqlDataAdapter(command);
 builder = new SqlCommandBuilder(adapter);
 }
 
 public long Insert(RecordingDataSet recordingDataSet,
string genreName)
 {
 long genreId = GetNextId(recordingDataSet.Genres.TableName);
 
RecordingDataSet.Genre genreRow = 
recordingDataSet.Genres.NewGenre();
 genreRow.Id = genreId;
 genreRow.Name = genreName;
 recordingDataSet.Genres.AddGenre(genreRow);
 
 adapter.Update(recordingDataSet,
recordingDataSet.Genres.TableName);
 
 return genreId;
 }
 
 public RecordingDataSet.Genre 
FindById(long genreId,RecordingDataSet recordingDataSet)
 {
 command.Parameters["@id"].Value = genreId;
 adapter.Fill(recordingDataSet,
recordingDataSet.Genres.TableName);
 DataRow[] rows = recordingDataSet.Genres.Select(
 String.Format("id={0}",genreId));
 
if(rows.Length < 1) return null;
 return (RecordingDataSet.Genre)rows[0];
 }
 
 public void Delete(RecordingDataSet recordingDataSet,
long genreId)
 {
 RecordingDataSet.Genre loadedGenre = 
FindById(genreId,recordingDataSet);
 loadedGenre.Delete();
 adapter.Update(recordingDataSet,
recordingDataSet.Genres.TableName);
 }
 
 public long GetNextId(string tableName)
 { /* same as in ArtistGateway */ }
 }
 
10.获取下一个ID
[TestFixture]
public class IdGeneratorFixture
{
 private SqlConnection connection;
 
 [SetUp]
 public void OpenConnection()
 {
 connection = new SqlConnection(
 ConfigurationSettings.AppSettings.Get(
 "Catalog.Connection"));
 connection.Open();
 }
 
 [Test]
 public void GetNextIdIncrement()
 {
 SqlCommand sqlCommand = new SqlCommand(
 "select nextId from PKSequence where tableName=@tableName",
 connection);
 
sqlCommand.Parameters.Add(
 "@tableName",SqlDbType.VarChar).Value="Artist";
 
long nextId = (long)sqlCommand.ExecuteScalar();
 long nextIdFromGenerator = 
IdGenerator.GetNextId("Artist",connection);
 Assert.AreEqual(nextId,nextIdFromGenerator);
 nextId = (long)sqlCommand.ExecuteScalar();
 Assert.AreEqual(nextId,nextIdFromGenerator + 1);
 }
 
 [TearDown]
 public void CloseConnection()
 {
 connection.Close();
 }
}
//实现部分
public class IdGenerator
{
 public static long GetNextId(string tableName,
SqlConnection connection)
 {
 SqlTransaction transaction = connection.BeginTransaction(
IsolationLevel.Serializable,"GenerateId");
 
 SqlCommand selectCommand = new SqlCommand(
 "select nextId from PKSequence where tableName = @tableName",
 SqlDbType.VarChar).Value=tableName;
 
 long nextId = (long)selectCommand.ExecuteScalar();
 SqlCommand updateCommand = new SqlCommand(
 "update PKSequence set nextId = @nextId where tableName=@tableName",SqlDbType.VarChar).Value=tableName;
 updateCommand.Parameters.Add("@nextId",SqlDbType.BigInt).Value=nextId+1;
 updateCommand.ExecuteNonQuery();
 transaction.Commit();
 return nextId;
 }
}
11.IdGeneratorFixture.cs优化过得实现:
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using NUnit.Framework;
using DataAccessLayer;
 
[TestFixture]
public class IdGeneratorFixture : ConnectionFixture
{
 [Test]
 public void GetNextIdIncrement()
 {
 SqlCommand sqlCommand = 
new SqlCommand(
 "select nextId from PKSequence where tableName=@tableName",
 Connection);
 sqlCommand.Parameters.Add("@tableName",SqlDbType.VarChar).Value="Artist";
 
long nextId = (long)sqlCommand.ExecuteScalar();
 long nextIdFromGenerator = IdGenerator.GetNextId("Artist",Connection);
 Assert.AreEqual(nextId,nextIdFromGenerator + 1);
 }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结