如何在erlang中进行依赖注入和模拟?

Mat*_*ari 21 erlang unit-testing dependency-injection mocking

在Java中编写代码时,通过模拟协作对象,使用组合依赖注入非常有帮助,从而可以轻松地进行纯单元测试.

我发现在Erlang中做同样的事情并不那么简单,而且代码更脏.

这可能是我的错,因为我对Erlang很新,并且对JUnit,EasyMock和java接口非常沉迷......

假设我有这个愚蠢的功能:

%% module mymod
handle_announce(Announce) ->
    AnnounceDetails = details_db:fetch_details(Announce),
    AnnounceStats = stats_db:fetch_stats(Announce),
    {AnnounceDetails, AnnounceStats}.
Run Code Online (Sandbox Code Playgroud)

在单元测试时mymod,我只想证明details_dbstats_db使用正确的参数调用,并且正确使用了返回值.在其他地方测试details_dbstats_db生成正确值的能力.

为了解决这个问题,我可以这样重构我的代码:

%% module mymod
handle_announce(Announce, [DetailsDb, StatsDb]) ->
    AnnounceDetails = DetailsDb:fetch_details(Announce),
    AnnounceStats = StatsDb:fetch_stats(Announce),
    {AnnounceDetails, AnnounceStats}.
Run Code Online (Sandbox Code Playgroud)

并以这种方式进行测试(基本上将调用直接存入测试模块):

%% module mymod_test
handle_announce_test() ->
    R = mymod:handle_announce({announce, a_value}, [?MODULE, ?MODULE, ?MODULE]),
    ?assertEqual({details,stats}, R).

fetch_details({announce, a_value}) ->
    details.

fetch_stats({announce, a_value}) ->
    stats.
Run Code Online (Sandbox Code Playgroud)

它工作正常,但应用程序代码变脏了,我总是要随身携带那些丑陋的模块列表.

我尝试了几个模拟库(erlymock和(另一个),但我并不满意.

你如何对你的erlang代码进行单元测试?

谢谢!

Gor*_*rie 22

这里要考虑两件事......

您需要将所有代码分成两个不同类型的模块:

  • 纯功能模块(又名无副作用模块)
  • 具有副作用的模块

(您应该阅读并确保您了解其中的差异 - 最典型的副作用 - 以及示例代码中的副作用 - 正在写入数据库).

纯功能模块变得微不足道.每个导出的函数(根据定义)在放入相同的值时总是返回相同的值.您可以使用Richard Carlsson和Mickael Remond编写的EUnit/Assert框架.Bish-bash-bosh,工作很好'不...

关键是大约90%的代码应该是纯粹的功能模块 - 你会大大减少你的问题.(你可能认为这不是'解决'你的问题,只是'减少'它 - 你会大致正确......)

一旦实现了这种分离,对具有副作用的模块进行单元测试的最佳方法是使用标准测试框架.

我们这样做的方式不是使用模拟对象 - 而是在init_per_suite或init_per_test函数中加载数据库,然后自己运行模块......

最好的方法是尽快直接进行系统测试,因为单元测试很难维护 - 所以足够的单元测试可以让你进行系统测试往返而不再需要(甚至更好的删除) db单元尽快测试).

  • 对具有副作用的模块进行单元测试的问题是它们的维护成本。让我举一个具体的例子。在构建系统时,我们必须进行大量性能调整和重组。每次我们这样做时,我们都必须重新调整数据库架构和其他东西。进行系统测试意味着我们可以直接启动并进行测试,测试套件会告诉我们是否失败。如果我们有很多单元测试——它们将被绑定到 db 表示并且必须被重写。 (2认同)