Рабочий процесс ТДД лучшие практики .Net с помощью Нанит


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

Так что я действительно пытаюсь добиться, чтобы приобрести какой-то контрольный/процесс —и вот где мне нужно, чтобы вы помочь мне— или "тест-план", который даст мне приличное покрытие кода.

Предположим идеальный случай, где мы можем начать проект с нуля скажем, помощника, Мейлер класс, который будет иметь следующий код:

(Я создал класс просто ради пособничество вопрос с примерами кода, так что любую критику или совет приветствуется и будет очень рады)

Мейлер.в CS

using System.Net.Mail;
using System;

namespace Dotnet.Samples.NUnit
{
    public class Mailer
    {
        readonly string from;
        public string From { get { return from; } }

        readonly string to;
        public string To { get { return to; } }

        readonly string subject;
        public string Subject { get { return subject; } }

        readonly string cc;
        public string Cc { get { return cc; } }

        readonly string bcc;
        public string BCc { get { return bcc; } }

        readonly string body;
        public string Body { get { return body; } }

        readonly string smtpHost;
        public string SmtpHost { get { return smtpHost; } }

        readonly string attach;
        public string Attach { get { return Attach; } }

        public Mailer(string from = null, string to = null, string body = null, string subject = null, string cc = null, string bcc = null, string smtpHost = "localhost", string attach = null)
        {
            this.from = from;
            this.to = to;
            this.subject = subject;
            this.body = body;
            this.cc = cc;
            this.bcc = bcc;
            this.smtpHost = smtpHost;
            this.attach = attach;
        }

        public void SendMail()
        {
            if (string.IsNullOrEmpty(From))
                throw new ArgumentNullException("Sender e-mail address cannot be null or empty.", from);

            SmtpClient smtp = new SmtpClient();
            MailMessage mail = new MailMessage();
            smtp.Send(mail);
        }
    }
}

MailerTests.в CS

    using System;
    using NUnit.Framework;

    namespace Dotnet.Samples.NUnit
    {
        [TestFixture]
        public class MailerTests
        {
            [Test]
            public void SendMail_FromArgumentIsNotNull_ReturnsTrue()
            {
                // Arrange
                string argument = null;
                // Act
                Mailer mailer = new Mailer(from: argument);
                // Assert
                Assert.IsNotNull(mailer.From, "parameter 'from' cannot be null.");
            }

            [Test]
            public void SendMail_FromArgumentIsNotEmpty_ReturnsTrue()
            {
                // Arrange
                string argument = String.Empty;
                // Act
                Mailer mailer = new Mailer(from: argument);
                // Assert
                Assert.IsNotEmpty(mailer.From, "parameter 'from' cannot be empty.");
            }

            [Test]
            public void SendMail_FromArgumentIsNullOrEmpty_ThrowsException()
            {
                // Arrange
                dynamic argument = null;
                // Act
                Mailer mailer = new Mailer(from: argument);
                Action act = () => mailer.SendMail();
                // Assert
                Assert.Throws<ArgumentNullException>(new TestDelegate(act));
            }
        }
    }

Так что после моих первых 2 тестов следующим очевидным шагом будет реализация функции, чтобы сделать их пройти, но я должен держать непройденные тесты и создавать новые, после выполнения кода, который будет делать те пройти, или я должен изменить существующие, сделав их пройти?

Любые советы о этой теме действительно будет чрезвычайно ценится.



3751
6
задан 29 мая 2011 в 05:05 Источник Поделиться
Комментарии
3 ответа

Важно получить настроя в режиме телетайпа заранее. Что я имею в виду

1) определить ответственность класса - это помогает в определении тестовых случаев.
2) определить взаимодействие класса с внешними зависимостями - модульное тестирование не должно быть тестирование поведение внешних зависимостей.

В данном случае - ты написал класс-оболочку для программы sendmail, задачей которого является "сбор и отправка".

Я бы сказал, что SmtpClient зависимость - у вас должна быть возможность высмеивать ее поведение в тесте.

Мейлер.в CS

using System.Net.Mail;
using System;

