Как получить инициализацию теста в памяти и использовать в каждом тесте - PullRequest
0 голосов
/ 12 февраля 2019

Я пытаюсь создать Unit Test.У меня есть класс User:

 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; }
}

Мой первый тест - это тест 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");
    }

Если я добавлю новый метод теста в свой класс тестирования (я использую отдельныйклассы для тестирования каждой сущности), мне нужно создать новый экземпляр пользователя.Вот почему я сделал это:

    public class BaseTest<T>
{
    public T TestEntity;

    public MainContext TestContext = new MainContext();
}

Теперь каждый тестовый класс наследуется от этого класса.А также я создал метод инициализации теста.Теперь мой тестовый класс выглядит следующим образом:

 [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();
    }
}

Теперь я добавляю новое свойство в 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);
    }

После этого я создаю новый тест:

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

Тест не пройден, так как UserContact of TestEntity имеет значение null.Я понял, что TestEntity должен быть создан с помощью логики.После этого я исправляю метод инициализации теста:

 [TestInitialize]
    public void SetTestEntity()
    {
        TestEntity = new User() { UserContact = new Contact() };
    }

Вот модель контакта

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

Мой вопрос: как установить TestEntity только один раз, возможно ли это (возможно, получить его в памятии использовать его при вызове метода SetTestEntity)?Потому что метод SetTestentity создает новую сущность в каждом тесте и занимает больше времени на разработку.(Например, если создание экземпляра UserContact занимает всего 3 секунды, тест выполняется более 3 секунд).Другой способ, в данном случае, это установить UserContact в тесте ContactLists, но я думаю, что это не очень хорошая идея.В будущем, когда мы добавим новую логику, мне нужно будет исправить каждый тест.Пожалуйста, дайте мне любое предложение и / или идеи.

Ответы [ 2 ]

0 голосов
/ 21 февраля 2019

TestInitialize и TestCleanup запускаются до и после каждого теста, чтобы убедиться, что никакие тесты не связаны.

Если вы хотите запускать методы до и после ВСЕХ тестов только один раз, декорируйте соответствующие методы с помощью ClassInitializeи атрибуты ClassCleanup.

При написании тестов вы можете использовать следующие дополнительные атрибуты:

Пример кода -

// Use ClassInitialize to run code before running the first test in the class
[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext) { }

// Use ClassCleanup to run code after all tests in a class have run
[ClassCleanup()]
public static void MyClassCleanup() { }

// Use TestInitialize to run code before running each test 
[TestInitialize()]
public void MyTestInitialize() { }

// Use TestCleanup to run code after each test has run
[TestCleanup()]
public void MyTestCleanup() { }

По сути, вы можете иметь свой метод SetEntity в своем методе ClassIntialize. Надеюсь, это поможет.

0 голосов
/ 14 февраля 2019

Если вам действительно нужно TestInitialize пробегать перед каждым тестом.Вы можете использовать ClassInitialize для запуска инициализации теста для класса только один раз.

НО

Из того, что я вижу, ваша проблема с производительностью вызвана дизайном и архитектурой вашего приложения, когда вы нарушаете принцип единой ответственности.Создание статического объекта базы данных или разделение его между тестами - это не решение, а только увеличение технической задолженности.После того, как вы поделитесь чем-нибудь в ходе теста, его необходимо поддерживать по тесту И по определению модульный тест СЛЕДУЕТ запускать отдельно и независимо, чтобы позволить тестировать каждый сценарий на свежие данные.

Вы не должны создавать модели баз данных, которые зависят от MainContext.Должен ли одиночный User действительно знать, сколько Users есть в базе данных?Если нет, то создайте отдельный репозиторий, в который будет вставлено MainContext и метод GetUsersCount(), а также выполните модульное тестирование с помощью InMemoryDatabase, добавив несколько пользователей, вызывающих конкретную реализацию, и проверьте, было ли добавлено правильное количество пользователей, например:

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();
        }
    }

Позже только методы, которые действительно используют контекст, должны быть протестированы с InMemoryDatabase, а для методов, использующих IUserRepository, каждый конкретный метод должен быть проверен, поскольку он тестируется отдельно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...