What's happening behind the scenes in XCTest's #testable? - ios

I know that
#testable import MyModule
gives ability to explore non-public members of MyModule from a "test" (built with "testTarget") module MyModuleTests.
I need the same functionality in my "non-test" module. Not in production, just in debug mode.
My question is: do you know how to do this?
And related (I think, harder question): what magic is actually happening behind #testable?

To answer your question, for debugging purposes, you can actually use this. Let's say you have a workspace MyAwesomeWkspace and a project inside MyAwesomeProject.
Now, create a new framework aka module called MyAwesomeModule. Inside that module create a non-public class called Person.
If you try to use the class Person inside MyAwesomeProject by doing import MyAwesomeModule and then something like let p = Person() you will have an error.
But if you do #testable import MyAwesomeModule, the magic happens and you can now use the class.
Basically #testable allows you to test things that you didn't declare public. The annotation only works with import as you can see it here.
So in order to work, the target is compiled with -enable-testing so that you can have access to non-public members. At least based on what's here
Because, by default, the debug build configuration is compiled with -enable-testing, the example I showed you will work. But if you change the build config to release, you'll see an error saying Module .. was not compiled for testing since the release config is not built with the flag.
The Swift access control model, as described in the Access Control
section of The Swift Programming Language (Swift 4), prevents an
external entity from accessing anything declared as internal in an app
or framework. By default, to be able to access these items from your
test code, you would need to elevate their access level to at least
public, reducing the benefits of Swift’s type safety.
Xcode provides a two-part solution to this problem:
When you set the Enable Testability build setting to Yes, which is
true by default for test builds in new projects, Xcode includes the
-enable-testing flag during compilation. This makes the Swift entities declared in the compiled module eligible for a higher level of access.
When you add the #testable attribute to an import statement for a
module compiled with testing enabled, you activate the elevated access
for that module in that scope. Classes and class members marked as
internal or public behave as if they were marked open. Other entities
marked as internal act as if they were declared public.
More here
Late edit: One of the cool parts of swift is that is open source. So if you want to dive deep into the "magic", check it out: https://github.com/apple/swift

#testable import <module_name> and -enable-testing
[Swift access modifiers]
[Swift module]
consumer side uses #testable import -> producer side should use `-enable-testing` flag
producer side: enable -enable-testing
Enable Testability(ENABLE_TESTABILITY) - YES
Other Swift Flags(OTHER_SWIFT_FLAGS) - -enable-testing
consumer side: #testable
internal(default) and public access level for class is visible for current module as open
internal(default) access level for others(struct, enum) is visible for current module as public
If you build test schema(consumer) with #testable but producer doesn't include -enable-testing you get
Module '<module_name>' was not compiled for testing
Some experiments:
SomeModule
internal class SomeInternalClass {
internal func foo() { }
}
public class SomePublicClass {
public func foo() { }
}
internal class SomeInternalStruct {
internal func foo() { }
}
internal enum SomeInternalEnum: String {
case foo = "hello world"
}
Tests: If you omit #testable next errors will occur
import XCTest
#testable import ExperimentsTests
class ExperimentsTestsTests: XCTestCase {
func testExample() throws {
let someInternalStruct = SomeInternalStruct() //Cannot find 'SomeInternalStruct' in scope
someInternalStruct.foo()
let someInternalEnum = SomeInternalEnum(rawValue: "") //Cannot find 'SomeInternalEnum' in scope
SomeInternalEnum.foo //Cannot find 'SomeInternalEnum' in scope
}
class SomePublicSubClass: SomePublicClass { //Cannot inherit from non-open class 'SomePublicClass' outside of its defining module
override func foo() { } //Overriding non-open instance method outside of its defining module
}
class SomeInternalSubClass: SomeInternalClass { //Cannot find type 'SomeInternalClass' in scope
override func foo() { } //Method does not override any method from its superclass
}
}

Related

Is there #friend functionality to accompany #protected?

