我应该模拟单元测试中的每个依赖项吗? [英] Should I mock every single dependencies in unit testing?

查看:39
本文介绍了我应该模拟单元测试中的每个依赖项吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

经过大量阅读,我发现人们建议在单元测试中模拟方法中的每个依赖项.

After a lot reading, I found that people advice to mock every dependencies in a method under unit test.

单元测试时是否应该模拟所有依赖项?

我应该什么时候模拟?

但是,我有一个方法可以创建对象列表(依赖项)并使用这些对象的方法来更改列表.在现实场景中,该方法是将客户端发送的数据负载转换为这些对象.下面是 Typescript 中的代码实现,但它只是给你一个想法.

However, I have a method that creates a list of objects (the dependencies) and uses the method these objects to alter the list. In the real-life scenario, the method is transforming data payload sent by the client into these objects. Below is the code implementation in Typescript but it just gives you an idea.

import {expect} from 'chai';

import 'mocha';

class Bar {
    private val: number;

    constructor(val: number) {
        this.val = val;
    }

    isValid(): boolean {
        return this.val !== 2;
    }

    canMergeWith(bar: Bar): boolean {
        return this.val === bar.val;
    }
}

class BarBuilder {
    constructor() {
    }

    createBars(payload: number[]): Bar[] {
        const bars: Bar[] = [];
        payload.forEach(p => {
            bars.push(new Bar(p));
        });

        const filtered = this.filterBars(bars);
        const merged = this.mergeBars(filtered);
        return merged;
    }

    private filterBars(bars: Bar[]): Bar[] {
        return bars.filter(b => b.isValid());
    }

    private mergeBars(bars: Bar[]): Bar[] {
        const merged: Bar[] = [];
        bars.forEach((bar, i) => {
            if (i > 0) {
                const before = bars[i - 1];
                if (!bar.canMergeWith(before)) {
                    merged.push(bar);
                }
            } else {
                merged.push(bar);
            }
        });
        return merged;
    }
}

describe('BarBuilder', () => {
    it('Should convert payload into valid bars', () => {
        const payload = [1, 2, 3, 4, 4, 5, 5];
        const barBuilder = new BarBuilder();
        const bars = barBuilder.createBars(payload);
        expect(bars).to.deep.equal([{val: 1}, {val: 3}, {val: 4}, {val: 5}]);
    });
});

目前,测试通过.但它违反了我应该模拟类中的每个依赖项的规则.

Currently, the test is passing. But it is violating the rule that I should mock every dependency in the class.

我的问题是我应该做以下哪一项:

My question which one of the following should I do:

  1. 我真的应该模拟 Bar 类以遵守该规则吗?

如果是这样,我会发现另一个困难.如果我模拟 Bar 类,我如何让模拟方法 canMergeWithisValid 根据它们的输入返回 true 或 false?难道不等同于重写类本身吗?

If so, I find another difficulty doing that. If I mock the class Bar, how do I make the mock methods canMergeWith and isValid return true or false based on their input? Isn't the equivalent as re-writting the class itself?

  1. 我应该重构我的代码吗?

我也在想问题可能在于我的代码无法测试.之前,方法canMergeWithisValid 属于BarBuilder.为了便于阅读,我将它们移到 Bar 中.但是现在,单元测试 BarBuilder 不再简单.

I was also thinking probably the problem is with my code being not testable. Before, the methods canMergeWith and isValid belonged to BarBuilder. For the sake of readability, I moved them inside Bar. But now, the unit testing BarBuilder is not trival anymore .

  1. 尽管测试违反了单元测试规则,但仍保持原样.

推荐答案

我发现人们建议在单元测试中模拟方法中的每个依赖项.

I found that people advice to mock every dependencies in a method under unit test.

有些人建议这样做,是的.在链接的线程中,更多人不同意.单元测试实际上是一个分裂的主题.

Some people advise this, yes. In the linked threads, more people disagree. Unit testing is actually a divisive subject.

当我第一次了解到没有被业界广泛接受的单元测试定义时,我感到很惊讶.人们经常谈论单元测试,好像只有一个定义,尤其是模拟一切".定义;但是如果您通读链接的线程,您会发现这种方法存在许多缺陷,尤其是这些测试的脆弱性.

It surprised me the first time I learned there is no definition of unit testing that is widely accepted across the industry. People often talk about unit testing as if there is one definition, especially the "mock everything" definition; but if you read through the linked threads, you will see numerous pitfalls of that approach, especially the fragility of those tests.

我认为模拟一切"定义仍然如此普遍,因为它是微不足道的.没有任何思考,只是嘲笑一切.另一种方法是考虑何时模拟,何时不模拟.那更难.你必须证明嘲笑是合理的.将单元测试定义为模拟一切"更容易.因为那样你就不会考虑嘲笑的目的是什么.这只是定义的一部分.

I think the "mock everything" definition remains so pervasive due to how trivial it is. There's no thinking involved, just mock everything. The alternative is to consider when to mock and when not to. That's harder. You have to justify mocking. It's easier to define unit testing as "mock everything" because then you don't think about what purpose the mocking serves. It's just part of the definition.

Ian Cooper谈论单元测试,正如 Kent Beck 在 TDD 上下文中定义的那样.我发现他的观点比模拟一切"更现代.方法.

Ian Cooper has given a talk on unit testing, as defined by Kent Beck in the context of TDD. I find his perspective to be more modern than the "mock everything" approach.

Kent 以一种非常特殊的方式使用单元测试.当他使用这个短语时,他的意思是您应该能够将所有测试作为一个套件一起运行,而不会影响其他测试的运行.隔离单元是测试.并且犯了很多错误,因为人们认为隔离单元是被测试的类.不是.

Kent uses unit test in a very specific way. When he uses the phrase, all he means is that you should be able to run all your tests together, as one suite, without one test being run impacting the other tests. The unit of isolation is the test. And a lot of mistakes have been made because people believe the unit of isolation is the class under test. It is not.

我的建议:当他们帮助你时创建模拟.不要创建模拟来满足规则.背后没有达成共识的规则.

My advice: create mocks when they help you. Don't create mocks to satisfy a rule. There is no rule with consensus behind it.

这篇关于我应该模拟单元测试中的每个依赖项吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