当前位置: 首页 > 知识库问答 >
问题:

如何为类型化类编写单元测试?

陆耀
2023-03-14

我一直在学习使用Jest库编写JavaScript/TypeScript代码的单元测试。下面是一个我不知道如何处理的例子。它是用TypeScript输入的——只有两个公共方法和一个构造函数需要service1参数。

我想我需要测试两种情况:

>

  • 如果。attr是

    如果this.attr

    我的问题是:

    >

  • 我无法访问attr属性,它是私有的,我不知道如何为它赋值(可能是在测试中创建实例时,但idk如何)

    我不知道这是什么。服务1。get()函数为空。我没有在代码中看到它的任何实现,也不知道它是如何工作的。我应该把它作为参数传递给这个类的实例吗?

    我很困惑,在这个特定的例子中,我应该使用fakeTimers还是mock/spy?

    export class Class4 {
        private attr: number;
        private intervalId;
    
        constructor(private service1) { }
    
        public method() {
            this.intervalId = setInterval(() => {
                if (this.service1.get() > 42) {
                    this.end()
                } else {
                    this.attr++;
                }
            }, 100);
        }
    
        public getAttr() {
            return this.attr;
        }
    
        private end() {
            clearInterval(this.intervalId);
        }
    }
    

    我需要你的帮助在写作测试中开玩笑只有2种情况下,我描述。

    编辑。下面是基于这个类的简单测试。它没有分配一个值this.attr(我的参数的值得到assinged到service1虽然)和运行测试后,我收到一个错误消息

    预计:收到40个:未定义

    代码:

        it('should stop incrementing Class4.attr if it\'s > 42', () => {
            const class4 = new Class4(40);
            const attrVal = class4.getAttr();
            expect(attrVal).toBe(40);
        });
    
  • 共有1个答案

    卞成荫
    2023-03-14

    我不确定这是否有帮助,但下面是一个例子,说明如何使用Jest来测试类似的东西。

    这是你从typescript翻译到es6的代码,附带了一个轻松的假笑话实现。它在一个单独的脚本中,不涉及示例本身。

    在这个测试中,伪Jest只实现了所需的Jest匹配器:expect、toBeGreaterThan、not、havebeen calledtime。

    还有以下笑话实用程序:UseFaketTimer、AdvanceTimesByTime、ClearAllTimer和mock

    // self calling function is required to simulate Class4 module and for fake Jest mock to work
    (function() {
    // translated from typescript to es6
    class Class4 {
        attr = 0;
    
        intervalId = null;
    
        constructor(service1) {
            this.service1 = service1;
        }
    
        method() {
            this.intervalId = setInterval(() => {
                if (this.service1.get() > 42) {
                    this.end();
                } else {
                    this.attr++;
                }
            }, 100);
        }
    
        getAttr() {
            return this.attr;
        }
    
        end() {
            clearInterval(this.intervalId);
        }
    }
    // this is required to simulate Class4 module and for fake Jest mock to work
    window.Class4 = Class4;
    })();
    
    // even if we do not know exactly what Service is,
    // we know that it has a get method which returns a varying number.
    // so this implementation of Service will do
    // (it's ok since we're testing Class4, not Service)
    class ServiceImpl {
        v = 0;
        set(v) { this.v = v; }
        get() { return this.v; }
    }
    
    // after this call, jest will control the flow of
    // time in the following tests
    // (reimplements the global methods setInterval, setTimeout...etc)
    jest.useFakeTimers();
    
    // actually it should be jest.mock('<path to your module>')
    // but remember we're using a fake Jest working in SO's snippet)
    // now Class4 is a mock
    jest.mock(Class4);
    
    // we need a Service instance for a Class4 object to be instanciated
    const service = new ServiceImpl();
    
    const class4 = new Class4(service);
    
    it('Class4 constructor has been called 1 time', () => {
        expect(Class4).toHaveBeenCalledTimes(1);
    });
    
    it('should be incrementing Class4.attr if service.get() < 42', () => {
        // service.get() will return 40
        service.set(40);
    
        // storing initial attr val
        let lastAttrVal = class4.getAttr();
    
        // now class4 is running and should be incrementing
        class4.method();
    
        // jest controls the time, advances time by 1 second
        jest.advanceTimersByTime(1000);
    
        expect(class4.getAttr()).toBeGreaterThan(lastAttrVal);
    });
    
    it('should have been called Class4.end 0 time', () => {
        expect(Class4.mock.instances[0].end).toHaveBeenCalledTimes(0);
    });
    
    it('should stop incrementing Class4.attr if service.get() > 42', () => {
        // service.get() will now return 45, this should end class4
        // incrementation in the next interval
        service.set(45);
    
        // storing current attr val
        let lastAttrVal = class4.getAttr();
    
        jest.advanceTimersByTime(1000);
    
        expect(class4.getAttr()).not.toBeGreaterThan(lastAttrVal);
    
    });
    
    it('end should have been called end 1 time', () => {
        expect(Class4.mock.instances[0].end).toHaveBeenCalledTimes(1);
    });
    
    jest.clearAllTimers();
    <script type="text/javascript">
    window.jest = {};
    jest.useFakeTimers = () => {
        jest.oldSetTimeout = window.setTimeout;
        jest.oldSetInterval = window.setInterval;
        jest.oldClearTimeout = window.clearTimeout;
        jest.oldClearInterval = window.clearInterval;
        jest.time = 0;
        jest.runningIntervals = [];
        window.setInterval = (callback, delay) => {
            let interIndex = jest.runningIntervals.findIndex(i => i.cancelled);
            let inter = interIndex !== -1 && jest.runningIntervals[interIndex];
            if (!inter) {
                inter = {};
                interIndex = jest.runningIntervals.length;
                jest.runningIntervals.push(inter);
            }
            Object.assign(
                inter,
                {
                    start: jest.time,
                    last: jest.time,
                    callback,
                    delay,
                    cancelled: false
                }
            );
            callback();
            return interIndex;
        };
        window.clearInterval = idx => {
            jest.runningIntervals[idx].cancelled = true;
        };
        jest.advanceTimersByTime = advance => {
            for (const end = jest.time + advance;jest.time < end; jest.time++) {
                jest.runningIntervals.forEach(inter => {
                    if (!inter.cancelled && jest.time - inter.last >= inter.delay) {
                        inter.last = jest.time;
                        inter.callback();
                    }
                });
            }
        };
        jest.clearAllTimers = () => {
            jest.runningIntervals.length = 0;
            window.setTimeout = jest.oldSetTimeout;
            window.setInterval = jest.oldSetInterval;
            window.clearTimeout = jest.oldClearTimeout;
            window.clearInterval = jest.oldClearInterval;
        };
    };
    
    jest.resolve = (v) => {
      console.log(v ? 'PASS' : 'FAIL');
    }
    window.it = (description, test) => {
        console.log(description);
        test();
    };
    window.expect = (received) => {
      return {
        toBeGreaterThan: (expected) => jest.resolve(received > expected),
        not: {
          toBeGreaterThan: (expected) => jest.resolve(received <= expected),
        },
        toHaveBeenCalledTimes: (expected) => jest.resolve((received ? received.mock.calls.length : 0) === expected),
      }
    }
    jest.mock = (cls) => {
        if (cls.mock) return;
        const mock = {
            instances: [],
            calls: []
        }
        const proto0 = cls.prototype;
    
        function ClassMock(...args) {
            mock.calls.push(args);
            
            this.instance = new proto0.constructor(...args);
            this.instanceMock = {};
            mock.instances.push(this.instanceMock);
            Object.getOwnPropertyNames(proto0).forEach((member) => {
              if (member === 'constructor' || typeof proto0[member] !== 'function') return;
              this.instanceMock[member] = this.instanceMock[member] || { mock: { calls: [] } };
              this.instance[member] = (function(...args) {
                  this.instanceMock[member].mock.calls.push(args);
                  return proto0[member].apply(this.instance, [args]);
              }).bind(this);
          });
        }
    
        Object.getOwnPropertyNames(proto0).forEach((member) => {
            if (member === 'constructor' || typeof proto0[member] !== 'function') return;
            ClassMock.prototype[member] = function(...args) {
                return this.instance[member](...args);
            }
        });
        
        
        ClassMock.mock = mock;
        window[proto0.constructor.name] = ClassMock;
    }
    </script>
     类似资料:
    • 试图弄清楚我是否可以使用spring kafka和spring kafka测试为@KafkaListener编写单元测试。 我的听众课。 我的测试类别: 我的测试配置类: 有什么简单的方法可以做到这一点吗? 或者我应该以其他方式测试@KafkaListener?在单元测试中,如何确保在Kafka中收到新消息时调用@KafkaListener。

    • 在尝试连接数据库并获得连接尝试失败时,所有这些测试用例都失败了。和异常,因为它没有获取信息。 我对spring boot framework和JUnit几乎是新手,希望帮助我理解这一点以及编写单元测试用例的正确方法。

    • 一些背景知识,以防我为实际想要实现的目标解决了错误的问题:我的目标是编写一个函数: 它检查和是否具有相同的类型并相等,然后返回提供的(我们当时知道它与相同--T可能不是内射的,但它是一个函数!)或,否则。 我已经找到了一个解决办法,将违规的包装在中,使用,然后再次打开包装: 这种解决方法是完全可以接受的,但我希望去掉虚假的类型。

    • 我对Spring boot和JUnit是新手。我在Spring Boot中有一个Rest服务,在那里我接收请求,使用请求参数查询数据库,从查询中接收结果并将其作为响应发送。 我的控制器代码如下所示: 我的JdbcTemplate被作为一个单独类中的bean处理,如下所示 我的代码运行良好。 现在我想使用JUnit4为我的控制器编写单元测试。我使用MockMvc发送请求,但我的单元测试从未起飞。它总

    • 问题内容: 我有一个Java课。如何进行 单元测试? 就我而言,我有课做一个二进制和。它需要两个数组,将它们求和,然后返回一个新的二进制数组。 问题答案: 使用正确的输入定义正常情况下的预期和期望输出。 现在,通过声明一个类来实现测试,将其命名为任何东西(通常是类似TestAddingModule之类的东西),并向其添加testAdd方法(即,类似于下面的方法): 编写一个方法,并在其上方添加@T