使用私有setter测试属性

14

目前在我的项目的某个部分存在一个类似以下的领域对象:

public class Address
{
    public virtual string HouseName { get; set; }

    public virtual string HouseNumber { get; set; }

    public virtual string RoadName { get; set; }

    public virtual string Postcode { get; set; }

    public virtual string District { get; private set; }
}

District属性是数据库中的计算列。由于数据访问提供程序(Entity Framework)的要求,需要一个私有集合。在正常的程序流程中这是没有问题的,因为District永远不需要设置,它会在保存/更新数据库中的地址记录时更新。

我想测试的方法如下:

public IEnumerable<House> GetHousesWithinSameDistrict (int addressId)
{
    var addressToMatch = _addressRepository
      .FirstOrDefault(address => address.Id == addressId)

    return _houseRepository
      .Where(house => house.Address.District == addressToMatch.District)
}

当我尝试设置addressToMatch变量时,这给我带来了麻烦,因为我无法设置区域属性,因此我无法编写一个测试来检查是否返回匹配的地址。

那么我该如何设置一个Address对象用于此测试呢?


也许是 protected internal + assembly:InternalsVisibleTo - Anton Gogolev
@DeblatonJean-Philippe 相关,但我认为绝不是重复。 - Jeroen
@DeblatonJean-Philippe 绝不是那个问题的重复。 - FLSH
1
将其设置为“protected set”,并创建一个子类,其中包含一个setter方法(作为测试夹具的私有类)是一种(不太美观但实用的)解决方法。 - Jeroen
抱歉,我误解了这个问题。感谢您指出。 - Deblaton Jean-Philippe
1
没有看到你的测试,很难给出建议,但是你可以使用一个需要匹配的地址的 Mock,并使用 SetupGet 返回你想要的值,你可以使用反射设置所需的值,或者如果你正在使用 MS 测试框架,你可以使用 PrivateObject 来设置该值。https://msdn.microsoft.com/en-us/library/ms244100.aspx - forsvarir
2个回答

20

如果您正在使用Moq(问题已经打上了该标签,所以我假设您在使用它),那么这个功能可以直接使用:

var mockAddress = new Mock<Address>();
mockAddress.SetupGet(p => p.District).Returns("Whatever you want to match");

因此,一个更完整的示例是:

var mockAddress = new Mock<Address>();
mockAddress.SetupGet(p => p.Id).Returns(42);
mockAddress.SetupGet(p => p.District).Returns("What you want to match");

var mockAddressRepository = new Mock<IRepository<Address>>();
var addresses = new List<Address> { mockAddress.Object };
mockAddressRepository.Setup(p => p.GetEnumerator()).Returns(addresses.GetEnumerator());

var addressToMatch = mockAddressRepository.Object.FirstOrDefault(address => address.Id == 42);
Console.WriteLine(addressToMatch.District);

这会输出预期值:

What you want to match

1
这个答案真是太棒了。我本来想把我的属性设为 internal 并设置 InternalsVisibleTo,或者使用 PrivateObject。这个答案非常简单易懂。谢谢你。 - Bob Horn

3
首先,有一个猜测:也许是将你的 District 属性声明为 protected internal 并在 "domain" 程序集中添加一个 assembly:InternalsVisibleTo 属性,这样就可以从测试中设置 Distinct
class Address
{
    public virtual string District { get; protected internal set; }
}

或者,你可以在你的Address类中添加一个内部构造函数,它会设置District属性的值。同样,使用assembly:InternalsVisibleTo注释你的“domain”程序集。


有没有使用InternalsVisibleTo的替代方法?这使得我的代码有些混乱和不清晰(我正在练习ddd,所以我类中所有属性的设置都是私有的),如果您能告诉我更加优雅的解决方案,我会非常感激。谢谢 - picaCHuXX

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接