在测试实现时,我采用的方法是否是常见的方法,还是我做错了

本文关键字:方法 错了 常见 是否是 测试 实现 | 更新日期: 2023-09-27 18:35:11

好的,所以我有下面的模块,它从表中返回用户 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 的模拟实现,并且可能会在我的单元测试中断言我返回一个不为 null 的 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());
        }
    }
}

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

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

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

谢谢

在测试实现时,我采用的方法是否是常见的方法,还是我做错了

我通常认为,如果它涉及数据库,则不是单元测试。话虽如此,我已经研究了这个问题好几年了,但我不能为您提供更优雅的解决方案来测试您的数据检索语句。

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

这种方法会导致集成测试而不是单元测试。如果在没有可用于数据库的连接的生成服务器上运行单元测试,该怎么办?

首先,如果您使用的是实际资源,则必须处于标准之下,那么这将是集成测试而不是单元测试。

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

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

第二部分)对结果测试过滤器操作。

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

例如

        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);
        }