namespace Dotnet.Samples.NUnit
{
public class Mailer
{
readonly string from;
public string From { get { return from; } }

readonly string to;
public string To { get { return to; } }

readonly string subject;
public string Subject { get { return subject; } }

readonly string cc;
public string Cc { get { return cc; } }

readonly string bcc;
public string BCc { get { return bcc; } }

readonly string body;
public string Body { get { return body; } }

readonly string smtpHost;
public string SmtpHost { get { return smtpHost; } }

readonly string attachment;
public string Attachment { get { return Attachment; } }

private SmtpClient smtpClient;

public Mailer(string from = null, string to = null, string body = null, string subject = null, string cc = null, string bcc = null, string smtpHost = "localhost", string attachment = null, SmtpClient smtpClient= new SmtpClient())
{
this.from = from;
this.to = to;
this.subject = subject;
this.body = body;
this.cc = cc;
this.bcc = bcc;
this.smtpHost = smtpHost;
this.attachment = attachment;
}

public void SendMail()
{
if (string.IsNullOrEmpty(From))
throw new ArgumentNullException("Sender e-mail address cannot be null or empty.", from);

MailMessage mail = new MailMessage();
smtpClient.Send(mail);
}
}
}

Тестирование Программы Sendmail

[Test]
public voud ShouldCallSendOfSmtpClientWhenSendMailIsCalled
{
//Stub & Setup
var mockSmtpClient = MockRepository.GenerateMock<SmtpClient>();
var mailer = new Mailer(smtpClient: mockSmtpClient);

//Act
var message = new MailMessage();
mailer.SendMail(message);

//Assert
mockSmtpClient.AssertWasCalled(m=>m.Send(Arg<MailMessage>.Is.Anything));
}

4
ответ дан 1 июня 2011 в 05:06 Источник Поделиться

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

Просто лечить свой тестовый код вроде рабочий код. Удалить дублирования. Сохранить его простым. По сути, с модульными тестами, вы хотите как можно меньше испытаний.

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

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

Кроме того, не бойтесь несколько тестовых приспособления, чтобы сделать ваш организовать... (хотя код вроде тестировали не то)

namespace Dotnet.Samples.NUnit
{
[TestFixture]
public class NullFromAddressFixture
{
private Mailer mailer;

[SetUp]
public void Setup()
{
// Arrange
dynamic argument = null
mailer = new Mailer(from: argument);

}

[Test]
public void SendMail_FromArgumentIsNotNullOrEmpty_ReturnsTrue()
{
Assert.IsNotNullOrEmpty(mailer.From, "Parameter cannot be null or empty.");
}

[Test]
public void SendMail_FromArgumentIsNullOrEmpty_ThrowsException()
{
// Act
Action act = () => mailer.SendMail();
act.ShouldThrow<ArgumentNullException>();

// Assert
Assert.Throws<ArgumentNullException>(new TestDelegate(act));
}
}

[TestFixture]
public class EmptyFromAddressFixture
{
[Test]
public void SendMail_FromArgumentIsOfTypeString_ReturnsTrue()
{
// Arrange
dynamic argument = String.Empty;

// Act
Mailer mailer = new Mailer(from: argument);

// Assert
mailer.From.Should().Be(argument, "Parameter should be of type string.");
}

// INFO: At this first 'iteration' I've almost covered just the first argument of the method so logically this sample is nowhere near completed.
// TODO: Create a test that will eventually require the implementation of a method to validate a well-formed email address.
// TODO: Create as much tests as needed to give the remaining parameters good code coverage.
}
}

3
ответ дан 30 мая 2011 в 04:05 Источник Поделиться

Чтобы ответить на ваш вопрос прямо, вы не как тест, как только он проходит, если он сломается (вы будете часто требуется изменить настройку для данного класса, так как новые требования добавляются, но эту проблему можно решить путем выполнения тестовых классов и наследования). Дело в том, чтобы иметь набор тестов, которые проверяют ожидаемый результат, так что если ожидаемые результаты изменений, вы не должны изменить ваши тесты. И тесты должны начать тестирование на ожидаемый результат. Вы обычно не пишу 1 условие, сделать его пройти, а затем добавить дополнительные условия в одном тесте; вы либо начинаете с несколькими условиями вы тестируете, или писать дополнительные тесты.

Чтобы использовать упрощенный пример, чтобы убедиться, что я правильно понимаю, что вы спрашиваете, если вы тестировали функция Добавить(тип int арг1, арг2 Инт), и вы написали 2 теста:

[Test]
public void Add_2_plus_2_is_4()
{
Assert.AreEqual(4, Add(2, 2));
}

[Test]
public void Add_Throws_Argument_Exception_For_Negative_Number()
{
Assert.Throws<ArgumentException>(Add(2, -1));
}

Как только тесты проходят, Вы бы оставить их на месте, когда вы идете на писать модульные тесты для вычитания(тип int арг1, арг2 инт) способ. Вы не измените 2 тест, вам просто написал, чтобы вместо проверки вычесть способ. Это то, что ты просишь?

3
ответ дан 2 июня 2011 в 02:06 Источник Поделиться