Moq: Как издеваться над классом, который не виден? - PullRequest
3 голосов
/ 27 августа 2010

У меня есть следующий упрощенный код, который описывает мою проблему:

public interface IMyUser
{
    int Id { get; set; }
    string Name { get; set; }
}

Который используется в слое доступа к данным следующим образом:

public interface IData
{
    T GetUserById<T>(int id) where T : IMyUser, new();
}

Класс userlogic определяется следующим образом:

public class UserLogic
{
    private IData da;

    public UserLogic(IData da)
    {
        this.da = da;
    }

    public IMyUser GetMyUserById(int id)
    {
        return da.GetUserById<MyUser>(id);
    }
}

Userlogic использует класс MyUSer , который виден только внутри.

Я хочу использовать Moq, чтобы высмеивать вызов слоя dataaccess . Но из-за того, что я не могу получить доступ к классу MyUser из кода моего модульного теста (который разработан), я не знаю, как настроить moq? ​​

Код Moq должен выглядеть примерно так:

var data = new Mock<IData>();
data.Setup(d => d.GetUserById<MyUser ???>(1)).Returns(???);

var logic = new UserLogic(data.Object);
var result = logic.GetMyUserById(1);

Как это решить?

Ответы [ 3 ]

2 голосов
/ 30 августа 2010

Позвольте мне подробнее остановиться на ответе Шёрда. Проблема, с которой вы сталкиваетесь, связана с невозможностью доступа к типу MyUser из тестовой сборки. Эта проблема легко решается с помощью InternalsVisibleTo атрибута сборки.

Однако я бы порекомендовал переосмыслить ваш дизайн и избавиться от интерфейса IMyUser, а вместо этого просто использовать класс MyUser (который должен быть общедоступным). Обычно вы размещаете сервисы за интерфейсами, а не объектами. Есть ли веские причины для предоставления нескольких реализаций IMyUser?

Посмотрите, насколько чище эта реализация:

public interface IData
{
    MyUser GetUserById(int id);
}

public class UserLogic
{
    private IData da;

    public UserLogic(IData da)
    {
        this.da = da;
    }

    public MyUser GetMyUserById(int id)
    {
        return da.GetUserById(id);
    }
}

internal class MyUser {
    int Id { get; set; }
    string Name { get; set; }
}

Существует другое решение, если вы настаиваете на наличии интерфейса IMyUser и его внутренней реализации. Ваше существующее решение, если я правильно выведу содержание IData.GetUserById<T>, будет выглядеть примерно так:

public class UserData : IData {
    T GetUserById<T>(int id) where T : IMyUser, new(){
       T returned = new T();
       //fill in properties
       returned.Name = "test";
       return returned;
    }
}

Приведенный выше код является небольшим нарушением SRP (предупреждение, PDF) и сочетает в себе две обязанности - извлечение объекта из постоянного хранилища и создание экземпляра объекта. Мало того, он также накладывает ответственность за создание интерфейса, что еще хуже.

Разделение этих обязанностей с использованием Abstract Factory и Внедрение зависимостей (PDF) приведет к намного более чистому дизайну, который не страдает от той же проблемы, что и раньше.

public interface IMyUserFactory {
    IMyUser Create();
}

public interface IData
{
    IMyUser GetUserById(int id);
}

internal MyUserFactory : IMyUserFactory {
   public IMyUser Create() {return new MyUser();}
}

internal class UserData : IData {

    IMyUserFactory m_factory;
    public UserData(IMyUserFactory factory) {
       m_factory = factory;
    }

    public IMyUser GetUserById(int id) {
       IMyUser returned = m_factory.Create();
       //fill in properties
       returned.Name = "test";
       return returned;
    }
}

//and finally UserLogic class
public class UserLogic
{
    private IData da;

    public UserLogic(IData da)
    {
        this.da = da;
    }

    public IMyUser GetMyUserById(int id)
    {
        return da.GetUserById(id);
    }
}

//The test then becomes trivial
[TestMethod]
public void Test() {
  var data = new Mock<IData>();
  data.Setup(d => d.GetUserById(1)).Returns(new Mock<IMyUser>().Object);

  var logic = new UserLogic(data.Object);
  var result = logic.GetMyUserById(1);
}
2 голосов
/ 27 августа 2010

Не можете ли вы использовать

da.GetUserById<IMyUser>(id);

вместо

da.GetUserById<MyUser>(id);
1 голос
/ 27 августа 2010

Если я хочу скрыть функциональность, но пусть она будет тестируемой, я объявлю функции как internal, а затем в верхней части файла добавлю атрибут [assembly: InternalsVisibleTo("MyAssemblyName")], где MyAssemblyName - модульный тестсборка, к которой вы хотите предоставить доступ.Спасибо, Стеф, за указание на мою предыдущую ошибку.

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