gpt4 book ai didi

unit-testing - 如何对从包中导入的方法进行 stub 和 spy 事件?

转载 作者:行者123 更新时间:2023-12-01 22:44:20 25 4
gpt4 key购买 nike

我是一名 JavaScript 和 Python 开发人员。这是一个使用 jestjs 的单元测试代码片段测试框架:
index.ts :

import dotenv from 'dotenv';

export class OsEnvFetcher {
constructor() {
const output = dotenv.config();
if (output.error) {
console.log('Error loading .env file');
process.exit(1);
}
}
}
index.test.ts :

import { OsEnvFetcher } from './';
import dotenv from 'dotenv';

describe('OsEnvFetcher', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('should pass', () => {
const mOutput = { error: new Error('parsed failure') };
jest.spyOn(dotenv, 'config').mockReturnValueOnce(mOutput);
const errorLogSpy = jest.spyOn(console, 'log');
const exitStub = jest.spyOn(process, 'exit').mockImplementation();
new OsEnvFetcher();
expect(dotenv.config).toBeCalledTimes(1);
expect(errorLogSpy).toBeCalledWith('Error loading .env file');
expect(exitStub).toBeCalledWith(1);
});
});

单元测试的结果:
 PASS  stackoverflow/todo/index.test.ts (11.08s)
OsEnvFetcher
✓ should pass (32ms)

console.log
Error loading .env file

at CustomConsole.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)

----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 50 | 100 | 100 |
index.ts | 100 | 50 | 100 | 100 | 6
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 12.467s

示例中的测试方法在使用 Arrange, Act, Assert的js项目中很常见图案。由于 dotenv.config()方法会做一些文件系统 I/O 操作,它有一个副作用。所以我们将为它制作一个 stub 或模拟。这样,我们的单元测试就没有副作用,并且在隔离的环境中进行测试。

这同样适用于 python。我们可以使用 unittest.mock模拟对象库做同样的事情。我对这些单元测试方法非常满意。

现在,我转行,尝试做同样的事情。代码在这里:
osEnvFetcher.go
package util

import (
"log"
"os"
"github.com/joho/godotenv"
)

var godotenvLoad = godotenv.Load

type EnvFetcher interface {
Getenv(key string) string
}

type osEnvFetcher struct {}

func NewOsEnvFetcher() *osEnvFetcher {
err := godotenvLoad()
if err != nil {
log.Fatal("Error loading .env file")
}
return &osEnvFetcher{}
}

func (f *osEnvFetcher) Getenv(key string) string {
return os.Getenv(key)
}
osEnvFetcher_test.go :

package util

import (
"testing"
"fmt"
)

func TestOsEnvFetcher(t *testing.T) {
old := godotenvLoad
defer func() { godotenvLoad = old }()
godotenvLoad = func() error {
return
}
osEnvFetcher := NewOsEnvFetcher()
port := osEnvFetcher.Getenv("PORT")
fmt.Println(port)
}

测试用例没有完成。我不知道应该如何模拟、 stub 或 spy godotenv.Load方法(相当于 dotenv.config() )和 log.Fatal方法?我找到了这个模拟包 - mock .但是 godotenv包没有接口(interface),它是由函数组成的。

我正在寻找一些方法,例如 jest.mock(moduleName, factory, options)jest.spyOn(object, methodName) .或者,像 stubsspiessinonjs .或者,像 spyOnjasmine .这些方法几乎可以涵盖任何测试场景。无论是使用 DI 还是直接导入模块。

我看到了一些方法,但它们都有自己的问题。

例如。 https://stackoverflow.com/a/41661462/6463558 .

我需要 stub 十个有副作用的方法怎么办?在运行测试之前,我需要将一个包的这些方法分配给 10 个变量,并用测试用例中的 mocked 版本替换它们十次。它是不可扩展的。也许我可以创建一个 __mocks__目录并将所有模拟版本对象放入其中。这样我就可以在所有测试文件中使用它们。

使用依赖注入(inject)更好。通过这种方式,我们可以将模拟对象传递给函数/方法。但有些场景是直接导入包,使用包的方法(第3个或内置标准库)。这也是我的问题中的场景。我认为这种情况是不可避免的,肯定会出现在代码的某一层。

我可以使用 jestjs 轻松处理这种情况, 例如
util.js ,
exports.resolveAddress = function(addr) {
// ...
const data = exports.parseJSON(json);
return data;
}
exports.parseJSON = function(json) {}
main.js :

// import util module directly rather than using Dependency Injection
import util from './util';
function main() {
return util.resolveAddress();
}
main.test.js :

import util from './util';
const mJson = 'mocked json data';
jest.spyOn(util, 'parseJSON').mockReturnValueOnce(mJson)
const actual = main()
expect(actual).toBe('mocked json data');

我直接进口 util模块和模拟 util.parseJSON方法及其返回值。我不确定 Go 的包是否可以做到这一点。目前,这些问题是 安排问题。

此外,我还需要检查方法是否确实被调用,以确保代码逻辑和分支是正确的。 (例如,使用 .toBeCalledWith()jestjs 方法)。这是 断言 问题。

提前致谢!

最佳答案

这是我基于此 answer 的解决方案:
osEnvFetcher.go :

package util

import (
"log"
"os"
"github.com/joho/godotenv"
)

var godotenvLoad = godotenv.Load
var logFatal = log.Fatal

type EnvFetcher interface {
Getenv(key string) string
}

type osEnvFetcher struct {}

func NewOsEnvFetcher() *osEnvFetcher {
err := godotenvLoad()
if err != nil {
logFatal("Error loading .env file")
}
return &osEnvFetcher{}
}

func (f *osEnvFetcher) Getenv(key string) string {
return os.Getenv(key)
}
osEnvFetcher_test.go :

package util

import (
"testing"
"errors"
)


func mockRestore(oGodotenvLoad func(...string) error, oLogFatal func(v ...interface{})) {
godotenvLoad = oGodotenvLoad
logFatal = oLogFatal
}

func TestOsEnvFetcher(t *testing.T) {
// Arrange
oGodotenvLoad := godotenvLoad
oLogFatal := logFatal
defer mockRestore(oGodotenvLoad, oLogFatal)
var godotenvLoadCalled = false
godotenvLoad = func(...string) error {
godotenvLoadCalled = true
return errors.New("parsed failure")
}
var logFatalCalled = false
var logFatalCalledWith interface{}
logFatal = func(v ...interface{}) {
logFatalCalled = true
logFatalCalledWith = v[0]
}
// Act
NewOsEnvFetcher()
// Assert
if !godotenvLoadCalled {
t.Errorf("godotenv.Load should be called")
}
if !logFatalCalled {
t.Errorf("log.Fatal should be called")
}
if logFatalCalledWith != "Error loading .env file" {
t.Errorf("log.Fatal should be called with: %s", logFatalCalledWith)
}
}

覆盖测试的结果:
☁  util [master] ⚡  go test -v -coverprofile cover.out
=== RUN TestOsEnvFetcher
--- PASS: TestOsEnvFetcher (0.00s)
PASS
coverage: 80.0% of statements

报道html记者:

enter image description here

关于unit-testing - 如何对从包中导入的方法进行 stub 和 spy 事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61952251/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com