With required protocol and provided protocol, it's much easier to mock dependencies in unit test.
Let's see a login module depending to a net service:
// Login module
class LoginService {
func login(account: String, password: String, completion: (Result<LoginError>) -> Void) {
// Use RequiredNetServiceInput to request the network
let netService = Router.makeDestination(to: RoutableService<RequiredNetServiceInput
>())
let request = makeLoginRequest(account: account, password: password)
netService?.POST(request: request, completion: completion)
}
}
// Dependencies
extension RoutableService where Protocol == RequiredNetServiceInput {
init() {}
}
Objective-C Sample
Use RequiredNetServiceInput to request the network// Login module
@interface LoginService : NSObject
@end
@implementation LoginService
- (void)loginWithAccount:(NSString *)account password:(NSString *)password completion:(void(^)(Result *result))completion {
// Use RequiredNetServiceInput to request the network
id<RequiredNetServiceInput> netService = [ZIKRouterToService(RequiredNetServiceInput) makeDestination];
Request *request = makeLoginRequest(account, password);
[netService POSTRequest:request completion: completion];
}
@end
// Dependencies
@protocol RequiredNetServiceInput <ZIKServiceRoutable>
- (void)POSTRequest:(Request *)request completion:(void(^)(Result *result))completion;
@end
You don't need to import a real network module in unit test. Just write a fake network module:
class MockNetService: RequiredNetServiceInput {
func POST(request: Request, completion: (Result<NetError>) {
completion(.success)
}
}
// Register the fake module
ZIKAnyServiceRouter.register(RoutableService<RequiredNetServiceInput>(),
forMakingService: MockNetService.self) { (config, router) -> EditorViewProtocol? in
return MockNetService()
}
Objective-C Sample
@interface MockNetService : NSObject <RequiredNetServiceInput>
@end
@implementation MockNetService
- (void)POSTRequest:(Request *)request completion:(void(^)(Result *result))completion {
completion([Result success]);
}
@end
// Register the fake module
[ZIKServiceRouter registerServiceProtocol:ZIKRoutable(EditorViewInput) forMakingService:[MockNetService class]];
Unit test code:
class LoginServiceTests: XCTestCase {
func testLoginSuccess() {
let expectation = expectation(description: "end login")
let loginService = LoginService()
loginService.login(account: "account", password: "pwd") { result in
expectation.fulfill()
}
waitForExpectations(timeout: 5, handler: { if let error = $0 {print(error)}})
}
}
Objective-C Sample
@interface LoginServiceTests : XCTestCase
@end
@implementation LoginServiceTests
- (void)testLoginSuccess {
XCTestExpectation *expectation = [self expectationWithDescription:@"end login"];
[[LoginService new] loginWithAccount:@"" password:@"" completion:^(Result *result) {
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:5 handler:^(NSError * _Nullable error) {
!error? : NSLog(@"%@", error);
}];
}
@end