예제 #1
0
        public static void Main()
        {
            // This code shows that both service implementations have access to the database and
            // that they still fulfill the same interface contract
            IReportingService untestableReportingService = new Problem.ReportingService();
            Console.WriteLine(
                "Students enrolled, queried via the untestable service implementation: {0}",
                untestableReportingService.GetNumberOfEnrolledStudents());

            IReportingService testableReportingService = new Solution.ReportingService();
            Console.WriteLine(
                "Students enrolled, queried via the testable service implementation: {0}",
                testableReportingService.GetNumberOfEnrolledStudents());

            Console.ReadKey();
        }
예제 #2
0
        public void AlasHowDoYouTestThisWithoutAWorkingDatabaseConnection()
        {
            // Arrange

            // Step one:
            // Arrange the test data the linq statements within the ReportingService instance should run against.
            var studentWithEnrollment = new Student
                                            {
                                                Enrollments = new List<Enrollment>
                                                                  {
                                                                      new Enrollment()
                                                                  }
                                            };

            var studentWithoutEnrollment = new Student
                                               {
                                                   Enrollments = new List<Enrollment>()
                                               };

            var testStudents = new List<Student>
                               {
                                   studentWithEnrollment,
                                   studentWithoutEnrollment
                               }.AsQueryable(); // Step two: Make the test data available as an IQueryable
                                                // or in other words: As the type that linq runs its queries against.

            // At this point we can already predict how many students should be counted as enrolled
            // -> We put one student with enrollments and one student without enrollments into our set of test data,
            //    so once we hook the ReportingService up with the test data, the answer to GetNumberOfEnrolledStudents()
            //    should be 1.
            var expectedNumberOfEnrolledStudents = 1;

            // Step three:
            // Create a mock DbSet
            var mockStudentsDbSet = new Mock<DbSet<Student>>();
            // A DbSet implements IQueryable to allow the execution of linq queries against the DbSet itself.
            // That's good news, because that means that we can just hook up our selfmade IQueryable 'students'
            // to our mocked DbSet, which means that any time a linq query is executed against our mocked DbSet it really
            // gets executed against our selfmade IQueryable 'students' which - as you might remember - contains exactly
            // the test data we want it to contain and does certainly not require any database connection whatsoever.
            //
            // The code to hook up our IQueryable to the mocked DbSet is kinda ugly though...
            mockStudentsDbSet.As<IQueryable<Student>>().Setup(queryable => queryable.Provider).Returns(testStudents.Provider);
            mockStudentsDbSet.As<IQueryable<Student>>().Setup(queryable => queryable.Expression).Returns(testStudents.Expression);
            mockStudentsDbSet.As<IQueryable<Student>>().Setup(queryable => queryable.ElementType).Returns(testStudents.ElementType);
            mockStudentsDbSet.As<IQueryable<Student>>().Setup(queryable => queryable.GetEnumerator()).Returns(testStudents.GetEnumerator);

            // Step four:
            // Next we need to mock the DbContext or in our case the SchoolContext. We can't simply replace the Students
            // DbSet in an original SchoolContext because the DbContext part would still try to open a connection to the
            // database - which our unit test does not have access to.
            var mockSchoolContext = new Mock<SchoolContext>();
            // Step five:
            // Hook out mocked students DbSet into out mocked SchoolContext so any call against mockSchoolContext.Students
            // gets redirected to our mocked students DbSet.
            mockSchoolContext.Setup(db => db.Students).Returns(mockStudentsDbSet.Object);
            // Important: The DbSet-Properties within the DbContext / SchoolContext need to be declared as
            // virtual or Moq will be unable to mock the properties.
            // i.e.
            //   public virtual DbSet<Student> { get; set; }
            // instead of
            //   public DbSet<Student> { get; set; }
            // Database first entity framework automatically marks the properties as virtual.
            // If you use the entity framework code first approach you have to ensure this yourself.

            // Step six:
            // Now  we need to hook up our ReportingService with our mocked SchoolContext. This can be achieved via
            // dependency injection. This example uses a simple form of constructor injection. Depending on your
            // situation a more complex solution might be required.
            IReportingService reportingService = new ReportingService(mockSchoolContext.Object);

            // And that's it! We're done!
            // The following code now executes against our test IQueryable and returns the result predicted
            // above. Through mocking and decoupling we are now able to test any linq statements written against
            // entity framework while completely ignoring the underlying database.

            // Act
            var actualNumberOfEnrolledStudents = reportingService.GetNumberOfEnrolledStudents();

            // Assert
            Assert.AreEqual(expectedNumberOfEnrolledStudents, actualNumberOfEnrolledStudents);
        }