如何在内存中初始化测试并在每次测试中使用

DIl*_*d K 8 c# performance unit-testing

我正在尝试创建单元测试.我有班级用户:

 public class User
{
    public int UsersCount
    {
        get
        {
            using (MainContext context = new MainContext())
            {
                return context.Users.Count();
            }
        }
    }
    public Guid Id { get; set; } = Guid.NewGuid();
    public string UserName { get; set; }
    public string Password { get; set; }
    public Contact UserContact { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我的第一个测试是UsersCount_Test测试,它测试UsersCount属性:

 [TestMethod]
    public void UsersCount_Test()
    {
        var user = new User();
        var context = new MainContext();
        int usersCount = context.Users.Count();
        context.Users.Add(new User());
        context.SaveChanges();
        Assert.AreEqual(usersCount + 1, user.UsersCount, $"It should be {usersCount + 1} because we're adding one more user");
    }
Run Code Online (Sandbox Code Playgroud)

如果我在我的测试类中添加新的测试方法(我使用单独的类来测试每个实体),我需要创建User的新实例.这就是我这样做的原因:

    public class BaseTest<T>
{
    public T TestEntity;

    public MainContext TestContext = new MainContext();
}
Run Code Online (Sandbox Code Playgroud)

现在每个测试类都继承自这个类.我还创建了测试初始化​​方法.现在我的测试类看起来像这样:

 [TestClass]
public class UserTest : BaseTest<User>
{
    [TestMethod]
    public void UsersCount()
    {
        int usersCount = TestContext.Users.Count();
        TestContext.Users.Add(new User());
        TestContext.SaveChanges();
        Assert.AreEqual(usersCount + 1, TestEntity.UsersCount, $"It should be {usersCount + 1} because we're adding one more user");
    }

    [TestInitialize]
    public void SetTestEntity()
    {
        TestEntity = new User();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我正在向User添加新属性并编写一些逻辑:

  string phoneNumber;
    public string PhoneNumber { get { return phoneNumber; } set { SetUserContact(phoneNumber, value); phoneNumber = value; } }

    void SetUserContact(string oldContact, string newContact)
    {
        UserContact.ContactsList.Remove(oldContact);
        UserContact.ContactsList.Add(newContact);
    }
Run Code Online (Sandbox Code Playgroud)

之后,我正在创建新的测试:

     [TestMethod]
    public void ContactList_Test()
    {
        var newPhone = "+8888888888888";
        TestEntity.PhoneNumber = newPhone;
        Assert.IsTrue(TestEntity.UserContact.ContactsList.Any(a => a == newPhone), $"It should contains {newPhone}");
    }
Run Code Online (Sandbox Code Playgroud)

测试失败,因为TestEntity的UserContact为null.我知道TestEntity应该由逻辑创建.之后我修复了测试initilizer方法:

 [TestInitialize]
    public void SetTestEntity()
    {
        TestEntity = new User() { UserContact = new Contact() };
    }
Run Code Online (Sandbox Code Playgroud)

这是联系模式

    public class Contact
{
    public Guid Id { get; set; } = Guid.NewGuid();
    public virtual List<string> ContactsList { get; set; } = new List<string>();
}
Run Code Online (Sandbox Code Playgroud)

我的问题是如何只设置TestEntity一次,是否可能(可能在内存中使用它并在调用SetTestEntity方法时使用它)?因为SetTestentity方法在每个测试中创建一个新实体,所以需要更多的开发时间.(例如,如果创建UserContact实例全部需要3秒,则测试运行时间超过3秒).在这种情况下,另一种方法是在ContactLists测试中设置UserContact,但我认为这不是一个好主意.在将来我们将添加新逻辑时,我需要修复每个测试.请给我任何建议和/或想法.

Luk*_*azy 5

如果您真的必须TestInitialize在每次测试之前运行。您只能使用ClassInitialize一次为类运行测试初始化​​。

从我所看到的,您的性能问题是由您的应用程序的设计和架构引起的,您违反了单一责任原则。创建静态数据库实体或在测试中共享它不是解决方案,它只会产生更多的技术债务。一旦你在测试中共享任何东西,它就必须在测试中维护,并且根据定义,单元测试应该单独和独立运行,以允许使用新数据测试每个场景。

您不应该创建依赖于MainContext. 单User真的应该知道Users数据库里有多少吗?如果没有,请创建单独的存储库,通过添加少数用户调用特定实现并检查是否添加了正确的用户数量,使用 InMemoryDatabaseMainContext注入和方法GetUsersCount()和单元测试,如下所示:

public interface IUsersRepository
    {
        int GetUsersCount();
    }

    public class UsersRepository : IUsersRepository
    {
        private readonly EntityFrameworkContext _context;

        public UsersRepository(EntityFrameworkContext context)
        {
            _context = context;
        }

        public int GetUsersCount()
        {
            return _context.Users.Count();
        }
    }
Run Code Online (Sandbox Code Playgroud)

以后只有真正使用上下文的方法才应该被测试,InMemoryDatabase对于使用 IUserRepository 的方法,每个特定的方法都应该被模拟,因为它是单独测试的。