Projected / enumerated数组返回复制到所有其他元素的最后一个元素

时间:2014-01-01 15:29:41

标签: c# arrays linq nunit moq

我不确定这是否是Moq问题,或者我做了一些盲目明显的事情,而且我已经看了太久了。

我通过以下声明获得了一些非常奇怪的结果:

var orderedFiles = files
    .Select(p => {
        var post = _serializer.Deserialize<BlogPostModel>(_fs.File.ReadAllText(p));
        post.FileDate = GetFileNameDate(p);
        post.FilePath = p;
        return post;
    })
    .OrderByDescending(x => x.FileDate);

当我枚举这个时,(通过使用orderedFiles.ToArray()或.ToList())我得到了原始“files”数组中的LAST元素,该数组返回给枚举中的所有元素。因此,对于下面的测试,我得到预计的file5值,5次。

当我在Select()方法调用中放置断点时,每次迭代都返回正确的值。在我枚举后,值都是相同的 - 截图如下:

The projected values are returned correctly

上图:从Select()中正确返回投影值 - 每次迭代都会返回一个完全不同的对象。

The enumerated values are all equal to the last element of the original GetFiles() call

上图:枚举值都等于原始GetFiles()调用的最后一个元素,您可以通过Locals窗口中的两个展开元素看到。

这是我的测试(当调用GetService()时,模拟被插入到服务中,并且在nunit SetUp方法中将许多全局设置放在一起):

[Test]
public void GetBlogPosts_PicksUpFilesInReverseFileNameDateOrder() {
    // Arrange
    var file1 = Path.Combine(blogPathContent, "2013-12-28-04-31-41-this-is-the-blog-title-1.json"); // 2
    var file2 = Path.Combine(blogPathContent, "2013-10-09-01-54-43-this-is-the-blog-title-2.json"); // 3
    var file3 = Path.Combine(blogPathContent, "2014-01-12-18-52-32-this-is-the-blog-title-3.json"); // 1
    var file4 = Path.Combine(blogPathContent, "2012-12-20-06-18-23-this-is-the-blog-title-5.json"); // 5
    var file5 = Path.Combine(blogPathContent, "2013-06-04-12-28-56-this-is-the-blog-title-4.json"); // 4

    mockSerializer
        .Setup(x => x.Deserialize<BlogPostModel>(It.IsAny<string>()))
        .Returns(new BlogPostModel());

    mockDirectory
        .Setup(x => x.GetFiles(It.Is<string>(s => s == blogPathContent),
            It.Is<string>(s => s == string.Concat("*", blogFilesExt))))
        .Returns(new[] {file1, file2, file3, file4, file5});

    service = GetService();

    // Act
    var actualResult = service.GetBlogPosts(new GetBlogPostsRequest());

    // Assert
    //mockFile1.VerifyGet(x => x.FullName, Times.Exactly(5), "FullName should have been called exactly 5 times.");
    Assert.NotNull(actualResult.BlogList, "BlogList should not be null");
    Assert.NotNull(actualResult.BlogList.BlogPosts, "BlogPosts should not be null");
    Assert.True(actualResult.Success, "Success should be true");
    Assert.IsNull(actualResult.Message, "Message should be null");
    Assert.AreEqual(5, actualResult.BlogList.BlogPosts.Length, "5 blog posts are expected");

    Assert.AreEqual(new DateTime(2014, 1, 12, 18, 52, 32), actualResult.BlogList.BlogPosts[0].FileDate, "file3 should be pos 1");
    Assert.AreEqual(new DateTime(2013, 12, 28, 4, 31, 41), actualResult.BlogList.BlogPosts[1].FileDate, "file1 should be pos 2");
    Assert.AreEqual(new DateTime(2013, 10, 9, 1, 54, 43), actualResult.BlogList.BlogPosts[2].FileDate, "file2 should be pos 3");
    Assert.AreEqual(new DateTime(2012, 12, 20, 6, 18, 23), actualResult.BlogList.BlogPosts[4].FileDate, "file4 should be pos 5");
    Assert.AreEqual(new DateTime(2013, 6, 4, 12, 28, 56), actualResult.BlogList.BlogPosts[3].FileDate, "file5 should be pos 4");
}

这是实施代码:

public GetBlogPostsResponse GetBlogPosts(GetBlogPostsRequest req)
{
    var files = _fs.Directory.GetFiles(contentPath, string.Concat("*", blogFilesExtension));
    if (files == null || files.Length <= 0) {
        return new GetBlogPostsResponse {
            Success = false,
            Message = "No blog posts have been made"
        };
    }

    // Deserialize / map each message
    var orderedFiles = files
        .Select(p => {
            var post = _serializer.Deserialize<BlogPostModel>(_fs.File.ReadAllText(p));
            post.FileDate = GetFileNameDate(p);
            post.FilePath = p;
            return post;
        })
        .OrderByDescending(x => x.FileDate);

    var posts = orderedFiles.ToArray();
    var response = new GetBlogPostsResponse {
        Success = true,
        BlogList = new BlogListModel {
            // BUG: enumerating here is returning a copy of the last element of the array for all elements
            BlogPosts = posts
        }
    };

    return response;
}

我错过了一些明显的东西吗?我检查了所有模拟设置是否正确,上面的调试似乎表明ToArray()方法正在扮演有趣的乞丐......但这是不可能的,对吧? :|

1 个答案:

答案 0 :(得分:3)

典型 - 排序了!如果有的话,在SO上张贴让我休息一下,稍后再回来。

这是有道理的,现在我已经弄明白......我修改了以下测试线:

mockSerializer
    .Setup(x => x.Deserialize<BlogPostModel>(It.IsAny<string>()))
    .Returns(new BlogPostModel());

以下内容:

mockSerializer
    .Setup(x => x.Deserialize<BlogPostModel>(It.IsAny<string>()))
    .Returns(() => new BlogPostModel());

必须通过普通的lambda覆盖返回新的BlogPostModel()。看起来Moq会重新使用从Setup返回的值,如果你没有明确告诉它每次返回一个唯一值 - 这解释了为什么它总是返回最后一个元素 - 因为枚举的每个元素只是一个引用从Moq返回的对象。

呼!