I'm using the Dart annotation #protected because I want to limit a member to only be called from a single other class, like so
class Foo {
#projected
void doSomethingSpecial() {}
}
class Bar {
final foo = Foo();
consumeDoSomethingSpecial() {
foo.doSomethingSpecial();
}
}
Understandably, foo.doSomethingSpecial() triggers the warning
The member 'doSomethingSpecial' can only be used within instance members of subclasses...
In the C++ world I would annotate Bar as a friend of Foo to permit this call but I'm not seeing the equivalent annotation in Dart?
I did see that I can suppress a warning by adding
// ignore: the_appropriate_lint_rule
above the line with the warning but I'm not seeing a lint rule that applies to using the #protected annotation?
"friend" in Dart is essentially implemented by ensuring that the identifiers are "library-local" (begin with underscore), and all friends are in the same library. A library in Dart is typically just a single file; however, using part/part-of, it can span multiple files.
Any reference to an underscore-prefixed identifier is in-scope for the same library, but out of scope anywhere else, even if you import that file.

Make swift internal function unavailable for Test Target

I want to create a function or convenience init of a class that can not be available for TestTarget when import with #testable import, I am not sure it's possible but looking for any way to restrict it.
class A {
// Should not be accessible in Test Target
func foo() {
}
}
In Testing when #testable import it should not be available.
/**********
UPDATE
***********/
Problem statement
The Long param init method is used with convenience methods to provide default arguments but then in testing, I don't want to access that convenience method with the default argument because it's easy to forget to provide mock depedancy.
There are two solutions to do so.
You can add the following code in your init
#if DEBUG
if let _ = NSClassFromString("XCTest") {
fatalError("Should not be used in unit test.")
}
#endif
With this solution, you can still use this init in your unit test, but it will crash at runtime.
Add a new configuration Testing, you can do this by Select your debug config -> Click + -> Duplicate Debug, and rename it to Testing
then in your main target, add a new flag TESTING in the config Testing.
Next go to Product -> Scheme -> Edit Scheme Set build configuration to Testing for your Test
Finally add this around your init.
#if !TESTING
init() {}
#endif
Now, you should not be able to use this init in your unit test.
Mark that function as Private. Then it will not be available in test target.
You cannot access private entities from another module and this also applies to test targets. That is what access control is for.
You could just import the module as opposed to #testable import.

Cannot import package in unit tests for a Jenkins Shared Library

