如何在 Typescript 中对私有方法进行单元测试
How to unit test private methods in Typescript
当我尝试在 Class 中对 私有方法进行单元测试时出现错误,因为 私有方法只能在 [=34] 中访问=]。在这里,我为我的 class 和 mocha 测试添加了示例片段。请为我提供实现私有方法单元测试的解决方案。
Class 姓名:Notification.ts
class Notification {
constructor() {}
public validateTempalte() {
return true;
}
private replacePlaceholder() {
return true;
}
}
单元测试:
import {Notification} from 'Notification';
import * as chai from "chai";
describe("Notification", function(){
describe('#validateTempalte - Validate template', function() {
it('it should return success', function() {
const result = new Notification()
chai.expect(result.validateTempalte()).to.be.equal(true);
});
});
describe('#replacePlaceholder - Replace Placeholder', function() {
it('it should return success', function() {
const result = new Notification()
// As expected getting error "Private is only accessible within class"
chai.expect(result.replacePlaceholder()).to.be.equal(true);
});
});
});
作为解决方法,目前,我正在将函数 replacePlaceholder 的访问说明符更改为 public。但我认为这不是一种有效的方法。
由于在 class 之外无法访问私有方法,您可以使用另一个 public 方法,该方法在 Notification class 中调用 replace Placeholder() 然后测试 public方法。
技术上,在当前版本的 TypeScript 中,私有方法仅 compile-time 检查为私有 - 因此您可以调用它们。
class Example {
public publicMethod() {
return 'public';
}
private privateMethod() {
return 'private';
}
}
const example = new Example();
console.log(example.publicMethod()); // 'public'
console.log(example.privateMethod()); // 'private'
我提到这个只是因为你问了怎么做,而这就是你可以做的。
正确答案
但是,该私有方法必须由其他方法调用...否则根本不会被调用。如果您测试其他方法的行为,您将在使用它的上下文中覆盖私有方法。
如果您专门测试私有方法,您的测试将与实现细节紧密耦合(即,如果重构实现,则不需要更改好的测试)。
免责声明
如果您仍然在私有方法级别进行测试,编译器 将来可能会 更改并使测试失败(即如果编译器使方法 "properly" private,或者如果 ECMAScript 的未来版本添加了可见性关键字等)。
一种省略 Typescript 检查的可能解决方案是动态访问 属性(不知道它是否好)。
myClass['privateProp']
或方法:myClass['privateMethod']()
在我的例子中,我使用对象的原型来访问私有方法。很好用,TS不骂人
例如:
class Example {
private privateMethod() {}
}
describe() {
it('test', () => {
const example = new Example();
const exampleProto = Object.getPrototypeOf(example);
exampleProto.privateMethod();
})
}
如果您使用静态方法,则使用 exampleProto.<strong>constructor</strong>.privateMethod();
.
在 HolgerJeromin 的评论中,comment issue 有一个仍然使用 属性 语法的简洁解决方案。
解决方案是将您的对象 / class 类型转换为 any
。
示例:
(<any>myClass).privateMethod();
const value = (<any>myClass).privateValue;
(myClass as any).privateMethod();
const value = (myClass as any).privateValue;
此方法满足编译器以及VSCode语法高亮。
这是我在 issue that talks about this
中的一些笔记
- 通过字符串访问更常见,尽管我不明白为什么它可能更安全。
- 这些功能是有意设计的,因此它们的帮助大于阻碍。
- 可能有一种方法可以禁用此类功能,这样人们就不会将此代码复制并粘贴到生产环境中。
"noImplicitAny": true,
可能 有助于 tsconfig.json
将私有函数抽出到separate/stand单独的函数中,但不要导出到外部。
这在语义上是正确的,因为毕竟 — 私有函数是私有的,不应被任何人访问 除了 class 本身。
我的主观解决方案:您可以定义一个新的testing-only接口,通过添加私有方法作为(隐式public)接口方法.然后,将实例化对象转换为这个新的测试类型。这满足 tsc
和 VS code
类型检查。你的例子和我的解决方案:
interface INotification {
validateTemplate(): boolean,
}
class Notification implements INotification {
constructor() {}
public validateTemplate() {
return true;
}
private replacePlaceholder() {
return true;
}
}
测试:
import {Notification} from 'Notification';
import * as chai from "chai";
interface INotificationTest extends INotification {
replacePlaceholder(): boolean;
}
describe("Notification", function(){
describe('#validateTemplate - Validate template', function() {
it('it should return success', function() {
const result = new Notification() as INotificationTest;
chai.expect(result.validateTemplate()).to.be.equal(true);
});
});
describe('#replacePlaceholder - Replace Placeholder', function() {
it('it should return success', function() {
const result = new Notification() as INotificationTest;
// Works!
chai.expect(result.replacePlaceholder()).to.be.equal(true);
});
});
});
优点:
tsc
和vs code
不抱怨
IntelliSense
(或任何其他自动完成)有效
- 简单(主观)
- 如果您不想定义原始接口 (
INotification
),您可以完全定义测试接口 (INotificationTest
) 而不是以相同的方式扩展和转换它。
缺点:
- 添加了样板文件
- 需要更新和同步两个界面
- 显式转换为非原始类型可能会引入错误。
我让你来决定这是否值得。就我而言,积极因素超过消极因素。我已经用 jest
对此进行了测试,但我认为 mocha.js
在这里没有什么不同。
编辑:但一般来说我会同意 回答
当我尝试在 Class 中对 私有方法进行单元测试时出现错误,因为 私有方法只能在 [=34] 中访问=]。在这里,我为我的 class 和 mocha 测试添加了示例片段。请为我提供实现私有方法单元测试的解决方案。
Class 姓名:Notification.ts
class Notification {
constructor() {}
public validateTempalte() {
return true;
}
private replacePlaceholder() {
return true;
}
}
单元测试:
import {Notification} from 'Notification';
import * as chai from "chai";
describe("Notification", function(){
describe('#validateTempalte - Validate template', function() {
it('it should return success', function() {
const result = new Notification()
chai.expect(result.validateTempalte()).to.be.equal(true);
});
});
describe('#replacePlaceholder - Replace Placeholder', function() {
it('it should return success', function() {
const result = new Notification()
// As expected getting error "Private is only accessible within class"
chai.expect(result.replacePlaceholder()).to.be.equal(true);
});
});
});
作为解决方法,目前,我正在将函数 replacePlaceholder 的访问说明符更改为 public。但我认为这不是一种有效的方法。
由于在 class 之外无法访问私有方法,您可以使用另一个 public 方法,该方法在 Notification class 中调用 replace Placeholder() 然后测试 public方法。
技术上,在当前版本的 TypeScript 中,私有方法仅 compile-time 检查为私有 - 因此您可以调用它们。
class Example {
public publicMethod() {
return 'public';
}
private privateMethod() {
return 'private';
}
}
const example = new Example();
console.log(example.publicMethod()); // 'public'
console.log(example.privateMethod()); // 'private'
我提到这个只是因为你问了怎么做,而这就是你可以做的。
正确答案
但是,该私有方法必须由其他方法调用...否则根本不会被调用。如果您测试其他方法的行为,您将在使用它的上下文中覆盖私有方法。
如果您专门测试私有方法,您的测试将与实现细节紧密耦合(即,如果重构实现,则不需要更改好的测试)。
免责声明
如果您仍然在私有方法级别进行测试,编译器 将来可能会 更改并使测试失败(即如果编译器使方法 "properly" private,或者如果 ECMAScript 的未来版本添加了可见性关键字等)。
一种省略 Typescript 检查的可能解决方案是动态访问 属性(不知道它是否好)。
myClass['privateProp']
或方法:myClass['privateMethod']()
在我的例子中,我使用对象的原型来访问私有方法。很好用,TS不骂人
例如:
class Example {
private privateMethod() {}
}
describe() {
it('test', () => {
const example = new Example();
const exampleProto = Object.getPrototypeOf(example);
exampleProto.privateMethod();
})
}
如果您使用静态方法,则使用 exampleProto.<strong>constructor</strong>.privateMethod();
.
在 HolgerJeromin 的评论中,comment issue 有一个仍然使用 属性 语法的简洁解决方案。
解决方案是将您的对象 / class 类型转换为 any
。
示例:
(<any>myClass).privateMethod();
const value = (<any>myClass).privateValue;
(myClass as any).privateMethod();
const value = (myClass as any).privateValue;
此方法满足编译器以及VSCode语法高亮。
这是我在 issue that talks about this
中的一些笔记- 通过字符串访问更常见,尽管我不明白为什么它可能更安全。
- 这些功能是有意设计的,因此它们的帮助大于阻碍。
- 可能有一种方法可以禁用此类功能,这样人们就不会将此代码复制并粘贴到生产环境中。
"noImplicitAny": true,
可能 有助于tsconfig.json
将私有函数抽出到separate/stand单独的函数中,但不要导出到外部。
这在语义上是正确的,因为毕竟 — 私有函数是私有的,不应被任何人访问 除了 class 本身。
我的主观解决方案:您可以定义一个新的testing-only接口,通过添加私有方法作为(隐式public)接口方法.然后,将实例化对象转换为这个新的测试类型。这满足 tsc
和 VS code
类型检查。你的例子和我的解决方案:
interface INotification {
validateTemplate(): boolean,
}
class Notification implements INotification {
constructor() {}
public validateTemplate() {
return true;
}
private replacePlaceholder() {
return true;
}
}
测试:
import {Notification} from 'Notification';
import * as chai from "chai";
interface INotificationTest extends INotification {
replacePlaceholder(): boolean;
}
describe("Notification", function(){
describe('#validateTemplate - Validate template', function() {
it('it should return success', function() {
const result = new Notification() as INotificationTest;
chai.expect(result.validateTemplate()).to.be.equal(true);
});
});
describe('#replacePlaceholder - Replace Placeholder', function() {
it('it should return success', function() {
const result = new Notification() as INotificationTest;
// Works!
chai.expect(result.replacePlaceholder()).to.be.equal(true);
});
});
});
优点:
tsc
和vs code
不抱怨IntelliSense
(或任何其他自动完成)有效- 简单(主观)
- 如果您不想定义原始接口 (
INotification
),您可以完全定义测试接口 (INotificationTest
) 而不是以相同的方式扩展和转换它。
缺点:
- 添加了样板文件
- 需要更新和同步两个界面
- 显式转换为非原始类型可能会引入错误。
我让你来决定这是否值得。就我而言,积极因素超过消极因素。我已经用 jest
对此进行了测试,但我认为 mocha.js
在这里没有什么不同。
编辑:但一般来说我会同意