Как протестировать бизнес-логику (правила модели Doman) при использовании структуры сущностей ADO.net? - PullRequest
1 голос
/ 08 октября 2009

Я пытаюсь проверить, что бизнес-правило не разрешает разделять одно и то же пространство с пользователем дважды. Ниже приведен метод тестирования. Строка с проблемой отмечена ниже.

public void ShareSpace(string spaceToShare,string emailToShareIt)
{
  SharedSpace shareSpace = new SharedSpace();
  shareSpace.InvitationCode = Guid.NewGuid().ToString("N");
  shareSpace.DateSharedStarted = DateTime.Now;
  shareSpace.Expiration = DateTime.Now.AddYears(DefaultShareExpirationInYears);
  shareSpace.Active = true;
  shareSpace.SpaceName = spaceToShare;
  shareSpace.EmailAddress = emailToShareIt;
  if (!this.MySpacesShared.IsLoaded) 
     this.MySpacesShared.Load(); //Here I am getting the exception further below.

  if (this.MySpacesShared.Any(s => (s.EmailAddress == emailToShareIt) 
                              & (s.SpaceName == spaceToShare)))
    throw new InvalidOperationException("Cannot share the a space with a user twice.");
  else
    this.MySpacesShared.Add(shareSpace);
}

Метод Test ниже:

[TestMethod]
public void Cannot_Share_SameSpace_with_same_userEmail_Twice()
{
    account.ShareSpace("spaceName", "user1@domain.com");
    try
    {
          account.ShareSpace("spaceName", "user1@domain.com");
          Assert.Fail("Should throw exception when same space is shared with same user.");
    }
    catch (InvalidOperationException)
    { /* Expected */ }
    Assert.AreEqual(1, account.MySpacesShared.Count);
    Assert.AreSame(null, account.MySpacesShared.First().InvitedUser);
}

Ошибка, которую я получаю по результатам теста:

Метод испытания SpaceHelper.Tests.Controllers.SpaceControllerTest.Cannot_Share_SameSpace_with_same_userEmail_Twice бросил исключение: System.InvalidOperationException: EntityCollection не может быть загружен потому что он не привязан к ObjectContext ..

Когда я пошагово описываю механизм отладки, эта ошибка появляется в событии Load (). Я почти уверен, что это связано с тем фактом, что в моем тестовом сценарии у меня нет ADO.NET Entity Framework, поскольку я использую здесь ложную информацию и не подключен к своей базе данных.

Если кто-то захочет увидеть здесь, это моя инициализация этого теста:

[TestInitialize()]
 public void MyTestInitialize() 
 {
     user = new User()
     {
         Active = true,
         Name = "Main User",
         UserID = 1,
         EmailAddress = "user1@userdomain.com",
         OpenID = Guid.NewGuid().ToString()
     };

     account = new Account()
     {
         Key1 = "test1",
         Key2 = "test2",
         AccountName = "Brief Account Description",
         ID = 1,
         Owner = user
     };
 }

Ответы [ 3 ]

0 голосов
/ 08 октября 2009

Лучший подход состоит в том, что вы не должны касаться классов, сгенерированных платформой Entity в вашем доменном слое. Вместо этого вы должны создать свой собственный бизнес-уровень и использовать LINQ для проецирования сгенерированных классов на ваш бизнес-объект.

Таким образом, вы можете упростить настройку теста.

0 голосов
/ 08 октября 2009

Я не использовал Entity Framework, и я не уверен на 100%, что знаю все, что здесь происходит, но то, что вам нужно сделать, - это поместить оболочку в код вашей структуры Entity Framework, который имеет интерфейс, а затем использовать фальшивый фреймворк, притворяясь, что вы вызываете базу данных, а на самом деле это не так. Я дам вам общую идею, но вам придется применить ее к Entity Framework, поскольку я не знаю специфики.

public interface IShareSpaceGateway {
  IEnumerable<ShareSpace> GetSharedSpaces(string spaceToShare, string emailToShareIt);
}

public class ShareSpaceGatewayEF: IShareSpaceGateway
{
  // MySpacesShared should be included up here, not sure what type it is
  public IEnumerable<ShareSpace> GetSharedSpaces(string spaceToShare, string emailToShareIt)
  {
    if (!this.MySpacesShared.IsLoaded) 
     this.MySpacesShared.Load();

    return this.MySpacesShared.Any(s => (s.EmailAddress == emailToShareIt) 
                              & (s.SpaceName == spaceToShare));
  }
}

Вы можете поместить любой метод, который вам нужен, в ISharedSpaceGateway, который имеет смысл. Идея состоит в том, чтобы уменьшить количество повторений кода.