I'm attempting to create unit tests for a JenkinsShared library using Gradle in order to run the test tasks.
I've followed this tutorial which upon conclusion one has a working test suite for a shared library for functions within the vars folder (with the unit tests in src/test/groovy/*Test.groovy).
However, in our internal shared jenkins library we followed a more object oriented style and isolated functionality into a package of classes in the format: src/org/company/*.groovy.
The problem arises when attempting to import said package into a unit test class. In the tutorial, the functions are imported using the loadScript method this method fails when loading a class which is dependent on another file.
Take the class:
package tests
import org.junit.*
import com.lesfurets.jenkins.unit.*
import static groovy.test.GroovyAssert.*
import org.company.UtilFactory
class UtilFactoryTest extends BasePipelineTest {
#Test
void testCall() {
def util = UtilFactory.getUtil("hello")
assertEquals true, true
}
}
src/org/company/UtilFactory.groovy
package org.company
class UtilFactory implements Serializable {
static Util instance
static Util getUtil(script=null) {
if (!(UtilFactory.instance)) {
if (!script) {
// Throws an exception if on the first call to getUtil the
// script parameter is null.
throw new ScriptUndefinedException("script parameter null on initial call to getUtil")
}
UtilFactory.instance = new Util(script)
}
return UtilFactory.instance
}
}
class ScriptUndefinedException extends Exception {
// Parameterless Constructor
public ScriptUndefinedException() {}
// Constructor that accepts a message
public ScriptUndefinedException(String message)
{
super(message);
}
}
Which gives me the exception:
jenkins-utilities/src/test/groovy/UtilFactoryTest.groovy: 7:
unable to resolve class org.company.UtilFactory
# line 7, column 1.
import org.company.UtilFactory
This may be more of a Gradle issue than a JenkinsShared Library. I've just spent a good portion of my day trying to figure out exactly what I'm doing wrong to no avail.
I would really appreciate any help to guide me in the right direction.
This library may be helpful getting your shared libraries to work in the unit test https://github.com/stchar/pipeline-sharedlib-testharness

How can I test a private or fileprivate function in project

I want to write some unit testing code for a manager class, the function I would write for is using some small private functions. I will prepare a lot if I testing the public function, so I want to test those private functions. But in test target I can't call the private function directly.
So I wanna ask, is there's a way to test them without change them from private to internal or public?
So I wanna ask, is there's a way to test them without change them from private to internal or public?
Add an internal function that does nothing but call the private function. Probably it's best to do it in an extension:
class Foo
{
fileprivate func myPrivateFunction(p: Int) { ... }
}
extension Foo
{
internal func testMyPrivateFunction(p: Int)
{
myPrivateFunc(p: p)
}
}
You can probably find a way of using conditional compilation to omit the extension for release builds e.g.
#if DEBUG
extension Foo
{
internal func testMyPrivateFunction(p: Int)
{
myPrivateFunc(p: p)
}
}
#endif
Not tested the conditional thing to see if it works, it's borrowed from here https://ericasadun.com/2018/04/18/forcing-compiler-errors-in-swift/
Sadly no. There isn't a "VisibleForTesting" tag in Swift as there is in java.
However you can define a protocol which your manager class then implements including only the methods you want to test.
For example if your manager has a function called createViewModel that calls several private methods testing that the viewModel created matches that of what we expect we have implicitly tested the private methods work. You can set up your manager with different initial conditions to test all varieties and edge cases
I think you are looking for #testable imports. From Apple's documentation:
When you add the #testable attribute to an import statement for a
module compiled with testing enabled, you activate the elevated access
for that module in that scope. Classes and class members marked as
internal or public behave as if they were marked open. Other entities
marked as internal act as if they were declared public.
Interfaces are the solution.
This solution is a bit more complicated than the others, but can help you for multiple purposes, like uncoupling modules on your app.
Let's say you have a class Foo which has an object of type Bar, and you need to call doStuff().
Create a Protocol for Bar. So Foo is decoupled from Bar and becomes fully testable without exposing its content to Foo. Something like this:
protocol BarProtocol {
func doStuff()
}
class Bar:BarProtocol {
func doStuff() {
print("Hello world")
}
}
class Foo {
var bar:BarProtocol
init() {
self.bar = Bar()
self.bar.doStuff()
}
}

How to use Namespaces in Swift?

The documentation only mentions nested types, but it's not clear if they can be used as namespaces. I haven't found any explicit mentioning of namespaces.
I would describe Swift's namespacing as aspirational; it's been given a lot of advertising that doesn't correspond to any meaningful reality on the ground.
For example, the WWDC videos state that if a framework you're importing has a class MyClass and your code has a class MyClass, those names do not conflict because "name mangling" gives them different internal names. In reality, however, they do conflict, in the sense that your own code's MyClass wins, and you can't specify "No no, I mean the MyClass in the framework" — saying TheFramework.MyClass doesn't work (the compiler knows what you mean, but it says it can't find such a class in the framework).
My experience is that Swift therefore is not namespaced in the slightest. In turning one of my apps from Objective-C to Swift, I created an embedded framework because it was so easy and cool to do. Importing the framework, however, imports all the Swift stuff in the framework - so presto, once again there is just one namespace and it's global. And there are no Swift headers so you can't hide any names.
EDIT: In seed 3, this feature is now starting to come online, in the following sense: if your main code contains MyClass and your framework MyFramework contains MyClass, the former overshadows the latter by default, but you can reach the one in the framework by using the syntax MyFramework.MyClass. Thus we do in fact have the rudiments of a distinct namespace!
EDIT 2: In seed 4, we now have access controls! Plus, in one of my apps I have an embedded framework and sure enough, everything was hidden by default and I had to expose all the bits of the public API explicitly. This is a big improvement.
Answered by SevenTenEleven in the Apple dev forum:
Namespaces are not per-file; they're per-target (based on the
"Product Module Name" build setting). So you'd end up with something
like this:
import FrameworkA
import FrameworkB
FrameworkA.foo()
All Swift declarations are considered to be part of
some module, so even when you say "NSLog" (yes, it still exists)
you're getting what Swift thinks of as "Foundation.NSLog".
Also Chris Lattner tweeted about namespacing.
Namespacing is implicit in Swift, all classes (etc) are implicitly
scoped by the module (Xcode target) they are in. no class prefixes
needed
Seems to be very different what I have been thinking.
While doing some experimentation with this I ended up creating these "namespaced" classes in their own files by extending the root "package". Not sure if this is against best practices or if it has any implications I'm mot aware of(?)
AppDelegate.swift
var n1 = PackageOne.Class(name: "Package 1 class")
var n2 = PackageTwo.Class(name: "Package 2 class")
println("Name 1: \(n1.name)")
println("Name 2: \(n2.name)")
PackageOne.swift
import Foundation
struct PackageOne {
}
PackageTwo.swift
import Foundation
struct PackageTwo {
}
PackageOneClass.swift
extension PackageOne {
class Class {
var name: String
init(name:String) {
self.name = name
}
}
}
PackageTwoClass.swift
extension PackageTwo {
class Class {
var name: String
init(name:String) {
self.name = name
}
}
}
Edit:
Just found out that creating "subpackages" in above code wont work if using separate files. Maybe someone can hint on why that would be the case?
Adding following files to the above:
PackageOneSubPackage.swift
import Foundation
extension PackageOne {
struct SubPackage {
}
}
PackageOneSubPackageClass.swift
extension PackageOne.SubPackage {
class Class {
var name: String
init(name:String) {
self.name = name
}
}
}
Its throwing a compiler error:
'SubPackage' is not a member type of 'PackageOne'
If I move the code from PackageOneSubPackageClass.swift to PackageOneSubPackage.swift it works. Anyone?
Edit 2:
Fiddling around with this still and found out (in Xcode 6.1 beta 2) that by defining the packages in one file they can be extended in separate files:
public struct Package {
public struct SubPackage {
public struct SubPackageOne {
}
public struct SubPackageTwo {
}
}
}
Here are my files in a gist:
https://gist.github.com/mikajauhonen/d4b3e517122ad6a132b8
I believe this is achieved using:
struct Foo
{
class Bar
{
}
}
Then it can be accessed using:
var dds = Foo.Bar();
Namespaces are useful when you need to define class with the same name as class in existing framework.
Suppose your app has MyApp name, and you need to declare your custom UICollectionViewController.
You don't need to prefix and subclass like this:
class MAUICollectionViewController: UICollectionViewController {}
Do it like this:
class UICollectionViewController {} //no error "invalid redeclaration o..."
Why?. Because what you've declared is declared in current module, which is your current target. And UICollectionViewController from UIKit is declared in UIKit module.
How to use it within current module?
var customController = UICollectionViewController() //your custom class
var uikitController = UIKit.UICollectionViewController() //class from UIKit
How to distinguish them from another module?
var customController = MyApp.UICollectionViewController() //your custom class
var uikitController = UIKit.UICollectionViewController() //class from UIKit
Swift uses modules much like in python (see here and here) and as #Kevin Sylvestre suggested you can also use the nested types as namespaces.
And to extend the answer from #Daniel A. White, in WWDC they were talking about the modules in swift.
Also here is explained:
Inferred types make code cleaner and less prone to mistakes, while
modules eliminate headers and provide namespaces.
You can use extension to use the mentioned structs approach for namespacing without having to indent all of your code towards the right. I've been toying with this a bit and I'm not sure I'd go as far as creating Controllers and Views namespaces like in the example below, but it does illustrate how far it can go:
Profiles.swift:
// Define the namespaces
struct Profiles {
struct Views {}
struct ViewControllers {}
}
Profiles/ViewControllers/Edit.swift
// Define your new class within its namespace
extension Profiles.ViewControllers {
class Edit: UIViewController {}
}
// Extend your new class to avoid the extra whitespace on the left
extension Profiles.ViewControllers.Edit {
override func viewDidLoad() {
// Do some stuff
}
}
Profiles/Views/Edit.swift
extension Profiles.Views {
class Edit: UIView {}
}
extension Profiles.Views.Edit {
override func drawRect(rect: CGRect) {
// Do some stuff
}
}
I haven't used this in an app since I haven't needed this level of separation yet but I think it's an interesting idea. This removes the need for even class suffixes such as the ubiquitous *ViewController suffix which is annoyingly long.
However, it doesn't shorten anything when it's referenced such as in method parameters like this:
class MyClass {
func doSomethingWith(viewController: Profiles.ViewControllers.Edit) {
// secret sauce
}
}
Even though it is possible to implement namespaces using Framework and Libraries but the best solution is to use local packages using Swift Package Manager. Besides having access modifiers, this approach has some other benefits. As in Swift Package Manager, the files are managed based on the directory system, not their target member ship, you won't have to struggle with merge conflicts that arise frequently in teamworks. Furthermore, there is no need to set file memberships.
To check how to use local Swift packages refer to the following link:
Organizing Your Code with Local Packages
In case anyone was curious, as of June 10th 2014, this is a known bug in Swift:
From SevenTenEleven
"Known bug, sorry! rdar://problem/17127940 Qualifying Swift types by their module name doesn't work."

Resources