我正在尝试测试我的项目中的一些异常,我捕获的异常之一是SQlException。
似乎不能使用new SqlException(),所以我不确定如何抛出异常,特别是在不调用数据库的情况下(因为这些是单元测试,所以通常建议不要调用数据库,因为它很慢)。
我正在使用NUnit和Moq,但我不确定如何伪造它。
在回答一些似乎都基于ADO.NET的问题时,请注意我使用的是Linq to Sql。所以这些东西就像是幕后的东西。
@MattHamilton要求提供更多信息:
System.ArgumentException : Type to mock must be an interface or an abstract or non-sealed class.
at Moq.Mock`1.CheckParameters()
at Moq.Mock`1..ctor(MockBehavior behavior, Object[] args)
at Moq.Mock`1..ctor(MockBehavior behavior)
at Moq.Mock`1..ctor()当它尝试模拟模型时,将帖子放到第一行
var ex = new Mock<System.Data.SqlClient.SqlException>();
ex.SetupGet(e => e.Message).Returns("Exception message");发布于 2009-09-07 06:22:31
由于您使用的是Linq to Sql,下面是使用NUnit和Moq测试您提到的场景的示例。我不知道你的DataContext的确切细节,也不知道你有什么可用的。根据您的需要进行编辑。
你需要用一个自定义的类来包装DataContext,你不能用Moq来模拟DataContext。您也不能模拟SqlException,因为它是密封的。你需要用你自己的异常类来包装它。完成这两件事并不难。
让我们从创建测试开始:
[Test]
public void FindBy_When_something_goes_wrong_Should_handle_the_CustomSqlException()
{
var mockDataContextWrapper = new Mock<IDataContextWrapper>();
mockDataContextWrapper.Setup(x => x.Table<User>()).Throws<CustomSqlException>();
IUserResository userRespoistory = new UserRepository(mockDataContextWrapper.Object);
// Now, because we have mocked everything and we are using dependency injection.
// When FindBy is called, instead of getting a user, we will get a CustomSqlException
// Now, inside of FindBy, wrap the call to the DataContextWrapper inside a try catch
// and handle the exception, then test that you handled it, like mocking a logger, then passing it into the repository and verifying that logMessage was called
User user = userRepository.FindBy(1);
}让我们实现测试,首先让我们使用存储库模式包装Linq to Sql调用:
public interface IUserRepository
{
User FindBy(int id);
}
public class UserRepository : IUserRepository
{
public IDataContextWrapper DataContextWrapper { get; protected set; }
public UserRepository(IDataContextWrapper dataContextWrapper)
{
DataContextWrapper = dataContextWrapper;
}
public User FindBy(int id)
{
return DataContextWrapper.Table<User>().SingleOrDefault(u => u.UserID == id);
}
}接下来像这样创建IDataContextWrapper,您可以在主题上查看此blog post,我的略有不同:
public interface IDataContextWrapper : IDisposable
{
Table<T> Table<T>() where T : class;
}接下来创建CustomSqlException类:
public class CustomSqlException : Exception
{
public CustomSqlException()
{
}
public CustomSqlException(string message, SqlException innerException) : base(message, innerException)
{
}
}下面是IDataContextWrapper的一个示例实现:
public class DataContextWrapper<T> : IDataContextWrapper where T : DataContext, new()
{
private readonly T _db;
public DataContextWrapper()
{
var t = typeof(T);
_db = (T)Activator.CreateInstance(t);
}
public DataContextWrapper(string connectionString)
{
var t = typeof(T);
_db = (T)Activator.CreateInstance(t, connectionString);
}
public Table<TableName> Table<TableName>() where TableName : class
{
try
{
return (Table<TableName>) _db.GetTable(typeof (TableName));
}
catch (SqlException exception)
{
// Wrap the SqlException with our custom one
throw new CustomSqlException("Ooops...", exception);
}
}
// IDispoable Members
}发布于 2009-09-06 23:44:45
你可以通过反射来做到这一点,当微软进行更改时,你必须维护它,但它确实有效,我刚刚测试了它:
public class SqlExceptionCreator
{
private static T Construct<T>(params object[] p)
{
var ctors = typeof(T).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
return (T)ctors.First(ctor => ctor.GetParameters().Length == p.Length).Invoke(p);
}
internal static SqlException NewSqlException(int number = 1)
{
SqlErrorCollection collection = Construct<SqlErrorCollection>();
SqlError error = Construct<SqlError>(number, (byte)2, (byte)3, "server name", "error message", "proc", 100);
typeof(SqlErrorCollection)
.GetMethod("Add", BindingFlags.NonPublic | BindingFlags.Instance)
.Invoke(collection, new object[] { error });
return typeof(SqlException)
.GetMethod("CreateException", BindingFlags.NonPublic | BindingFlags.Static,
null,
CallingConventions.ExplicitThis,
new[] { typeof(SqlErrorCollection), typeof(string) },
new ParameterModifier[] { })
.Invoke(null, new object[] { collection, "7.0.0" }) as SqlException;
}
} 这还允许您控制SqlException的数量,这一点非常重要。
发布于 2011-05-21 00:44:14
我有一个解决方案。我不确定这是天才还是疯狂。
以下代码将创建一个新的SqlException:
public SqlException MakeSqlException() {
SqlException exception = null;
try {
SqlConnection conn = new SqlConnection(@"Data Source=.;Database=GUARANTEED_TO_FAIL;Connection Timeout=1");
conn.Open();
} catch(SqlException ex) {
exception = ex;
}
return(exception);
}然后你可以像这样使用它(这个例子使用的是Moq)
mockSqlDataStore
.Setup(x => x.ChangePassword(userId, It.IsAny<string>()))
.Throws(MakeSqlException());以便您可以在存储库、处理程序和控制器中测试SqlException错误处理。
现在我要去躺着了。
https://stackoverflow.com/questions/1386962
复制相似问题