我有两个单元测试,testUserRegistration和testEmailConfirmation。注册测试首先运行,我创建了一个唯一的电子邮件来注册一个随机整数,例如autoTest1234@test.com或autoTest4928@test.com。在下一个测试testEmailConfirmation中,我需要使用在上一个测试中创建的用户名。如何将其从一种测试方法发送到另一种方法?
答案 0 :(得分:2)
这不是您应该如何考虑单元测试的方法。
首先,您不应该对测试顺序进行评估:您的测试可能会以随机顺序运行,Xcode决定,而不是您。您的测试应该彼此独立,并且您应该能够以任何顺序运行它们。在Xcode 10中,我们将进行测试并行化,并且您真的不希望它们彼此依赖。最后,这种随机化可以确保您的测试仅由于副作用而无法正常工作。
基本上,您需要两个测试。
第一个看起来像这样:
func testUserRegistration() {
// Given [an email address / username]
// When [you run the registration flow]
// Then [you assert that it worked]
}
现在该测试通过了,您可以认为您的用户注册有效,并且不应再对其进行测试。没必要。这就是为什么将其称为“单元测试”的原因,因为您可以测试“单元”。
现在在第二次测试中,您要测试的单元是“确认”部分:
func testEmailConfirmation() {
// Given [new email/username, you can call the registration method with this new email here]
// When [you apply your confirmation flow]
// Then [you assert that the confirmation is working]
}
您可以调用配置有另一封随机电子邮件的用户注册功能(而不是测试,真实功能),并根据该电子邮件进行断言。
希望有帮助!
答案 1 :(得分:0)
您可以通过将XCTestCase
子类化来实现。
在设置过程中,如果您在使用信号灯进行网络连接时阻止线程,然后在完成网络事件后取消对线程的锁定。
还可以将变量添加到子类中,以便可以随时访问所有测试中的变量。
对于目标C,您必须将文件拆分为.h和.m,以便能够对新的集成测试类进行子类化。
Objective-C 实现示例:
IntegrationTest.h
#import
@interface IntegrationTest : XCTestCase
// Add properties here you want your test cases to inherit.
// Added a user property as an example.
@property (nonatomic, strong, nullable) User *user;
@end
IntegrationTest.m
@implementation IntegrationTest
-(void)setUp {
[super setUp];
__weak IntegrationTest *wSelf = self;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[User registerUserWithEmail:@"SomeRandomEmail" completion:^(User *user, NSError * error) {
wSelf.user = user;
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
-(void)tearDown {
[super tearDown];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// Do any networking related to tearing down such as deleting the user you just created
[self.user deleteWithCompletion:^(NSError *error) {
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
下面是一个示例 Swift 的实现:
class IntegrationTest: XCTestCase {
user: User?
override func setUp() {
super.setup()
// Use a semaphore to block
let semaphore = DispatchSemaphore(value: 0)
weak var wSelf = self
registerUser(email: "SomeRandomEmail" ,completion: { (user, error) in
// Signal the semaphore for the tests to start running again
// Do additional work, set variables, etc. here
wSelf?.user = user
semaphore.signal()
})
if semaphore.wait(timeout: DispatchTime.now() + .seconds(10)) == .timedOut {
// If something goes wrong in the networking handle it here
print("Networking failed")
}
}
override func tearDown() {
let semaphore = DispatchSemaphore(value: 0)
if let nonNilUser = user {
// Delete the user or do additional networking for the teardown in the same manner
user.delete(completion: { (error) in
// Do additional tear down work here
semaphore.signal()
}
} else {
semaphore.signal()
}
}
}
您现在所要做的就是子类IntegrationTest类,并在 Swift 中调用super.setUp()
和super.tearDown()
,如果是,则调用[super setUp]
和[super tearDown]
Objective-C 。
我已经将这种结构用于集成测试两年多了,该测试需要网络和身份验证才能取得成功。
答案 2 :(得分:0)
似乎您不是在进行单元测试,而是在进行集成测试或E2E测试(我直觉另一端是在远程服务器上的某处)。这不是一件坏事,但让我们弄清楚术语:)。请注意,端到端测试在相当多的条件下都可能导致误报,例如暂时的网络不可达,因此它们不像单元测试那样健壮。
对以上内容进行抽象,您可以覆盖类setUp
,并将随机生成的电子邮件存储在此处:
@interface MyTestCase()
@property (class) NSString *email;
@property (class) NSString *password;
@end
@implementation MyTestCase
+ (void)setUp {
[super setUp];
self.email = [self someRandomEmailGenerator];
self.password = [self someRandomPasswordGenerator];
}
- (void)test_registration {
// register with MyTestCase.email, MyTestCase.password
}
- (void)test_signup() {
// signup with MyTestCase.email, MyTestCase.password
}
但是,请注意,就像其他人所说的那样,由于Xcode具有自己的逻辑,因此不能保证注册测试会在注册之前进行。目前,它按字母顺序进行测试,但是可能会有所变化,而并行化则可能使您头痛。
答案 3 :(得分:-1)
您可以尝试使用segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC: nameofdestinationVC = segue.destination as! nameofdestinationVC
destinationVC.string = string var in other VC!
}