Как и сейчас, BizLogin
не тестируется, так как он непосредственно создает экземпляр BizPermission
, который, в свою очередь, создает экземпляр PermissionDal
, который затем попадает в БД.
Лучшим решением будет рефакторинг BizLogin
для замены прямой реализации BizPermission
вызовом фабрики (метода) или Внедрение зависимостей . Из вашего поста мне не ясно, можете ли вы реорганизовать код - если это так, это предпочтительное решение.
Однако, если рефакторинг не возможен, вы все равно можете попробовать неприятный трюк. Это возможно в Java, я не очень хорошо знаю C #, но, поскольку эти два языка довольно похожи, я думаю, это возможно и в C # (хотя я не могу заполнить точные технические детали).
Вы можете заменить скомпилированные файлы классов BizPermission
на различные имитационные реализации для ваших модульных тестов. Это, конечно, рискованно, так как вы должны убедиться, что альтернативные реализации не смешиваются с вашими производственными сборками. Также это требует немного возиться с classpath и прочим. Так что попробуйте только если рефакторинг действительно, совершенно исключен.
Как заменить файлы классов тестовыми реализациями
(с использованием терминологии Java - я надеюсь, что это достаточно ясно и для C # ...) Основная идея состоит в том, что среда выполнения ищет классы на пути к классам и загружает первое подходящее определение класса, которое находит на пути к классам. Таким образом, вы можете создать фиктивную реализацию BizPermission
в исходной папке вашего модульного теста, в том же пакете и с тем же интерфейсом, что и в оригинале. Затем скомпилируйте его, например, в папка test-classes
(тогда как ваш производственный код скомпилирован, например, classes
). Теперь, если вы настроили свой путь к классу теста таким образом, чтобы test-classes
предшествовал classes
, среда выполнения загрузит поддельный класс BizPermission
при запуске ваших тестов, а не исходный, когда BizLogin
попытается создать экземпляр этого класса.
Пример рефакторинга для использования фабричного метода
public class BizLogin
{
private readonly ILoginDal _login;
public BizLogin(ILoginDal login)
{
_login = login;
}
protected BizPermission getBizPermission()
{
return new BizPermission();
}
public void Login(string userName, string password, out User user)
{
var bizPermission = getBizPermission();
var permissionList = bizPermission.GetPermissions(userName);
//Method I am actually testing
_login.login(userName,password,out user);
}
}
В тестовом коде:
public class FakeBizPermission implements BizPermission
{
public List<Permission>GetPermissions(string userName)
{
// produce and return fake permission list
}
}
public class BizLoginForTest
{
public BizLoginForTest(ILoginDal login)
{
super(login);
}
protected BizPermission getBizPermission()
{
return new FakeBizPermission();
}
}
Таким образом, вы можете протестировать критически важные функции с помощью BizLoginForTest
с минимальными изменениями по сравнению с исходным BizLogin
классом.
Альтернативой может быть внедрение полного фабричного объекта, как упомянуто в комментарии Джеффа. Это потребовало бы немного больше изменений кода (возможно, в том числе на клиентах BizLogin
), так что это немного более навязчиво.
Обратите внимание, что главная цель таких рефакторингов - всегда разрешать модульное тестирование, а не выигрывать приз за красоту вашего нового дизайна :-) Так что лучше всего начать с наименьших изменений в существующем коде, которые позволяют вам покрыть функциональность тестами. После того, как вы проведете тесты, вы сможете с большей уверенностью провести рефакторинг в сторону более чистого и лучшего дизайна.