Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/338.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/311.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 单元测试类的最佳实践,该类主要负责调用依赖项的方法,但也包含逻辑_Java_C#_Unit Testing_Testing_Integration Testing - Fatal编程技术网

Java 单元测试类的最佳实践,该类主要负责调用依赖项的方法,但也包含逻辑

Java 单元测试类的最佳实践,该类主要负责调用依赖项的方法,但也包含逻辑,java,c#,unit-testing,testing,integration-testing,Java,C#,Unit Testing,Testing,Integration Testing,让我们假设我有StartCommandHandler,它负责创建一些包含所需文件的文件。但要做到这一点,我必须给他一系列的子责任,比如: 检查FTP中是否存在文件 如果没有,则将文件从多个源下载到临时文件夹 然后执行文件夹中的一些脚本 然后在脚本执行后读取生成的文件 然后从该文件夹创建zip 然后删除该文件夹 然后更新数据库 作为该命令处理程序的结果,我们正在创建包含所有必需文件的文件夹。现在,该文件夹已准备好进行其他操作 我刚刚读了“单元测试的艺术”。并开始添加单元测试。我也遵循了坚实的原

让我们假设我有
StartCommandHandler
,它负责创建一些包含所需文件的文件。但要做到这一点,我必须给他一系列的子责任,比如:

  • 检查FTP中是否存在文件
  • 如果没有,则将文件从多个源下载到临时文件夹
  • 然后执行文件夹中的一些脚本
  • 然后在脚本执行后读取生成的文件
  • 然后从该文件夹创建zip
  • 然后删除该文件夹
  • 然后更新数据库
作为该命令处理程序的结果,我们正在创建包含所有必需文件的文件夹。现在,该文件夹已准备好进行其他操作

我刚刚读了
“单元测试的艺术”
。并开始添加单元测试。我也遵循了坚实的原则。特别是,
SRP
DIP
,我认为这是单元测试的先决条件。 所以,我上面提到的大多数事情都是通过特定的接口完成的。因此,该命令处理程序90%的工作是调用依赖项的方法。10%是这样的逻辑:

if(!_dependency1.IsAnySomething())
{
     _dependency2.Download();

      var isScriptNeeded = _dependency2.IsScriptNeeded();

      if(isScriptNeeded)
      {
          var res = _dependency3.ExecuteScript();
         _dependency4.SetScriptResult(res.Info, res.Date, res.State);
      }

     _dependency3.Archive();

     _dependency5.DeleteTemp();
}
我已经测试了该命令处理程序的所有依赖项。但是,hat命令处理程序还包括一些小逻辑,例如,是否需要下载文件,或者是否删除临时文件等等

我脑子里有很多问题,比如:

  • 也许单元测试对这样的单元没有意义?集成测试用于救援?因为,测试是否检查所有调用似乎是错误的,例如下载后是否调用了
    deletemp
    ,脚本是否执行,或者脚本结果是否以正确的方式传递给
    SetScriptResult
    方法。它是好的单元测试吗
  • 有没有办法重构该类以使其可测试

  • 您也可以对这种方法使用单元测试。这可以通过模拟依赖项来实现。原则如下:

    使用预定义的结果创建依赖项的模拟(通常通过实现依赖项的接口)。例如,您为
    dependency2
    提供了一个实现,该实现总是为
    IsScriptNeeded()
    返回
    true

    然后监视
    dependency3
    并检查它是否被调用(应该是!),以及
    dependency4.SetScriptResult(…)
    的参数是否与结果匹配

    对于C#,有像FakeiTasy和Moq这样的库,这使得模仿变得轻而易举。使用xUnit和Fakeetisy的示例代码段:

    使用系统;
    使用假货;
    使用Xunit;
    公共类MyTest
    {
    [事实]
    公开无效测试()
    {
    //创建模拟
    var mock=A.Fake();
    A.CallTo(()=>mock.DoSomething()).Returns(true);
    //创建要测试的类
    var sut=新系统未测试(模拟);
    sut.DoSomethingElse();
    //断言调用了mock.DoSomething()
    A.CallTo(()=>mock.DoSomething()).musthavecoursed();
    }
    }
    
    它是好的单元测试吗

    恐怕你得自己决定这个话题了

    据我所知,并不是每个人都同意UT应该涵盖哪些内容。我想说,这也取决于你的团队、你的公司以及你真正想要实现的目标

    • 有些人盲目地测试一切,不管发生什么,因为你永远不知道下一个bug会是什么。他们可能是对的。 在您的案例中,他们将对每个依赖项进行多个模拟,以测试通过和未通过的案例

    • 有些人认为它太昂贵,而且无法证明对高级协调类的测试,就像您在这里指出的那样。他们可能也是对的

    我很有信心,不这样做的唯一理由是成本。或者至少我不知道还有其他的。如果您的高级UT维护成本较低,则没有理由不这样做

    不幸的是,在很多情况下(在我看来,但我也同意它并不适用于所有地方),嘲笑很难维持,使UT变得毫无价值

    例如(我不想被误解),如果您的依赖关系使用低级对象,如套接字或文件,这些对象会导致竞争条件和/或意外延迟(在多线程环境中),那么您不能以帮助您检测错误的方式模拟它们:您应该模拟这些竞争条件和延迟

    若您尝试模拟它们,您将在模拟开发和维护上花费大量精力,所以您最好增加集成测试,以检测真实环境中的bug

    如果你不模仿它们,你的UT就不会提供任何安全性,因为它很确定这些bug将来自那些你没有模仿的比赛条件

    最后,我想指出,在这个问题中也有一个“教学”方面。您可能想要实现这些高级UT,使用模拟,即使它们不阻止任何事情。。。只是因为你想让你的同事先了解UT的一般规则是如何工作的,然后再让他们自己去打破它

    我希望我有更多的线索。 祝你好运。

    更新

    既然这个团队的职责是召集和协调其他事情, 您可以清除所有依赖项,并验证它们是否按正确的顺序被调用,以及调用次数是否符合要求。类似于Moq在模拟类上提供的Verify方法

            // Explicitly verify each expectation...
            mockSomeClass.Verify(s => s. SetScriptResult(It.IsAny<YourType>()/* or whatever value parameters you may setup snd ecpect it to be called*/), Times.Once());
    
    
    //显式验证每个期望值。。。
    验证(s=>s.SetScriptResult(It.IsAny()/*或您可能设置的任何值参数,并希望调用它*/),Times.Once());
    

    我相信您在这里所做的和提到的对于单元测试来说已经足够了,因为单元测试的责任是测试一个单元,假设其他互连单元是
    _dependency3.Archive();
    _dependency5.DeleteTemp();
    
    class Cache {
        Cache(ValueStore store, ValueFactory factory) { ... }
    
        object GetValue(object key) {
            if (!store.HasValue(key))
                factory.CreateValue(key);
            return store.GetValue(key);
        }
    }
    
    class CacheTest {
       void GetValue_CallsFactory_WhenValueNotInStore() {
          // arrange
          var store = Mock.Of<VaueStore>(_ => _.HasValue() == false);
          var factory = Mock.Of<ValueFactory>();
          var cache = new Cache(store, factory);
    
          // act
          cache.getValue();
    
          // assert
          Mock.Get(factory).Verify(_ => _.CreateValue(), Times.Once());
       }
    }