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

本文介绍了一个使用测试驱动开发(TDD)方法来构建SQL Server 2000数据库数据访问层的实例,包括连接字符串管理、数据操作及序列ID生成等方面,并通过具体的.NET代码示例展示了如何组织单元测试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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, loadedFromDB);
 
      Assert.AreEqual(artistId,loadedArtist.Id);
      Assert.AreEqual(artistName, 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",
            connection);
         command.Parameters.Add("@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",
         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;
   }
}
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);
      nextId = (long)sqlCommand.ExecuteScalar();
      Assert.AreEqual(nextId, nextIdFromGenerator + 1);
   }
}
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值