Автоматическое создание столбца идентификации в C# Net Core 3.1 Unit Test - PullRequest
0 голосов
/ 30 января 2020

Мои основы c классы

    public class Book {
        public int Id { get; set;}
        public string Title { get; set;} 
    }
    public class Author {
        public Author () {
            BookList = new HashSet<Book> ();
            for (int i = 0; i< 10; i++) {
                var book = new Book() {
                    Title = "Funny book" + i;
                }
                BookList.Add(book);
            }
        }
        public int Id { get; set;}
        public string Name { get; set;}
        public ICollection<Book> BookList { get; set; }
    }

Мой класс хранилища

        public interface IBookRepository
    {       
        Task<Book> CreateBook(Book book);
    }
    public class BookRepository : IBookRepository
    {
        public BookRepository(BookStoreContext context)
             : base(context)
        {
        }
        public async Task<Book> CreateBook(Book book)
        {
            if (book == null)
                throw new ArgumentNullException("book cannot be null");
            }
            Context.Book.Add(book);
            await Context.SaveChangesAsync().ConfigureAwait(false);

            return book;
        }           
    }

Я настраиваю свой модульный тест со следующим служебным классом

    public static class AuthorData
    {
        public static List<Author> GetData()
        {
            List<Author> authors = new List<Author>();
            Author author1 = new Author()
            {
                Id = 100,
                Name="author1"
            };
            authors.Add(author1);
            Author author2 = new Author()
            {
                Id = 200,
                Name="author2"
            };
            authors.Add(author2);
        }
    }
    public static class BookData
    {
        public static List<Book> GetData()
        {
            List<Book> books = new List<Book>();
            Book book1 = new Book()
            {
                Id = 1000,
                Name="Wonderful book 1"
            };
            books.Add(book1);
            Book book2 = new Book()
            {
                Id = 1001,
                Name="Wonderful book 2"
            };
            books.Add(book2);
        }
    }
      public class AuthorContextMock
      {
         public static BookStoreContext ConfigureSensorContext(IServiceCollection services)
          {
            services.AddDbContext<BookStoreContext>(c =>
         c.UseInMemoryDatabase(Guid.NewGuid().ToString())
        .UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll)
        .EnableDetailedErrors()
        .EnableSensitiveDataLogging());
         var serviceProvider = services
        .AddEntityFrameworkInMemoryDatabase()
        .BuildServiceProvider();
         var Context = serviceProvider.GetRequiredService<BookStoreContext>();
         Context.Database.EnsureCreated();

         Context.Authors.AddRange(AuthorData.GetData());
         Context.Books.AddRange(BookData.GetData());
         Context.SaveChanges();
         return Context;
        }
    }

Мой модульный тест

[Fact]
    public async Task CreateBookTest()
    {       
        var context = BookContextMock.ConfigureBookContext(new ServiceCollection());
        BookRepository repo = new BookRepository(context);
        Book book = new BookSensor()
        {
            Title = "New Book 1",
        };
        var newBook = await repo.CreateBook(book);
    }

В методе модульного теста, если я не укажу Id для объекта книги, он выдаст ошибку, сообщив, что объект с Id = 1000 уже отслежен другим юридическое лицо.

Вот мои рассуждения:

(1) Когда я создал тестовые данные автора, он создал несколько книг для каждого автора. Книги были созданы без указания идентификатора, так как поле - это столбец Идентичность для таблицы SQL.

(2) Затем я создал свои тестовые данные книги с указанным идентификатором. Тестовые данные были успешно созданы.

(3) Во время модульного теста система, похоже, запуталась в поиске следующего доступного идентификатора.

Есть ли способ исправить проблему без указания идентификатора?

Юнит-тест хорошо работает для netcore 2.1. После того, как модульный тест был преобразован в net core 3.1, возникает проблема.

Ответы [ 2 ]

0 голосов
/ 31 января 2020

