The methods listed by you relate to different types of testing. Creating a database and requests for it - integration testing. Creating a database, filling it up, passing tests will all take a lot of time and you don’t want to run these tests every minute. On the other hand, these tests can be included in the build process (integration, continuous integration). If you do not have a continuous integration server, run them manually, just less often. To do this, mark them with the TestCategory attribute, or put everything in one assembly: Visual Studio will allow you to run tests from a single project or just one category.
The second option is unit testing. It is the unit tests that are run continuously and they, naturally, do not have to perform lengthy operations: access files, access network resources, and the database server. It is in these tests that mock and stub objects are used (they are fake).
Therefore, my answer is: you have to do those and other tests. Now how to do it.
Unit tests
Very often, programmers ask the question - how do I modularly test the Entity Framework? Answer: in any way, its developers write unit tests on the Entity Framework, and you write tests on your code. It is also a mistake to write complex tests that are much more complicated than your code.
public class EfUserRepository : IUserRepository { private readonly IDbContextFactory dbContextFactory; public EfUserRepository(IDbContextFactory dbContextFactory) { this.dbContextFactory = dbContextFactory; } public UserData ReadById(int id) { using (var dbContext = _dbContextFactory.Create()) { return dbContext.Users.Single(u => u.Id == id); } } }
What can you really test in this code? Immediately I want to test the work of Single and make sure that the method returns a single answer or throws an exception. But Single is not your code, moreover, for some reason, it is rather difficult to test its call, because it is an extension method, not an IQueryable interface IQueryable . But you can check that the dbContextFactory method will call Create and that the context created has a call to the Users property. Here’s how you can do this simply with the Moq library.
public void ReadById_WhenCalled_CallsDbContextFactoryCreate() { var dbContextFactoryMock = new Mock<IDbContextFactory>(); var dbContextFactory = dbContextFactoryMock.Object; var userRepository = new EfUserRepository(dbContextFactory); var user = userRepository.ReadById(1); dbContextFactoryMock.Verify(x => x.Create()); }
Integrating testing
Here you need a base and real requests. It is in integration testing that you check the code as a whole - create a record in the table, then read it and make sure that it is there.
Your question is how to fill this database, and there are two main ones here: you can either separately fill the database with test data and write tests separately, and you can create your own test data set in each test (create the necessary records in the tables). The second option seems bad - there will be a lot of unnecessary records, tests will work for a long time. But, if your project is large, this is the only solution, because you will get confused pretty quickly, because the base will be filled in one place, and use in several other places.
But now, as I understand it, you are working on a project alone and there are tables within ten to fifteen? The first option will do.
Do I need to clean the base after the tests? It is possible, but in most running projects, deleting data leads to problems. The usual solution is to run the tests on a separate database, maybe even every time a new one.
It is best to hang integration testing on macros that are similar to DEBUG , for example, DB_INTEGRATION . In the Entity Framework migrations (in the Migrations folder) lies the Configuration class, which has a Seed method. This method is called to populate the data.
internal sealed class Configuration : DbMigrationsConfiguration<DispatcherDbContext> { public Configuration() { AutomaticMigrationsEnabled = false; } protected override void Seed(DbContext context) { FillTestData(context); } [Conditional("DB_INTEGRATION")] private void FillTestData(DbContext context) { // с помощью context.AddOrUpdate добавляем тестовые записи // во все таблицы context.SaveChanges(); } }
Insert the FillTestData method and attach the Conditional attribute to it, due to which the method will be called and inserted into the assembly only if the macro definition DB_INTEGRATION .
Exactly the same attribute must be added to all integration test methods that will access this data.
[TestMethod] [TestCategory("Integration")] [Conditional("DB_INTEGRATION")] public void UserCreation() { var dbContextFactory = new DbContextFactory(); var userRepository = new EfUserRepository(dbContextFactory); var username = Utils.GenerateRandomString(200); var user1 = userRepository.Create(username); var user2 = userRepository.ReadById(user1.Id); Assert.AreEqual(user1.Name, user2.Name); }
Now go to the properties page of the test project, and on the Build tab, type DB_INTEGRATION in the list of Conditional compilation symbols . After recompiling the project, test data will appear in the database, and tests will appear in the test panel. In the Test Explorer, the left upper Group By button, select Group By Traits and the Integration category tests will be shown in a separate group that you can run. Regular unit tests will be in the No Traits group, so it will be convenient to run them separately from integration tests and often.
Can integration testing be done fast?
Now, if you really want to make a real test of calls to the database, but that worked quickly? Yes, it is possible.
The second question: unit tests should work even if you do not have any external services and databases, is it possible to conduct testing without an external database server? Also yes.
Many modern DBMSs allow storing databases in memory. Some DBMSs are specifically designed to emulate the operation of an external server, although they are built into your application, see for example SQLite and SQL Server Compact Edition.
These tests will still not be modular in essence, but in terms of speed they will be at the modular level, and they can be run frequently.
Start exploring the issue with http://weblogs.asp.net/scottgu/vs-2010-sp1-and-sql-ce https://www.nuget.org/packages/EntityFramework.SqlServerCompact/