Я изо всех сил пытаюсь разобраться в модульном тестировании. Я следил за примерами как из ужина Nerd, так и из профессионального фреймворка asp.net MVC, но как только я попробую свой собственный, быстро застрял. В качестве теста я попытался создать класс проверки, который просто использует информацию для входа - имя пользователя и пароль для возврата пользователя из репозитория. Я использовал этот пример, так как довольно просто представить, какие тесты можно выполнить: Is_Password_Valid, Is_Username_Valid и т. Д.

Я слишком долго пытался понять это. Может ли кто-нибудь привести пример того, как они могут подойти к этому как к модульному тесту? Думаю, как только я раскрою это, я уйду.

//Arrange
string email = "test@test.com";
string password = "test";

//Arrange
List<Customer> customer = new List<Customer>();

customer.Add(new Customer { CustomerId = 1, Email = email, Password = "best", FirstName = "test", LastName = "wods", Sex = true });
mockRepos = new Moq.Mock<ICustomerRepository>();
mockRepos.Setup(x => x.GetCustomerByPasswordUsername(email, password)).Returns(customer.First());
Authenticate auth = new Authenticate(mockRepos.Object);

//Act
var result = auth.Login(email, password);

//Assert
//this is where I start to become unstuck??????
4
hoakey 12 Дек 2010 в 20:11

2 ответа

Лучший ответ

Похоже, вы на правильном пути, но позвольте мне попытаться объяснить, как бы я прошел тест.

Фактическая тестируемая система (SUT) - это Authenticate класс. Вы мало что рассказали об этом, поэтому я предполагаю следующее:

Он использует экземпляр ICustomerRepository для определения существования пользователя на основе комбинации имени пользователя (электронной почты) и пароля.

Когда репозиторий возвращает экземпляр Customer, учитывая комбинацию имени пользователя и пароля, метод Login возвращает true. Когда репозиторий возвращает null, метод Login возвращает false.

Я буду использовать эти предположения в дальнейшем, но если они неверны, я уверен, что вы сможете изменить тесты так, чтобы они имели смысл для вашего сценария.

Тест 1. Если комбинация имени пользователя и пароля верна, Login вернет true

public void LoginWillReturnTrueForAValidUsernamePasswordCombination()
{
    string email = "test@test.com";
    string password = "test";

    //Dummy customer
    var customer = new Customer();

    //Create mock
    var mockRepos = new Moq.Mock<ICustomerRepository>();
    mockRepos.Setup(x => x.GetCustomerByPasswordUsername(
            It.Is<string>(s => s == email), 
            It.Is<string>(s => s == password))
        .Returns(customer);

    var auth = new Authenticate(mockRepos.Object);

    //Act
    var result = auth.Login(email, password);

    //Assert
    Assert.IsTrue(result);
}

Обратите внимание на использование It.Is. По сути, макет настроен таким образом, что он будет возвращать фиктивный объект клиента только тогда, когда адрес электронной почты и пароль, определенные в вашем тесте, передаются методу GetCustomerByPasswordUsername.

Тест 2. Если комбинация имени пользователя и пароля неверна, Login вернет false

public void LoginWillReturnFalseForAnInvalidUsernamePasswordCombination()
{
    string email = "test@test.com";
    string password = "test";

    //Create mock
    var mockRepos = new Moq.Mock<ICustomerRepository>();
    mockRepos.Setup(x => x.GetCustomerByPasswordUsername(
            It.Is<string>(s => s == email), 
            It.Is<string>(s => s == password))
        .Returns<Customer>(null);

    var auth = new Authenticate(mockRepos.Object);

    //Act
    var result = auth.Login(email, password);

    //Assert
    Assert.IsFalse(result);
}

Несмотря на неявное тестирование с помощью вышеуказанных тестов, вы можете пойти дальше и написать тест, который гарантирует, что метод Login передает правильные параметры в репозиторий. Такой тест мог бы выглядеть следующим образом:

Тест 3: при входе в репозиторий будет правильно запущен вход

public void LoginWillInvokeGetCustomerByPasswordUsernameCorrectly()
{
    string email = "test@test.com";
    string password = "test";

    //Create mock
    var mockRepos = new Moq.Mock<ICustomerRepository>();
    mockRepos.Setup(x => x.GetCustomerByPasswordUsername(
            It.Is<string>(s => s == email), 
            It.Is<string>(s => s == password))
        .Returns<Customer>(null)
        .Verifiable();

    var auth = new Authenticate(mockRepos.Object);

    //Act (ignore result. We are only testing correct invocation)
    auth.Login(email, password);

    //Assert
    mockRepos.Verify();
}

Метод имитации Verify генерирует исключение, если настроенные методы не были вызваны.

Надеюсь, это поможет. Не стесняйтесь спрашивать, есть ли у вас дополнительные вопросы.

14
Klaus Byskov Pedersen 12 Дек 2010 в 20:39
Привет, Клаус. Это великолепная информация. Какой исчерпывающий ответ. Большое спасибо!
 – 
hoakey
12 Дек 2010 в 21:07

Если у вас есть тестируемая система, которая использует API, к которому ваша SUT также добавляет слушателя, это также может стать немного сложнее (вы можете сделать то же самое с исполнителями и таймерами (имитируйте и то, и другое)).

class RealAPIYouSUTUses {
      public void addListener(XXXListener l);
}

class MockAPI implements RealAPIYourSUTUses{
      public void addListener(XXXListener l) {
         this.cachedListener = l;
      }
      public XXXListener getListener() {
         return cachedListener;
      } 
}

Затем ваш модульный тест может протестировать листезнер и его взаимодействие с системой ...

class Test {
    public void test() {
        MockAPI mockAPI = new MockAPI();   
        SUT sut = new SUT(mockAPI);  

        sut.doSomething();
        //expect your listener to be added from calling doSomething....
        XXXListener l = mockApi.getListener();

        //simulate firing an event into your SUT 
        l.fireEvent(new Event("asdf"));
    }
}

Самое интересное во всем этом то, что вы можете имитировать Executor.java и Timer.java, и ваш тест может захватить исполняемый файл, который был передан исполнителю, и TimerTaks для таймера, и вы можете запустить их в порядке и в обратном порядке, чтобы убедиться, что ваша система работает.

Я занимаюсь асинхронным программированием и модульным тестированием, как эта скала, чтобы вы могли убедиться, что интеграция между всеми вашими классами работает.

0
Dean Hiller 15 Дек 2011 в 22:52