1

好的,所以我有以下模块,它从表中返回用户 ID 列表,其中 id 与正则表达式匹配:

public sealed class UserIdListRetriever : IUserIdListRetriever
{
    private readonly EntityFrameworkClass _databaseConnection;

    public UserIdListRetriever(EntityFrameworkClass databaseConnection)
    {
        _databaseConnection = databaseConnection;
    }

    public IEnumerable<string> Retrieve()
    {
        var salesAgents = _databaseConnection
            .tblAccounts
            .Select(account => account.UserId)
            .Distinct();

        var regex = new Regex(@"(?<=\\)(.*?)(?=\:)");

        return (from agent in salesAgents
                .AsEnumerable()
                select regex.Match(agent)
                into match
                where match.Success
                select match.Value.ToUpper())
                .OrderBy(match => match);
    }
}

这是界面:

public interface IUserIdListRetriever
{
    IEnumerable<string> Retrieve();
}

我一直在读我应该测试行为,而不是实现,但我在这里关心的是我的班级是否返回准确的用户 ID 列表。

我可以创建一个 IUserIdListRetriever 的模拟实现,并且可能在我的单元测试中断言我返回一个不为空的字符串的 IEnumerable,但这不会测试我的 LINQ 是否正确,或者我的正则表达式是正确的,但感觉没有那么有用。

我觉得这两件事在这里很重要(无论我的 LINQ 是否正确,以及我的正则表达式是否正确),我最终得到了这样的测试类:

using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
etc etc

namespace myNamespaceTests
{
    [TestClass]
    public class UserIdListRetrieverTests
    {
        [TestMethod]
        public void UserIdListRetrieverReturnsAccurateInformation()
        {
            var databaseConnection = 
                new EntityFrameworkClass("connection;string;");

            var userIdListRetriever = new UserIdListRetriever(databaseConnection );

            var userIds = userIdListRetriever.Retrieve();

            /*
             * I put a breakpoint here, 
             * and run the same query in SQL Management studio 
             * to make sure I have the same results
            */
             Assert.IsTrue(userIds.Any());
        }
    }
}

这感觉非常错误,但从我的角度来看,我发现这是最有用的,因为它仍然允许我快速(虽然不是那么快)测试这个模块正在做我想让它做的事情。

我有很多这样的模块,我的代码仍然是模块化和可测试的,但我发现只有当我花一点时间手动运行单元测试、逐步检查每个模块并手动对数据库运行查询以验证时,这些测试才有用我的数据检索模块给我的信息是我期望看到的。在此之后,我可以自信地说,我的代码库中的每个模块都按照我的意愿去做。

我不认识其他人以这种方式工作,这通常是一个不好的迹象(我错了,还是其他人都错了?)。知识渊博的人可以解释我在哪里出错并解释他们如何测试上面的类,以一种他们可以快速运行测试并且这些测试是自动化的方式,但他们可以自信地说他们的每个模块都有预期的行为?

谢谢

4

2 回答 2

2

这种方法导致集成测试而不是单元测试。如果您在没有与数据库可用的连接的构建服务器上运行单元测试会怎样。

首先,您必须了解如果您使用的是实际资源,那么这将是一个集成测试而不是单元测试。

因此,如果您想测试数据库连接的完整性,那么您做对了。但是如果你只是想测试你的过滤器逻辑。然后您必须将您的方法 Retrieve() 重构为两部分。

第 i) 部分返回从数据库返回的确切结果。

部分 ii)根据结果测试过滤器操作。

这样您就可以模拟从数据库返回的结果。然后测试过滤​​方法以确保在给定的数据库结果上它工作正常。

例如

        public IEnumerable<string> Retrieve()
        {
            return _databaseConnection.tblAccounts.Select(account => account.UserId).Distinct();
        }

        public IEnumerable<string> GetMatchingItems(IEnumerable<string> salesAgents)
        {
            var regex = new Regex(@"(?<=\\)(.*?)(?=\:)");

            return (from agent in salesAgents
                    .AsEnumerable()
                    select regex.Match(agent)
                        into match
                        where match.Success
                        select match.Value.ToUpper())
                    .OrderBy(match => match);
        }
于 2013-10-14T12:19:37.273 回答
2

我一般认为,如果它涉及到数据库,那不是单元测试。话虽如此,我多年来一直在研究同样的问题,但我无法为您提供更优雅的解决方案来测试您的数据检索语句。

我会注意到这段代码不遵守单一职责原则 - 它从 EF 源检索数据,然后进一步过滤它。您可以做的是将此代码拆分为 2 个单独的部分:一个用于检索列表,另一个用于检查与您的正则表达式匹配的字符串的列表。然后,您可以轻松设置单元测试以验证正则表达式是否按预期工作。

于 2013-10-14T12:24:37.860 回答