涉及数据库时的单元测试

时间:2016-10-13 07:33:48

标签: unit-testing

我对单元测试很陌生,我需要一只手来理解我是否正在以正确的方式做事。我的主要问题是关于数据库测试...这是我的代码,然后我将暴露我的困惑

考虑这个类,它是我要执行的管道项目

public class RetrieveApplicationUsernamePipelineStep : IPipelineStep
{
    public const string RetrieveApplicationUsernameKey = "RetrieveApplicationUsername";

    private readonly IRetrieveApplicationUserRepository repository;

    public int Order => 3;
    public string Name => RetrieveApplicationUsernameKey;

    public RetrieveApplicationUsernamePipelineStep(IRetrieveApplicationUserRepository repository)
    {
        this.repository = repository;
    }

    public async Task<IDictionary<string, object>> Action(IDictionary<string, object> context)
    {
        string res = await repository.GetApplicationUser(context);

        context[Resources.ApplicationUser] = res;

        return context;
    }
}

我写了以下测试

 [TestFixture]
public class RetrieveApplicationUsernamePipelineStepTests
{
    private IRetrieveApplicationUserRepository retrieveApplicationUserRepository;

    [OneTimeSetUp]
    public void Start()
    {
        var configuration = new ConfigurationFromConfigFile();
        retrieveApplicationUserRepository = new RetrieveApplicationUserRepository(configuration);
    }

    [Test]
    public async Task ActionSuccessfullyCompleted()
    {
        var context = new Dictionary<string, object>();

        var repository = Substitute.For<IRetrieveApplicationUserRepository>();

        repository.GetApplicationUser(context).Returns("user1");

        var pipeline = new RetrieveApplicationUsernamePipelineStep(repository);

        var res = await pipeline.Action(context);

        Assert.IsNotNull(res[Resources.ApplicationUser]);
        Assert.IsNotEmpty((string)res[Resources.ApplicationUser]);
    }


    [Test]
    public void ActionFailingCompleted()
    {
        var context = new Dictionary<string, object>();

        var repository = Substitute.For<IRetrieveApplicationUserRepository>();

        repository.GetApplicationUser(context).Throws(new UserMappingNotFoundException());

        var pipeline = new RetrieveApplicationUsernamePipelineStep(repository);

        Assert.ThrowsAsync<UserMappingNotFoundException>(async () => await pipeline.Action(context));
    }

    [Test]
    public void NameTest()
    {
        var pipeline = new RetrieveApplicationUsernamePipelineStep(retrieveApplicationUserRepository);

        Assert.IsTrue(pipeline.Name == RetrieveApplicationUsernamePipelineStep.RetrieveApplicationUsernameKey);
    }

    [Test]
    public void OrderTest()
    {
        var pipeline = new RetrieveApplicationUsernamePipelineStep(retrieveApplicationUserRepository);

        Assert.IsTrue(pipeline.Order == 3);
    }
}

这些测试工作正常,因为对于ActionSuccessfullyCompleted和ActionFailingCompleted,我将IRetrieveApplicationUserRepository的结果替换为我期望的结果。

存储库的真正实现是

 public class RetrieveApplicationUserRepository : IRetrieveApplicationUserRepository
{
    #region Variables
    private readonly IConfiguration configuration;
    #endregion

    #region Ctor
    public RetrieveApplicationUserRepository(IConfiguration configuration)
    {
        this.configuration = configuration;
    }
    #endregion

    #region IRetrieveApplicationUserRepository
    public async Task<string> GetApplicationUser(IDictionary<string, object> context)
    {
        if (configuration.AppSettings[Resources.ApplicationUserFromDomainUserKey] == null)
            throw new KeyNotFoundException(Resources.ApplicationUserFromDomainUserKey);

        if (string.IsNullOrEmpty(configuration.ConnectionString))
            throw new NullReferenceException();

        string storedProcedure = configuration.AppSettings.Get(Resources.ApplicationUserFromDomainUserKey);

        string result;
        using (var sqlConnection = new SqlConnection(configuration.ConnectionString))
        {
            using (var sqlCommand = new SqlCommand(storedProcedure, sqlConnection))
            {
                sqlCommand.CommandType = CommandType.StoredProcedure;

                sqlCommand.Parameters.AddWithValue("@DOMAINUSER", context[Resources.DomainUser]);
                sqlCommand.Parameters.AddWithValue("@DOMAIN", context[Resources.DomainName]);
                sqlCommand.Parameters.AddWithValue("@APPID", context[Resources.ApplicationId]);

                sqlConnection.Open();

                result = (string)await sqlCommand.ExecuteScalarAsync();
            }
        }

        if (result == null)
            throw new UserMappingNotFoundException();

        return result;
    }
    #endregion
}

以下是问题:

  1. 我写的测试是否正确?
  2. 我见过使用Resharper的代码覆盖率它希望我测试覆盖属性...有没有办法可以避免这种情况?这个测试有意义吗?
  3. 当您使用与DB相关的单元测试组件时,您的方法是什么?你有一个用于测试的真实数据库吗?考虑到真正的数据库大约是10Gb所以我不希望有一个副本作为mdf(condider我可以有这个)只是为了测试数据库的一小部分
  4. 与我的同事交谈时,他们告诉我只为TDD使用测试,而我希望使用它们来避免回归
  5. 回到数据库问题,如果用户名是“John”,我不想在我写的地方进行测试,也许明天John用户将不再出现在数据库中,所以测试我希望通过不会'通过anoymore

1 个答案:

答案 0 :(得分:1)

通常的方法是使用抽象来隔离数据库端,因此您可以提供该抽象的测试虚拟(模拟,伪造等)。仅在执行集成测试时测试实际数据库。

对于数据库存储过程的测试,您可能需要一个不同的测试工具,在内存中创建一个新的测试数据库(相当于在RAM支持的文件系统中)。您只需要为单个测试填充足够的数据(我们在这里进行功能测试,而不是性能测试),并且您可以通过合理使用回滚来保留测试中的表结构。

我已经这样做了,但是不久之前,所以我将不再提供可能不再是最新技术的示例(即使代码仍然存在,如果我能找到它)