Ваш опыт очень похож на мой. В то время как я продан на TDD и не поступил бы иначе, я, конечно, понимаю вашу путаницу. Важно помнить, что TDD - это философия дизайна. При этом, я думаю, я могу помочь устранить некоторые ваши разочарования.
Начните с размышлений о том, чего вы пытаетесь достичь, не на уровне отдельного теста, а в том, что вы пытаетесь сделать. Если ваша задача (пользовательская история) включает в себя получение некоторых учетных данных и попытку аутентификации текущего пользователя на основе этих учетных данных, то начните с этого и продолжайте свой путь вниз. Вы, кажется, движетесь в этом направлении, только застревая на следующих шагах
При работе над отдельным тестом думайте об этом с точки зрения ожидаемого поведения , а не просто проверяйте некоторые входы и выходы. Думайте о себе, как об использовании этого компонента, и просто напишите строку кода так, как вы хотите, чтобы она была написана. Пусть эта часть поможет управлять интерфейсом / контрактом вашего сервиса. Вы должны задать себе вопрос: «Если бы я назвал этот метод, как бы я узнал, что он работает? Что бы я ожидал от него?» Это определит, какие утверждения вам нужно сделать.
Определите, каковы ваши внешние зависимости, и вместо этого используйте абстракции (принцип инверсии зависимости). Если эти зависимости являются чем-то, о чем вы заботитесь как часть проверки своего поведения, тогда используйте внедрение зависимостей , чтобы вы могли использовать mock в своем тесте.
Всегда, всегда, всегда следуйте этому порядку [Напишите ваш тест, посмотрите, как он провалится, код для прохождения, рефакторинг]. ИЗУЧИТЕ У МОЕЙ ОШИБКИ !!! Поверь мне, это не подлежит обсуждению. В противном случае вы можете обвинить свои проблемы в TDD, если он не используется должным образом.
Хорошо, поэтому, собрав все это вместе с вашим примером и некоторыми тестовыми примерами от lance , мы могли бы сделать что-то вроде этого:
[Test]
public void ShouldAuthenticateValidUser()
{
IMyMockDa mockDa = new MockDataAccess();
var service = new AuthenticationService(mockDa);
mockDa.AddUser("Name", "Password");
Assert.IsTrue(service.DoLogin("Name", "Password"));
//Ensure data access layer was used
Assert.IsTrue(mockDa.GetUserFromDBWasCalled);
}
[Test]
public void ShouldNotAuthenticateUserWithInvalidPassword()
{
IMyMockDa mockDa = new MockDataAccess();
var service = new AuthenticationService(mockDa);
mockDa.AddUser("Name", "Password");
Assert.IsFalse(service.DoLogin("Name", "BadPassword"));
//Ensure data access layer was used
Assert.IsTrue(mockDa.GetUserFromDBWasCalled);
}
Хорошо, так что там много чего происходит, и, возможно, многое для исследования. Однако вы можете начать видеть, как можно провести тщательное тестирование, используя лучший дизайн. В вышеприведенных примерах важно отметить, что Mock Object настраивается, но вам не нужно проходить через всю эту боль. Там есть много насмешливых рамок. Например, используя RhinoMocks , ваш тест будет выглядеть так:
[Test]
public void ShouldAuthenticateValidUser()
{
var mockRepo = new MockRepository();
var mockDa = mockRepo.DynamicMock<IMyMockDa>();
var service = new AuthenticationService(mockDa);
using(mockRepo.Record())
{
//I realize this is a terrible method and should not exist if you
// care about security, but for demonstration purposes...
Expect.Call(mockDa.GetPassword("User")).Return("Password");
}
using(mockRepo.Playback())
{
Assert.IsTrue(service.DoLogin("User", "Password"));
}
}
Привыкайте сначала делать вещи вручную, чтобы вы поняли концепции, а затем переходите к использованию фреймворка. Уф! Много информации, но, как видите, TDD - это целая философия дизайна. Однако это приведет к более чистому коду, лучшему дизайну и меньшему количеству ошибок.