存在于许多公司的代码库遗留代码。常在,有在开发过程中造成障碍的反模式。

我最近增加了我的第一个分析事件的一些功能的工作,我刚刚完成了一部分。我偶然发现了这是不正确的模式的后果,一些意想不到的问题。在这篇文章中,我会告诉你我是如何克服他们使用的抽象和改进的可测性。

卷起装置

我们有一个功能或流动称为特定类SessionMetrics。它包含了所有必要的部分发送与我们的用户会话分析事件。它的大多数方法采取数据作为输入,然后创建和发送事件特定于我们的分析软件库。它还定义了我们与事件发送共同的价值观。

对象SessionMetrics:BaseMetrics(){const的VAL CONVO_STATUS = “谈话状态” 的乐趣fireSelectedMsg(/ *地段则params的* /){//创建事件fireEvent(事件)}}

SessionMetrics以上从父类延伸BaseMetrics。父是极少的,是直接调用我们的分析软件库的包装。它还包含用于创建一个事件具体到我们的分析软件库的便捷方法。

下面是一个例子什么BaseMetrics看起来像。

公开课BaseMetrics {乐趣fireEvent(事件:事件){VAL实例= AnalyticsTool.getInstance()}}

问题

作为我们工作的一部分,我们需要从一个视图模型一个新的分析事件添加。天真,我们符合上述模式。该代码运行如预期,但我们很快就用坏的单元测试招呼。我们的静态结构直接拨打我们的分析软件库,因此我们的视图模型试验失败。

该生产线VAL实例= AnalyticsTool.getInstance()从上面的例子是导致该问题。我们AnalyticsTool从来就不是在JVM上运行,因此打破了我们的单元测试。我们能做什么?我们不能离开它这样。

什么是我们去向何方?

最起码,我们应该改变不破测试。在最好的,我们希望通过除了不破坏现有测试的测试来验证我们的改变。让我们志存高远,看看我们能能想出!

启动与接口

让我们通过创建将定义我们要发送到我们的分析客户的输入界面启动。

接口SessionMetrics {乐趣fireSelectedMsg(inboxSizeProperty:诠释?targetUserId:字符串?matchedUser:用户?initialNWays:诠释?)}

我们呼吁我们的界面SessionMetrics并且将有我们的活动方法fireSelectedMsg()这发生在我们的分析事件的所有必需的数据。

创建实现

类AnalyticsLibSessionMetrics:SessionMetrics,BaseMetrics(){控乐趣fireSelectedMsg(inboxSizeProperty:诠释?targetUserId:字符串?matchedUser:用户?initialNWays:智力){//创建从我们的分析软件库特定事件//我们传递的数据fireEvent(analyticsLibEvent)}}

然后,我们创造我们喜欢具体落实上述并将其命名为AnalyticsLibSessionMetrics名称应反映分析软件库这种具体实施正在与。在这种情况下,AnalyticsLib。它实现了我们的SessionMetrics除了上述工具接口BaseMetrics如果您正在使用火力地堡例如它会FirebaseSessionMetrics

现在,我们可以通过我们新创建的类到我们的视图模型,如:

MessageThreadViewModel(AnalyticsLibSessionMetrics())

使用假修复试验

让我们的测试并通过创建一个代表我们所宣布的接口假重新运行。

类FakeSessionMetrics:SessionMetrics {覆盖乐趣fireSelectedMsg(inboxSizeProperty:诠释?targetUserId:字符串?matchedUser:用户?initialNWays:智力){//什么都不做}}

随着我们的假声明,我们可以像测试创建我们的视图模型:

VAL fakeSessionMetrics = FakeSessionMetrics()MessageThreadViewModel(fakeSessionMetrics)

现在,我们的测试再次合作,因为我们是不是要真要给我们的分析软件库。这感觉就像我们可以做的更好!让我们验证该事件实际上是调用。

验证使用的是假

让我们一个实例变量添加到我们的FakeSessionMetrics

类FakeSessionMetrics:SessionMetrics {VAR selectedMsgEventFired =虚假覆盖的乐趣fireSelectedMsg(//很多则params的){selectedMsgEventFired =真}}

实例变量selectedMsgEventFired当我们的假货事件方法被调用设置为true。然后,我们可以从测试验证我们的fireSelectedMsg()方法如我们所希望的被调用。

assertThat(fakeSessionMetrics.selectedMsgEventFired).isTrue()

东西后仍不正确

这些参数我们fireSelectedMsg()方法正在都不值,我们传递给我们的分析客户端。我们做的,以获得一些价值,我们然后发送给我们分析客户数据的一些处理。在代码库的当前模式将放置在这个视图模型的处理逻辑。我目前的设计有它在我们的新创建SessionMetrics它并没有真正似乎无论是属于英寸

为什么它不属于任一?

我们的视图模型确实不应该负责处理数据发送给我们的分析层。它增加了视图模型的责任,并使其更难为我们验证逻辑在此处理。

如果我们把这个处理逻辑在我们的具体落实我们的帮手SessionMetrics,我们将无法对其进行测试。因为我们是直接拨打我们的分析软件库,我们将有我们最初的失败测试的问题。是否处理逻辑有什么与我们的具体落实?

数据的类型

让我们来看看到底是什么,我们将发送给我们的分析客户端。

VAL hasRead:布尔,VAL targetUserId:字符串?VAL ageindays应用:诠释,VAL inboxSize:诠释

接下来,我们可以创建数据类来表示这些值。

数据类SelectedMessageEvent(VAL hasRead:布尔,VAL targetUserId:字符串?, VAL ageindays应用:中等,VAL inboxSize:智力)

我们的SessionMetrics接口可以改变现在。

接口SessionMetrics {乐趣fireSelectedMsg(selectedMessageEvent:SelectedMessageEvent)}

这简化了接口和解耦我们分析我们的处理逻辑库。我们现在坦率正是我们的分析库预计此事件。

处理逻辑

我们可以因为它们在逻辑上一起去我们的处理逻辑与我们的数据类关联。我们可以附加一个静态方法从()到我们的数据类,如:

数据类SelectedMessageEvent(VAL hasRead:布尔,VAL targetUserId:字符串?, VAL ageindays应用:中等,VAL inboxSize:智力){伴侣对象{从(inboxSizeProperty乐趣的:int ?, targetUserId:字符串?, matchedUser:用户?, initialNWays的:int?):SelectedMessageEvent {//处理逻辑}}}

我们从我们的视图模型调用将如下所示:

VAL事件= SelectedMessageEvent.from(INSIZE,用户id,matchedUser,nWays)sessionMetrics.fireSelectedMsg(事件)

这一切都说明

我们的视图模型现在已经减少责任。它创建一个事件,虽然它不知道怎么办。它发送该事件,其中的不知道具体实施的接口。这一切都导致改善测试。现在,我们可以在测试时断言,我们的分析层被调用。

现在的分析层是使用我们的自定义数据类与不需要处理得出的值。现在,我们明确什么该层需要。如果,在未来,我们希望取代我们的分析库或添加另一种实现方式,那只需要消耗我们的新数据类。

用于创建事件处理逻辑已经被分离,并且可以被测试。我们可以提供如我们预期事件数据类中生成的输入和验证的宽阵列。

我们的测试现在的作品中,我们可以在我们的视图模型验证正确的行为,我们可以验证我们的事件数据类是正确创建。

我希望你喜欢这篇文章,请随时与我联系上推特

在OkCupid的工作感兴趣吗?188bet金宝搏官网我们正在招聘