Подумайте, как это работает ... вы говорите своей системе создать книгу с идентификатором 1000. вы запускаете свой метод, теперь в базе данных существует книга с идентификатором 1000. Затем вы снова запускаете метод, и он терпит неудачу, потому что, очевидно, ваша книга с идентификатором 1000 все еще находится в базе данных.

Далее вы издеваетесь над слоем базы данных и создаете сущность с идентификатором 1000 и никогда не избавляетесь от него. В следующий раз, когда вы запустите свой метод, вы, вероятно, получите проблемы с отслеживанием сущностей.

Эта вещь, которую вы называете тестом, на самом деле не является тестом, потому что нет утверждения, вы ничего не проверяете, поэтому не тест.

Это все очень сложно и неизбежно может привести к потере волос. То, что вы пытаетесь сделать, говорит мне о том, что вы действительно хотите интеграционные тесты, а не модульные тесты.

Если вы хотите модульные тесты, я бы сказал, взять EF из них и сосредоточиться на реальной функциональности, такой как бизнес-правила и * 1048. *.

Наличие интеграционных тестов - это неплохо, но если вы создаете реальные данные, в конце вызова вы также должны их очистить.

Реальный интеграционный тест будет выглядеть так:

  1. создать тестовую книгу в db
  2. выдать и проверить, что данные именно такие, как вы ожидаете, используя правильный Assert утверждения между вашими ожидаемыми данными и фактическими данными.

так:

  1. создать книгу с 1000
  2. проверить результат вызова
  3. загрузите книгу с идентификатором 1000 из базы данных
  4. используйте подтверждения, чтобы проверить, что вас волнует
  5. удалите книгу с идентификатором 1000 и все связанные данные из базы данных

нормальный поток для интеграционного теста.

Как правило, когда вы создаете данные, вы не должны сообщать своей базе данных, какой идентификатор использовать, позволить ей вставить запись и сказать, что такое уникальный идентификатор. Это решает ряд проблем, включая множественные вызовы одновременно. Не используйте функцию max в столбце для определения следующего идентификатора, который имеет проблемы с параллелизмом.

В зависимости от вашей системы, может быть лучше пометить поле идентификатора в db как идентификатор и автоинкремент, и тогда ваш ORM увидит это, и ваши слои изменятся, поэтому теперь вы не можете указывать идентификатор, когда пытаюсь что-то создать.

Однако существуют ситуации, когда необходимо указать идентификатор, и в этом случае можно использовать что-то вроде уникального идентификатора, в частности, GUID. У этого очень низкий шанс столкновения.

Я бы сказал, подумайте, что вы пытаетесь проверить. Да, вы можете издеваться над своими репозиториями и т.д. c, но если вы издеваетесь над ними, то что вы тестируете? если вы хотите проверить, правильно ли созданы ваши предметы, вам нужно go до базы данных, потому что никакие насмешки не помогут вам в этом. Вы также можете использовать базу данных в памяти, чтобы ускорить процесс и не затрагивать реальную базу данных.

Но факт остается фактом: модульные тесты связаны с функциональностью, проверкой преобразований данных и т. Д. c. Чем меньше насмешек вы можете сделать, тем лучше ваши тесты, особенно когда вы реорганизуете код.

Кроме того, старайтесь избегать ORM, если можете, думайте о вещах функционально. Вот некоторые данные, я вызываю этот метод, я должен получить этот результат. Это было бы уместно для юнит-теста.

0 голосов
/ 30 января 2020

Если вы изменили определение своего класса Book на что-то вроде:

public class Book {
    public string Id { get; set;}
    public string Title { get; set;} 
}

Вы можете добавить дополнительные Книги, сгенерировав новое руководство на лету, используя:

Guid g = Guid.NewGuid();

Тогда когда вы создаете новую книгу, просто вызовите g.ToString (), чтобы получить базовое значение и присвоить его свойству Id. Конечно, вам также придется изменить структуру таблицы вашей внутренней базы данных, чтобы поддержать это.

...