новый тест приводит к сбою старых тестов, я делаю это неправильно? - PullRequest
1 голос
/ 13 октября 2011

У меня есть модульные тесты, которые проверяют, не указано ли имя ребенка, а затем не сохраняют ребенка, а другой - если имя указано, то следует вызвать save. Я сделал красный / зеленый / рефакторинг, и это прошло. Я добавил новые тесты, которые делают то же самое для фамилии. Теперь тесты для имени не пройдены, потому что фамилия не указана. Я предоставил тестовый код ниже. Меня интересует, правильно ли я поступаю по этому поводу, или я просто должен исправить неверные тесты?

Кроме того, необходим интерфейс валидатора, потому что действительные изменения зависят от клиента, использующего программное обеспечение, в противном случае я бы кодировал эти проверки в сам класс Baby.

Обновление: Судя по немногим ответам, которые я уже получил, кажется, что я поступаю неправильно. Что я должен делать вместо этого, чтобы эта ситуация не возникала?

    [TestMethod]
    public void baby_is_not_saved_if_validation_fails() {
        // arange
        var validator = new Mock<IValidator<Baby>>();
        var output = new ValidationCollection();
        validator.Setup(v => v.IsValid(It.IsAny<Baby>(), out output)).Returns(false);
        var unitOfWork = GetMock();
        // act
        var b = new Baby();
        var svc = new BabyService(validator.Object, unitOfWork.Object);
        svc.AddNewBaby(b);
        // assert
        unitOfWork.Verify(u => u.SaveChanges(), Times.Never());
    }

    [TestMethod]
    public void baby_is_saved_if_validation_passes() {
        // arange
        var validator = new Mock<IValidator<Baby>>();
        var output = new ValidationCollection();
        validator.Setup(v => v.IsValid(It.IsAny<Baby>(), out output)).Returns(true);
        var unitOfWork = GetMock();
        // act
        var b = new Baby();
        var svc = new BabyService(validator.Object, unitOfWork.Object);
        svc.AddNewBaby(b);
        // assert
        unitOfWork.Verify(u => u.SaveChanges(), Times.Once());
    }

    [TestMethod]
    public void if_first_name_is_not_supplied_baby_is_not_added() {
        // arrange
        var validator = new DefaultBabyValidator();
        var unitOfWork = GetMock();
        // act
        var b = new Baby();
        var svc = new BabyService(validator, unitOfWork.Object);
        svc.AddNewBaby(b);
        // assert
        unitOfWork.Verify(u => u.SaveChanges(), Times.Never());
    }

    Mock<IHealthUnitOfWork> GetMock() {
        var uow = new Mock<IHealthUnitOfWork>();
        var dbSet = new Mock<IDbSet<Baby>>();
        dbSet.Setup(db => db.Add(It.IsAny<Baby>())).Returns(new Baby());
        uow.Setup(u => u.SaveChanges()).Verifiable();
        uow.SetupGet(u => u.Babies).Returns(dbSet.Object);

        return uow;
    }

    [TestMethod]
    public void if_first_name_is_supplied_baby_is_added() {
        // arrange
        var validator = new DefaultBabyValidator();
        var unitOfWork = GetMock();
        // act
        var b = new Baby { FirstName = "Charles" };
        var svc = new BabyService(validator, unitOfWork.Object);
        svc.AddNewBaby(b);
        // assert
        unitOfWork.Verify(u => u.SaveChanges(), Times.Once());
    }

    [TestMethod]
    public void if_last_name_is_not_supplied_baby_is_not_added() {
        // arrange
        var validator = new DefaultBabyValidator();
        var unitOfWork = GetMock();
        // act
        var b = new Baby { FirstName = "Charles" };
        var svc = new BabyService(validator, unitOfWork.Object);
        svc.AddNewBaby(b);
        // assert
        unitOfWork.Verify(u => u.SaveChanges(), Times.Never());
    }
}

Ответы [ 2 ]

2 голосов
/ 13 октября 2011

Ваша проблема в том, что ваши тесты представляют противоречивые требования. Если имя указано, а фамилия - нет, тогда if_first_name_is_supplied_baby_is_added () говорит, что ребенок должен быть сохранен, а if_last_name_is_not_supplied_baby_is_not_added () говорит, что ребенок не должен быть сохранен.

0 голосов
/ 13 октября 2011

Проблема в том, что в исходном тесте имени не было спецификаций для фамилии в то время. Когда ваши спецификации изменятся, тогда ваши тесты могут измениться. Я провела рефакторинг своих тестов, так что мне не нужно менять предыдущие тесты, так как в классе Baby обнаружены новые поля. Теперь у меня есть один метод испытаний, который устанавливает истинное условие для правильного ребенка. Обязательные полевые испытания теперь проверяют следующее:

  • если обязательное поле недействительно
    • IsValid должен вернуть false
    • ожидаемое исключение должно существовать в возвращенной коллекции (AggregateException.InnerExceptions)
  • если обязательное поле верно
    • только проверить, что ожидаемое исключение не существует

Теперь мне нужно обновить только один тест (все поля являются действительными), когда обнаружены новые поля.

[TestMethod] // only update this one as new fields are discovered
public void when_baby_is_valid_validation_returns_true() {
    var validator = new DefaultBabyValidator();

    // valid baby goes here
    var baby = new Baby();

    AggregateException fail;
    Assert.IsTrue(validator.IsValid(baby, out fail));
}

[TestMethod]
public void validation_returns_exception_if_first_name_is_null() {
    var validator = new DefaultBabyValidator();
    AggregateException results;
    var isValid = validator.IsValid(new Baby(), out results);
    var expected = results.InnerExceptions
                    .OfType<ArgumentException>()
                    .SingleOrDefault(ex => ex.ParamName == "FirstName");
    Assert.IsFalse(isValid); // should always return false if this condition is met
    Assert.IsNotNull(expected); // should also contain the expected exception
}

[TestMethod]
public void validation_does_not_return_exception_if_first_name_is_valid() {
    var validator = new DefaultBabyValidator();
    AggregateException results;
    validator.IsValid(new Baby { FirstName = "Charles" }, out results);
    var expected = results.InnerExceptions
        .OfType<ArgumentException>()
        .SingleOrDefault(ex => ex.ParamName == "FirstName");
    Assert.IsNull(expected); // exception should not exsist. 
                             // We don't care if it returns true.
                             // There is only one case where IsValid should
                             // return true.
}
...