我正在尝试对方法进行单元测试,并且它使用传递给模拟方法的字典来向电子邮件添加附件。测试总是失败,踩过一切似乎都是正确的,但Assert
似乎没有验证。
一般来说,是否有一种特殊的单元测试字典方法,并且可以使用<string, Stream>
设置字典。代码在下面,但不要认为它有什么,但可能设置错误,我想我错过了一些明显的东西。
[Fact]
public void Process_ShouldAttachCsvStreamWhenBuildingEmailMessage()
{
//Arrange
var fixture = new Fixture();
var settings = fixture.Create<Settings>();
var sutFixtures = new SUTFixtures(true);
var response = RemoteClientResponseHelper.GetMockHttpWebResponse(sutFixtures.Items);
//deal with attachement
var csv = sutFixtures.ToCsv();
var bytes = Encoding.GetEncoding("iso-8859-1").GetBytes(csv);
var messageAttachments = new Dictionary<string, Stream> {{"MissingImages.csv", new MemoryStream(bytes)}};
var moqClientService = new Mock<IClientService>();
moqClientService.Setup(x => x.Call(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), null))
.Returns(response.Object);
Dictionary<string, Stream> attachmentsVerify = null;
var moqEmailService = new Mock<IEmailService>();
moqEmailService.Setup(
x =>
x.BuildMessage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(),
It.IsAny<bool>(), It.IsAny<Dictionary<string, Stream>>()))
.Callback<string, string, string, string, bool, Dictionary<string, Stream>>(
(to, from, subject, body, isHtml, attachments) =>
{
attachmentsVerify = attachments;
});
//Act
var sut = new MissingImageNotificationStep(moqClientService.Object, moqEmailService.Object, settings);
sut.Process(new MappingData() { Parts = sutFixtures.DataTable });
//Assert
moqEmailService.Verify(m => m.BuildMessage(It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<bool>(),
It.IsAny<Dictionary<string, Stream>>()), Times.Once());
Assert.Equal(messageAttachments, attachmentsVerify);
}
更新
当我想到一个自定义比较器时,一定是非常懒惰但是想想也许它已经存在了。我有一些工作,对于我的情况,无论如何,看看比较器,我必须做一些明确的转换,这在我的情况下很好,但有点臭,因此我的代码需要一个重构,也不确定GetHash在这种情况下做了什么如果这个代码在测试之外被使用,我会看一下。
自定义比较器
public class DictionaryComparer : IEqualityComparer<Dictionary<string, Stream>>
{
private readonly IEqualityComparer<Stream> _valueComparer;
public DictionaryComparer(IEqualityComparer<Stream> valueComparer = null)
{
this._valueComparer = valueComparer ?? EqualityComparer<Stream>.Default;
}
public bool Equals(Dictionary<string, Stream> x, Dictionary<string, Stream> y)
{
if (x.Count != y.Count)
return false;
if (x.Keys.Except(y.Keys).Any())
return false;
if (y.Keys.Except(x.Keys).Any())
return false;
foreach (var pair in x)
{
var xValue = pair.Value as MemoryStream;
var yValue = y[pair.Key] as MemoryStream;
if (xValue.Length != yValue.Length)
return false;
xValue.Position = 0;
yValue.Position = 0;
var xArray = xValue.ToArray();
var yArray = yValue.ToArray();
return xArray.SequenceEqual(yArray);
}
return true;
}
public int GetHashCode(Dictionary<string, Stream> obj)
{
unchecked
{
var hash = 17;
foreach (var key in obj.Keys)
{
hash = hash * 23 + key.GetHashCode();
}
return hash;
}
}
}
通过XUnit调用
Assert.Equal(messageAttachments, attachmentsVerify, new DictionaryComparer());
答案 0 :(得分:2)
目前的行为是预料之中的。由于消息附件和attachmentverify引用不同的对象,Assert.Equal返回false。
您可以扩展Dictionary类,并覆盖Equals和GetHashCode。之后,Assert.AreEqual将在您的自定义词典中使用时返回true。
您还可以使用xunit CollectionAsserts来比较不同集合的项目(在本例中为字典)。
如果你想避免平等气味,你可以创建自己的相等比较器,只检查公共属性(使用反射)。 Testing deep equality
答案 1 :(得分:0)
正如我在评论中提到的,您可能需要覆盖Equals
方法。
默认情况下,比较基于引用两个对象。尽管内容相同,但您的词典是不同的对象。您需要通过覆盖相等的Assert
和进行深度比较(内容比较)来帮助Equals
做出决定。
对于CollectionAssert
,我的知识需要相同的顺序。
因此,您在应用OrderBy
之前使用Assert
,或覆盖Equals
。
通过覆盖Equals
,您可以在代码中的任何位置比较字典。
这是一个关于如何覆盖该方法的示例(您可能希望对Dictionary执行相同的操作)。
public class MetricComparator : IEqualityComparer<Metric>
{
/// <summary>
/// Returns true if two given Metric objects should be considered equal
/// </summary>
public bool Equals(Metric x, Metric y)
{
return
x.Source == y.Source &&
x.Type == y.Type &&
x.Title == y.Title &&
x.Public == y.Public &&
x.DayAvg == y.DayAvg &&
x.DayMax == y.DayMax &&
x.DayMin == y.DayMin &&
x.HourAvg == y.HourAvg &&
x.HourMax == y.HourMax &&
x.HourMin == y.HourMin &&
x.CurrentValue == y.CurrentValue;
}
/// <summary>
/// Returns a hash of the given Metric object
/// </summary>
public int GetHashCode(Metric metric)
{
return
2 * metric.Source.GetHashCode() +
3 * metric.Type.GetHashCode() +
5 * metric.Title.GetHashCode() +
7 * metric.Public.GetHashCode() +
11 * metric.DayAvg.GetHashCode() +
13 * metric.DayMax.GetHashCode() +
17 * metric.DayMin.GetHashCode() +
23 * metric.HourAvg.GetHashCode() +
29 * metric.HourMax.GetHashCode() +
31 * metric.HourMin.GetHashCode() +
37 * metric.CurrentValue.GetHashCode();
}
}