我(相当)是TDD的新手,所以请保持温和!
我正在使用CodeFirst在VBS2015社区中使用EF6在C#中创建我的应用程序。
我为我的数据访问创建了一个接口,并且能够在内存中创建模拟数据库以进行单元测试。
我的每个模型都有一个唯一的标识符列,名为Id(例如,UserID)
好的,所以当我向Mock数据库添加记录时,我需要让它为记录创建一个新的ID值,因为我在后面的代码中引用它。当每个表都有唯一的ID列时,如何使用通用集执行此操作?我是否必须独立模拟每张桌子?
我的数据库界面如下所示:
Namespace Labinator2016.Lib.Headers
{
using System;
using System.Collections.Generic;
using System.Linq;
public interface ILabinatorDb : IDisposable
{
IQueryable<T> Query<T>() where T : class;
void Add<T>(T entity) where T : class;
void Delete<T1>(System.Linq.Expressions.Expression<Func<T1, bool>> target) where T1 : class;
void Remove<T>(T entity) where T : class;
void Update<T>(T entity) where T : class;
void SaveChanges();
}
}
我的模拟数据库看起来像这样:
namespace Labinator2016.Tests.TestData
{
using System;
using System.Collections.Generic;
using System.Linq;
using Lib.Headers;
using Labinator2016.Lib.Models;
class FakeDatabase : ILabinatorDb
{
public IQueryable<T> Query<T>() where T : class
{
return this.Sets[typeof(T)] as IQueryable<T>;
}
public void Dispose() { }
public void AddSet<T>(IQueryable<T> objects)
{
this.Sets.Add(typeof(T), objects);
}
public void Add<T>(T entity) where T : class
{
this.Added.Add(entity);
IQueryable<T> temp = this.Sets[typeof(T)] as IQueryable<T>;
List<T> existing = temp.ToList();
existing.Add(entity);
Sets[typeof(T)] = existing;
}
void ILabinatorDb.Delete<T1>(System.Linq.Expressions.Expression<Func<T1, bool>> Target)
{
}
public void Update<T>(T entity) where T : class
{
this.Updated.Add(entity);
}
public void Remove<T>(T entity) where T : class
{
this.Removed.Add(entity);
}
public void SaveChanges()
{
this.saved++;
}
public Dictionary<Type, object> Sets = new Dictionary<Type, object>();
public List<object> Added = new List<object>();
public List<object> Updated = new List<object>();
public List<object> Removed = new List<object>();
public int saved = 0;
}
}
目前,我有一些代码可以根据名称从用户表中获取用户对象。如果它不存在,则首先创建一个。这是我的代码:
User existingUser = this.db.Query<User>().Where(u => u.UserName == newUserName).FirstOrDefault();
if (existingUser == null)
{
User userToAdd = new User();
userToAdd.UserName = newUserName;
userToAdd.Password = PasswordHash.CreateHash("password");
this.db.Add<User>(userToAdd);
this.db.SaveChanges();
existingUser = this.db.Query<User>().Where(u => u.EmailAddress == nu).FirstOrDefault();
}
那么,我是否在我的应用程序中完全使用了错误的方法,还是有办法模拟这个功能?
答案 0 :(得分:0)
如果不确切地知道你在测试它有点难以说,但我怀疑你可以使用像Moq这样的模拟框架为你做很多繁重的工作。下面是一个简单的示例,说明如果新用户不存在于数据库中以及如何创建新用户,则如何测试新用户(使用Moq)。
public class UserManager
{
private readonly ILabinatorDb _liLabinatorDb;
public UserManager(ILabinatorDb liLabinatorDb)
{
_liLabinatorDb = liLabinatorDb;
}
public void CreateNewUser(string userName)
{
User existingUser = _liLabinatorDb.Query<User>().FirstOrDefault(u => u.UserName == userName);
if (existingUser == null)
{
User userToAdd = new User();
userToAdd.UserName = userName;
_liLabinatorDb.Add<User>(userToAdd);
_liLabinatorDb.SaveChanges();
}
}
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void GivenAUsernameWhenNoUserExistsInDatabaseThenNewUserCreated()
{
var mockDatabase = new Mock<ILabinatorDb>();
mockDatabase.Setup(a => a.Query<User>()).Returns(new List<User>().AsQueryable());
var userManager = new UserManager(mockDatabase.Object);
userManager.CreateNewUser("UserDoesntExistInDatabase");
mockDatabase.Verify(a => a.Add(It.IsAny<User>()),Times.Once);
mockDatabase.Verify(a => a.SaveChanges(),Times.Once);
}
[TestMethod]
public void GivenAUsernameWhenUserExistsInDatabaseThenNewUserNotCreated()
{
var mockDatabase = new Mock<ILabinatorDb>();
var mockedUsers = new List<User>
{
new User {UserId = 2, UserName = "UserExistsInDatabase"}
};
mockDatabase.Setup(a => a.Query<User>()).Returns(mockedUsers.AsQueryable());
var userManager = new UserManager(mockDatabase.Object);
userManager.CreateNewUser("UserExistsInDatabase");
mockDatabase.Verify(a => a.Add(It.IsAny<User>()), Times.Never);
mockDatabase.Verify(a => a.SaveChanges(), Times.Never);
}
}
答案 1 :(得分:0)
或者您可以在不创建具有正确框架(Typemock Isolator)的新Manager类的情况下执行此操作,我重新创建了第一个示例,以显示差异。它更短更清晰:
[TestMethod, Isolated]
public void TestAddWithEmptyDb()
{
//Arrange
var fakeDb = Isolate.Fake.AllInstances<FakeDatabase>(Members.CallOriginal);
var userList = new List<User>();
Isolate.WhenCalled(() => fakeDb.Query<User>()).WillReturnCollectionValuesOf(userList.AsQueryable());
//Act
UnderTest test = new UnderTest();
test.get("newName");
//Assert
Isolate.Verify.WasCalledWithArguments(() => fakeDb.Add<User>(null)).Matching(a => (a[0] as User).UserName == "newName");
Isolate.Verify.WasCalledWithAnyArguments(() => fakeDb.SaveChanges());
}
答案 2 :(得分:0)
我们的项目中也有类似的问题。
这是我们得出的结论:
GetUserByName(name)
,GetAllActiveUsers()
等方法的接口。