Search code examples
c#repositorytddrepository-patterndenormalization

How to test repository insertions?


I'm not sure how can I test my repositories that will insert something, should they be left alone or somehow tested? If yes, then how? Do I need another layer between repositories and context?

What makes my example even more complex is that I'm using NoSQL and demoralize my data thus changing two entities on save and not one.

Example Models

public class User
{
    public ObjectId Id { get; set; }
    public string Email { get; set; }
    // ... more properties
    public List<UserUsergroupDto> Usergroups { get; set; } // Usergroup Id and Name
}

public class Usergroup
{
    public ObjectId Id { get; set; }
    public string Name { get; set; }
    // ... more properties
    public List<UsergroupUserDto> Users { get; set; } // User Id and Email
}

Example Repository

public class UserRepository : IUserRepository
{
    // Inject db context, etc..        

    public User ChangeEmailById(ObjectId id, string email)
    {
        // Save user email
        var user = _db.Users.AsQueryable().First(x => x.Id == id);
        user.Email = email;
        _db.User.Save(user);

        // Save user email on every group he is (because denormalization)
        user.Usergroups.ForEach(x =>
        {
            var usergroup = _db.Usergroups.First(z => z.Id == x.Id);
            var userDto = usergroup.Users.First(z => z.Id == user.Id);
            userDto.Email = email;

            _db.Usergroups.Save(usergroup);
        };

        return user;
    }
}

Question

How does one test such repository? If it is not testable, then how can I refactor it so it is?


Solution

  • I believe you need an integration test, rather than a unit test. With an integration test you test that your UserRepository cooperates properly with the actual persistence tier (I would also suggest to prefix UserRepository to make this dependency clearer, eg MongoUserRepository or whatever is the underlying NoSQL DB you're using).

    These integration tests should look like the following (I don't know C# so apologies for errors)

    public void testChangeUserEmailById() {
        /* setup */
        user          = nextUser(); /* using the unique generator pattern */
        expectedEmail = nextEmailAddress();
        dbSpy         = connectToTheActualNoSqlDb();
        dbSpy.insert(user);
    
        /* exercise */
        noSqlUserRepository.ChangeEmailById(user.id, expectedEmail);
    
        /* verify */
        assertEquals(expectedEmail, dbSpy.getUserEmail(user.id);
    }
    
    public void testChangeUserGroupsEmailById() {
        /* setup */
        user          = nextUser(); /* using the unique generator pattern */
        expectedEmail = nextEmailAddress();
        dbSpy         = connectToTheActualNoSqlDb();
        dbSpy.insert(user);
    
        /* exercise */
        noSqlUserRepository.ChangeEmailById(user.id, expectedEmail);
    
        /* verify */
        /* --- I've not really understood what is to be changed here --- */
    }
    

    Finally, you may need to separate unit tests from integration tests as the latter run way slower. Ideally your building system's default task should run only unit tests. A specific task can then run the integration tests. Obviously your continuous integration system runs both