Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/ant/2.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
Unit testing 如何重构和单元测试复杂的遗留JavaEE5EJB方法?_Unit Testing_Design Patterns_Java Ee 5 - Fatal编程技术网

Unit testing 如何重构和单元测试复杂的遗留JavaEE5EJB方法?

Unit testing 如何重构和单元测试复杂的遗留JavaEE5EJB方法?,unit-testing,design-patterns,java-ee-5,Unit Testing,Design Patterns,Java Ee 5,我的同事和我目前正在将单元测试引入我们遗留的JavaEE5代码库。我们主要使用JUnit和Mockito。在编写测试的过程中,我们注意到EJB中的一些方法很难测试,因为它们同时做了很多事情 我对整个测试业务相当陌生,因此我希望了解如何更好地构造代码或测试。我的目标是写一个好的测试而不头痛 这是我们在管理消息队列的服务中的一个方法及其逻辑步骤的示例: 消费信息 确认先前下载的消息 GetNewUnderMessages 加法器消息(取决于稍微复杂的条件) markmessagesas已下载 序

我的同事和我目前正在将单元测试引入我们遗留的JavaEE5代码库。我们主要使用JUnit和Mockito。在编写测试的过程中,我们注意到EJB中的一些方法很难测试,因为它们同时做了很多事情

我对整个测试业务相当陌生,因此我希望了解如何更好地构造代码或测试。我的目标是写一个好的测试而不头痛

这是我们在管理消息队列的服务中的一个方法及其逻辑步骤的示例:

  • 消费信息

    • 确认先前下载的消息

    • GetNewUnderMessages

    • 加法器消息(取决于稍微复杂的条件)

    • markmessagesas已下载

    • 序列化消息对象

顶级方法当前在接口中公开,而所有子方法都是私有的。据我所知,仅仅开始测试私有方法是不好的做法,因为只有公共接口才重要

我的第一反应是公开所有子方法并单独测试它们,然后在顶级方法中确保它调用子方法。但是后来一位同事提到,在另一个级别上公开所有这些低级方法可能不是一个好主意,因为这可能会导致混淆,其他开发人员可能会在应该使用顶级方法的时候开始使用。我不能指责他的论点

我来了

如何协调公开易于测试的低级方法与避免接口混乱之间的关系?在我们的例子中,EJB接口

我在其他单元测试问题中读到,应该使用依赖注入或遵循单一责任原则,但我在实践中应用它时遇到了困难。对于如何将这种模式应用于上面的示例方法,有人有什么建议吗


您会推荐其他通用OO模式还是JavaEE模式?

正如您所注意到的,很难对具体的高级程序进行单元测试。您还发现了两个最常见的问题:

通常,程序被配置为使用特定的资源,例如特定的文件、IP地址、主机名等。为了解决这个问题,您需要重构程序以使用依赖项注入。这通常是通过向构造函数添加参数来完成的,这些参数将替换ahrdcoded值

测试大型类和方法也非常困难。这通常是由于测试复杂逻辑所需的测试数量的组合爆炸。为了解决这个问题,您通常会首先进行重构以获得更多(但更短)的方法,然后尝试从原始类中提取几个类,每个类都有一个单一入口方法(public)和几个实用方法(private),从而使代码更具通用性和可测试性。这基本上是单一责任原则

现在,您可以通过测试新类开始“升级”。这将容易得多,因为此时组合运算更容易处理

在此过程中,您可能会发现,通过使用以下设计模式,您可以大大简化代码:命令、组合、适配器、工厂、生成器和外观。这些是减少混乱的最常见模式

旧程序的某些部分可能在很大程度上是不稳定的,要么因为它们太粗糙,要么因为它不值得麻烦。在这里,您可以接受一个简单的测试,只检查已知输入的输出是否没有改变。本质上是回归测试。

依赖注入(DI)和单一责任原则(SRP)高度相关

SRP基本上是说,每个类只应该做一件事,并将所有其他事务委托给单独的类。例如,您的
serializeMessageObjects
方法应该被提取到它自己的类中——我们称之为
MessageObjectSerializer

DI意味着将
MessageObjectSerializer
对象作为参数注入(传递)到
MessageQueue
对象中——要么在构造函数中,要么在对
consumeMessages
方法的调用中。您可以使用DI框架来完成这项工作,但我建议手动完成,以获得概念

现在,如果您为
MessageObjectSerializer
创建了一个接口,您可以将其传递到
MessageQueue
,然后获得模式的完整值,因为您可以创建mock/stub以便于测试。突然,
consumeMessages
不必关注
serializeMessageObjects
的行为

下面,我试图说明这种模式。注意,当您想要测试消息时,不必使用
MessageObjectSerializer
对象。您可以创建一个mock或stub,它完全执行您希望它执行的操作,并传递它,而不是传递具体的类。这真的让测试变得容易多了。请原谅语法错误。我没有访问Visual Studio的权限,因此它是在文本编辑器中编写的

// THE MAIN CLASS
public class MyMessageQueue() 
{

    IMessageObjectSerializer _serializer; 



    //Constructor that takes the gets the serialization logic injected
    public MyMessageQueue(IMessageObjectSerializer serializer)
    {
        _serializer = serializer;

        //Also a lot of other injection 
    }


    //Your main method. Now it calls an external object to serialize
    public void consumeMessages()
    {
        //Do all the other stuff

        _serializer.serializeMessageObjects()
    }
}

//THE SERIALIZER CLASS
Public class MessageObjectSerializer : IMessageObjectSerializer 
{
    public List<MessageObject> serializeMessageObjects()
    {
        //DO THE SERILIZATION LOGIC HERE 
    }
}


//THE INTERFACE FOR THE SERIALIZER 
Public interface MessageObjectSerializer 
{
    List<MessageObject> serializeMessageObjects(); 
}
//主类
公共类MyMessageQueue()
{
IMessageObjectSerializer\u序列化程序;
//接受的构造函数将注入序列化逻辑
公共MyMessageQueue(IMessageObjectSerializer序列化程序)
{
_序列化程序=序列化程序;
//还有很多其他的注射剂
}
//现在它调用一个外部对象来序列化
公共消息()
{
//做所有其他的事情
_serializer.serializeMessageObjects()
}
}
//序列化程序类
公共类MessageObjectSerializer:IMessageObjectSerializer
{
公共列表序列化MessageObjects()
{
//进行系列化
public class Engine {
  public void doActionOnEngine() {}
  public void doOtherActionOnEngine() {}
}


public class Car {
  private Engine engine;

  // the setter is used for dependency injection
  public void setEngine(Engine engine) {
    this.engine = engine;
  }

  // notice that there is no getter for engine

  public void doActionOnCar() {
    engine.doActionOnEngine();
  }

  public void doOtherActionOnCar() {
    engine.doActionOnEngine();
    engine.doOtherActionOnEngine(),
  }
}