【问题标题】:Unit Test In N Tier ArchitectureN 层架构中的单元测试
【发布时间】:2014-09-30 07:49:10
【问题描述】:

我想为别人编写的程序编写测试。但是我在编写测试时遇到了一些问题。我无法确切了解如何伪造某些对象。我搜索并找到了Unit Test for n tier architecture,但这对我没有帮助。例如,我想为下面的代码编写一个测试(我知道这是一个虚拟代码,只是为了澄清)

        public List<CustomerObject> FetchCustomersByName(CustomerObject obj)
        {
            DAL customerDal = new DAL();
            //Maybe other operations
            List<CustomerObject> list = customerDal.FetchByName(obj.Name);
            //Maybe other operations over list
            return list;
        }

我只想测试 FetchCustomersByName 但与 DAL 有联系。我想创建存根类,但在这种情况下,我必须更改我的原始代码。它是由其他人编码的。我怎样才能为这个方法写一个测试?

提前致谢。

【问题讨论】:

  • 代码 sn-p 不适用于 C#。
  • @Noctis 我意识到我必须使用模拟/存根,但我不明白如何在不接触原始代码的情况下调用 Mock 类(for dal)。

标签: c# .net visual-studio unit-testing testing


【解决方案1】:

不要对数据访问层进行单元测试。为其编写集成测试。

在 DAL 中模拟依赖项不仅值得麻烦,因为它不能保证任何事情。

如果你仔细想想,DAL 依赖于 SQL 方言和数据库模式。因此,您的单元测试可能工作得很好。但是,当您运行真正的解决方案时,它仍然会失败。原因可能是您的 SQL 查询不正确,或者类属性类型之一与表列类型不匹配。

单元测试通常是为业务逻辑编写的。他们捕获的一件事是不会产生异常的错误,例如不正确的条件或计算错误。

更新

好的。因此,您的示例实际上包含业务逻辑。方法名骗了我。

您必须改变创建 DAL 类的方式。但是您不必像 Jack Hughes 建议的那样使用构造函数注入。相反,您可以使用工厂模式:

    public List<CustomerObject> FetchCustomersByName(CustomerObject obj)
    {
        var customerDal = DalFactory.Create<CustomerDal>();

        //Maybe other operations
        List<CustomerObject> list = customerDal.FetchByName(obj.Name);
        //Maybe other operations over list
        return list;
    }

这有点容易,因为现在您可以使用“全部替换”将所有 var customerDal = new CustomerDal() 更改为 var customerDal = DalFactory.Create&lt;CustomerDal&gt;();

在那个工厂类中你可以调用不同的实现

public class DalFactory
{
    public static IDalFactory Factory { get set; }

    static DalFactory()
    {
        Factory = new DefaultDalFactory();
    }


    public static T Create<T>() where T : class
    {
        return Factory.Create<T>();
    }
}

public interface IDalFactory
{
    T Create<T>() where T : class
}

public class DefaultDalFactory : IDalFactory
{
    public T Create<T>() where T : class
    {
        return new T();
    }
}

代码并不漂亮,但它以最少的重构解决了您的问题。我建议你从那开始,然后尝试改变你的编码标准,以便允许构造函数注入。

要让它在您的测试中工作,您可以使用以下实现。它使用 [ThreadStatic] 允许同时运行多个测试。

public class TestDalFactory : IDalFactory
{
    [ThreadStatic]
    private static Dictionary<Type, object> _instances;

    public static Dictionary<Type, object> DalInstances
    {
        get
        {
            if (_instances == null)
                _instances = new Dictionary<Type, Object>();
            return _instances;
        }
   }

    public static TestDalFactory Instance = new TestDalFactory();

    public T Create<T>() where T : class
    {
        return (T)_instances[typeof(T)];
    }
}

接下来在您的测试中,您可以通过执行以下操作将 DAL 工厂配置为返回模拟:

[TestClass]
public class MyBusinessTests
{
    [TestInitialize]
    public void Init()
    {
        DalFactory.Instance = TestDalFactory.Instance;
    }

    [TestMethod]
    public void do_some_testing_in_the_business()
    {
        TestDalFactory.Instance.DalInstances[typeof(CustomerDal)] = new MyNewMock();

        //do the testing here
    }
}

【讨论】:

  • 这正是我们想要做的。但我不知道如何在我的方法中跳过 DAL 部分。
  • 感谢您的详细回答。但 DalFactory 类中名为 Factory 的变量应命名为 Instance。我错了吗?
【解决方案2】:

使用 DAL 的构造函数注入将允许您存根 DAL 层。理想情况下,您会注入一个接口。使用我使用的工具来模拟具体类有点痛苦。商业模拟工具可能更适合模拟具体类,但我没有使用任何这些。

class YourClass
{
    private DAL customerDal;

    public YourClass(DAL theDal)
    {
        customerDal = theDal;
    }

    public List<CustomerObject> FetchCustomersByName(CustomerObject obj)
    {
       // don't create the DAL here...
       //Maybe other operations
       List<CustomerObject> list = customerDal.FetchByName(obj.Name);
       //Maybe other operations over list
       return list;
     }
}

[Test]
public void TestMethodHere()
{
    // Arrange
    var dalMock = Mock.Of<DAL>();
    // setup your mock DAL layer here... need to provide data for the FetchByName method
    var sut = new YourClass(dalMock);

    // Act
    var actualResult = sut.FetchCustomersByName(new CustomerObject());

    // Assert
    // Your assert here...
}

【讨论】:

  • 事实上,这似乎是我的代码的唯一方法。但由于我们软件的编码标准,我无法改变创建 DAL 的方式。所以我开始认为我不能为我们的代码编写单元测试。
  • 如果您不能更改代码,那么单元测试将非常困难。您仍然可以使用以已知数据为种子的数据库系统编写集成测试。可能总比没有好,但从长远来看,您会发现测试运行时间过长。可能是您的编码标准专门设计用于使编写单元测试成为不可能,因为编写标准的人不相信单元测试。在这种情况下,即使尝试任何类型的自动化测试,也可能会将您置于困难的政治领域。
猜你喜欢
  • 2012-05-24
  • 1970-01-01
  • 1970-01-01
  • 2015-11-02
  • 2014-01-10
  • 1970-01-01
  • 1970-01-01
  • 2013-02-24
  • 1970-01-01
相关资源
最近更新 更多