gpt4 book ai didi

c# - 单元测试丰富的领域模型

转载 作者:行者123 更新时间:2023-12-02 14:11:31 25 4
gpt4 key购买 nike

这是 anemic domain model:

public partial class Person
{
public virtual int PersonId { get; internal protected set; }
public virtual string Title { get; internal protected set; }
public virtual string FirstName { get; internal protected set; }
public virtual string MiddleName { get; internal protected set; }
public virtual string LastName { get; internal protected set; }
}

这就是它的行为:

public static class Services
{

public static void UpdatePerson(Person p, string firstName, string lastName)
{
// validate firstname and lastname
// if there's a curse word, throw an exception


// if valid, continue

p.FirstName = firstName;
p.LastName = lastName;


p.ModifiedDate = DateTime.Now;
}

}

而且它几乎是可测试的:

[TestMethod]

public void Is_Person_ModifiedDate_If_Updated()
{
// Arrange
var p = new Mock<Person>();

// Act
Services.UpdatePerson(p.Object, "John", "Lennon");

// Assert
p.VerifySet(x => x.ModifiedDate = It.IsAny<DateTime>());
}

但是,我想实践富域模型,其中数据和行为在逻辑上更具凝聚力。所以上面的代码现在转换为:

public partial class Person
{
public virtual int PersonId { get; internal protected set; }
public virtual string Title { get; internal protected set; }
public virtual string FirstName { get; internal protected set; }
public virtual string MiddleName { get; internal protected set; }
public virtual string LastName { get; internal protected set; }

public virtual void UpdatePerson(string firstName, string lastName)
{
// validate firstname and lastname
// if there's a curse word, throw an exception


// if valid, continue


this.FirstName = firstName;
this.LastName = lastName;

this.ModifiedDate = DateTime.Now;
}
}

但是我遇到测试问题:

[TestMethod]
public void Is_Person_ModifiedDate_If_Updated()
{
// Arrange
var p = new Mock<Person>();

// Act
p.Object.UpdatePerson("John", "Lennon");

// Assert
p.VerifySet(x => x.ModifiedDate = It.IsAny<DateTime>());
}

单元测试错误:

Result Message: 

Test method Is_Person_ModifiedDate_If_Updated threw exception:
Moq.MockException:
Expected invocation on the mock at least once, but was never performed: x => x.ModifiedDate = It.IsAny<DateTime>()
No setups configured.

Performed invocations:
Person.UpdatePerson("John", "Lennon")
Result StackTrace:
at Moq.Mock.ThrowVerifyException(MethodCall expected, IEnumerable`1 setups, IEnumerable`1 actualCalls, Expression expression, Times times, Int32 callCount)
at Moq.Mock.VerifyCalls(Interceptor targetInterceptor, MethodCall expected, Expression expression, Times times)
at Moq.Mock.VerifySet[T](Mock`1 mock, Action`1 setterExpression, Times times, String failMessage)
at Moq.Mock`1.VerifySet(Action`1 setterExpression)
at Is_Person_ModifiedDate_If_Updated()

看到直接从模拟的对象调用方法,模拟的对象无法检测是否调用了其任何属性或方法。注意到这一点后,对富域模型进行单元测试的正确方法是什么?

最佳答案

首先,don't mock value objects或您正在测试的类(class)。此外,您也没有验证是否向人员提供了正确的修改日期。您检查是否已指定某个日期。但这并不能证明您的代码按预期工作。为了测试此类代码,您应该 mock current date由 DateTime.Now 或 create some abstraction 返回,这将提供当前的服务时间。您的第一个测试应该如下所示(我在这里使用 Fluent Assertions 和 NUnit):

[Test]
public void Should_Update_Person_When_Name_Is_Correct()
{
// Arrange
var p = new Person(); // person is a real class
var timeProviderMock = new Mock<ITimeProvider>();
var time = DateTime.Now;
timeProviderMock.Setup(tp => tp.GetCurrentTime()).Returns(time);
Services.TimeProvider = timeProviderMock.Object;
// Act
Services.UpdatePerson(p, "John", "Lennon");
// Assert
p.FirstName.Should().Be("John");
p.LastName.Should().Be("Lennon");
p.ModifiedDate.Should().Be(time); // verify that correct date was set
timeProviderMock.VerifyAll();
}

时间提供者是一个简单的抽象:

public interface ITimeProvider
{
DateTime GetCurrentTime();
}

我会选择单例服务而不是静态类,因为静态类总是有问题——高耦合、没有抽象、难以对依赖类进行单元测试。但是您可以通过属性注入(inject)时间提供程序:

public static class Services
{
public static ITimeProvider TimeProvider { get; set; }

public static void UpdatePerson(Person p, string firstName, string lastName)
{
p.FirstName = firstName;
p.LastName = lastName;
p.ModifiedDate = TimeProvider.GetCurrentTime();
}
}

这与你的第二次测试有关。不要模拟您正在测试的对象。您应该验证应用程序将使用的真实代码,而不是测试仅由测试使用的一些模拟。使用到达域模型进行的测试将如下所示:

[Test]
public void Should_Update_Person_When_Name_Is_Correct()
{
// Arrange
var timeProviderMock = new Mock<ITimeProvider>();
var time = DateTime.Now;
timeProviderMock.Setup(tp => tp.GetCurrentTime()).Returns(time);
var p = new Person(timeProviderMock.Object); // person is a real class
// Act
p.Update("John", "Lennon");
// Assert
p.FirstName.Should().Be("John");
p.LastName.Should().Be("Lennon");
p.ModifiedDate.Should().Be(time); // verify that correct date was set
timeProviderMock.VerifyAll();
}

关于c# - 单元测试丰富的领域模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20852190/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com