Теперь вы хотите иметь возможность внедрить вашу новую зависимость на IShareSpaceGateway. Лучший способ работы с внедрением зависимостей - использовать DI-контейнер, такой как Castle Windsor, Structure Map, Ninject или Unity. Я предполагаю, что ваш код может выглядеть здесь:

public class Account
{
  private ISharedSpaceGateway _sharedSpaceGateway;
  public Account(ISharedSpaceGateway sharedSpaceGateway)
  {
    _sharedSpaceGateway = sharedSpaceGateway;
  }

  public int ID { get; set; }
  public string Key1 { get; set; }
  public string Key2 { get; set; }
  public string AccountName { get; set; }

  public void ShareSpace(string spaceToShare,string emailToShareIt)
  {
    SharedSpace shareSpace = new SharedSpace();
    shareSpace.InvitationCode = Guid.NewGuid().ToString("N");
    shareSpace.DateSharedStarted = DateTime.Now;
    shareSpace.Expiration = DateTime.Now.AddYears(DefaultShareExpirationInYears);
    shareSpace.Active = true;
    shareSpace.SpaceName = spaceToShare;
    shareSpace.EmailAddress = emailToShareIt;
    var sharedSpaces = sharedSpaceGateway.GetSharedSpaces(spaceToShare, emailToShareIt);
    if(sharedSpaces.Count() > 0)    
      throw new InvalidOperationException("Cannot share the a space with a user twice.");

    this.MySpacesShared.Add(shareSpace);
  }
}

Теперь, в вашем модульном тесте, вы хотите использовать макет, такой как Moq или RhinoMocks, для настройки вашего теста. В своем тесте вы не хотите использовать вашу реальную реализацию SharedSpaceGateway, вы хотите передать поддельную. В этом примере используется RhinoMocks

public class TestFixture{


private ISharedSpaceGateway gateway;
[TestInitialize()]
 public void MyTestInitialize() 
 {
    gateway = MockRepository.CreateMock<ISharedSpaceGateway>();
    gateway.Expect(g => g.GetSharedSpaces("spaceName", "user1@domain.com"))
          .Return(new SharedSpace()); // whatever you want to return from the fake call

         user = new User()
         {
                 Active = true,
                 Name = "Main User",
                 UserID = 1,
                 EmailAddress = "user1@userdomain.com",
                 OpenID = Guid.NewGuid().ToString()
         };

         account = new Account(gateway) // inject the fake object
         {
                 Key1 = "test1",
                 Key2 = "test2",
                 AccountName = "Brief Account Description",
                 ID = 1,
                 Owner = user
         };
 }

[TestMethod]
public void Cannot_Share_SameSpace_with_same_userEmail_Twice()
{
        account.ShareSpace("spaceName", "user1@domain.com");
        try
        {
          account.ShareSpace("spaceName", "user1@domain.com");
          Assert.Fail("Should throw exception when same space is shared with same user.");
        }
        catch (InvalidOperationException)
        { /* Expected */ }
        Assert.AreEqual(1, account.MySpacesShared.Count);
        Assert.AreSame(null, account.MySpacesShared.First().InvitedUser);
        gateway.VerifyAllExpectations();
}

Существует множество аспектов использования DI-фреймворков и макетов, но эти концепции делают ваш код намного более тестируемым.

0 голосов
/ 08 октября 2009

Исходя из моего личного опыта, LINQ to SQL отнимает много времени, когда речь идет о написании модульных тестов.

Ваш уровень BL тесно связан с классами LINQ to SQL в том смысле, что он знает такие понятия, как .IsLoaded, может .Load() коллекции и т. Д. Переместите эту логику в ISharedSpacesPersistenceService и переписайте свой метод следующим образом:

// Dependency-Inject this
public ISharedSpacesPersistenceService SharedSpacesPersistenceService { get; set; }

public void ShareSpace(string spaceToShare,string emailToShareIt)
{
    SharedSpace shareSpace = new SharedSpace();
    shareSpace.InvitationCode = Guid.NewGuid().ToString("N");
    shareSpace.DateSharedStarted = DateTime.Now;
    shareSpace.Expiration = DateTime.Now.AddYears(DefaultShareExpirationInYears);
    shareSpace.Active = true;
    shareSpace.SpaceName = spaceToShare;
    shareSpace.EmailAddress = emailToShareIt;

    if(SharedSpacesPersistenceService.ContainsSpace(s.EmailAddress, spaceToShare)
        throw new InvalidOperationException("Cannot share the a space with a user twice.");     

    this.MySpacesShared.Add(shareSpace);
}

И просто придирка: замените DateTime.Now.AddYears(DefaultShareExpirationInYears) на DateTime.Now.Add(DefaultShareExpiration) и установите DefaultShareExpiration type на TimeSpan. Это будет намного лучше.

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