<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div><blockquote type="cite" class=""><div class="">On Jul 27, 2016, at 5:40 PM, David Owens II &lt;<a href="mailto:david@owensd.io" class="">david@owensd.io</a>&gt; wrote:</div><div class=""><div dir="auto" class=""><div class="">While arguably true, that's a philosophical debate. There are plenty of reasons to use @testable, and if that's what people are using, then I think there is a valid concern here.&nbsp;</div></div></div></blockquote><div><br class=""></div>There are absolutely plenty of reasons to use @testable. &nbsp;But if you're a library developer, and you're writing a test that <i class="">specifically</i>&nbsp;is validating that a third party will be able to use your library the way you want it to be used, that is the very definition of black-box testing, and you should not be using a @testable import in that specific test. &nbsp;The compiler's standard behavior if you don't use a @testable import is *exactly* the custom linter that you're talking about writing.</div><div><br class=""></div><div>Like Jordan said, @testable imports are file-specific, so you can freely mix tests that use them with tests that don't. &nbsp;If you have common testing infrastructure that needs internal access to your library, that's pretty easy to put in its own file, too.</div><div><br class=""></div><div>John.</div><div><br class=""><blockquote type="cite" class=""><div class=""><div dir="auto" class=""><div class=""><br class="">Sent from my iPhone</div><div class=""><br class="">On Jul 27, 2016, at 5:22 PM, John McCall &lt;<a href="mailto:rjmccall@apple.com" class="">rjmccall@apple.com</a>&gt; wrote:<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div class=""><blockquote type="cite" class=""><div class="">On Jul 27, 2016, at 4:41 PM, David Owens II via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:</div><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">I brought this up in review, but there seems to be a serious testability problem here because of how special @testable is.</div><div class=""><br class=""></div><div class=""><pre style="box-sizing: border-box; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 14px; margin-top: 0px; margin-bottom: 0px; line-height: 1.45; word-wrap: normal; padding: 16px; overflow: auto; background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal; color: rgb(51, 51, 51);" class=""><span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// This class is not subclassable outside of ModuleA.</span>
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">public</span> <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">class</span> NonSubclassableParentClass {
    <span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// This method is not overridable outside of ModuleA.</span>
    <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">public</span> <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">func</span> <span class="pl-en" style="box-sizing: border-box; color: rgb(121, 93, 163);">foo</span>() {}

    <span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// This method is not overridable outside of ModuleA because</span>
    <span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// its class restricts its access level.</span>
    <span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// It is not invalid to declare it as `open`.</span>
    open <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">func</span> <span class="pl-en" style="box-sizing: border-box; color: rgb(121, 93, 163);">bar</span>() {}

    <span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// The behavior of `final` methods remains unchanged.</span>
    <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">public</span> <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">final</span> <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">func</span> <span class="pl-en" style="box-sizing: border-box; color: rgb(121, 93, 163);">baz</span>() {}
}</pre><div class=""><br class=""></div></div><div class="">In a unit test, I *can* subclass `NonSubclassableParentClass`, the access level of `NonSubclassableParentClass` is irrelevant. There’s now no programatic way to ensure that I’m actually testing the contract that I had intended to provide to the consumers of my framework (that my class is subclassable). Is this really the intention?</div></div></div></blockquote><div class=""><br class=""></div>A "black box" unit test emulating consumer behavior has no business using a @testable import. &nbsp;It should just use the external API of the library.</div><div class=""><br class=""></div><div class="">John.<br class=""><div class=""><br class=""></div></div><div class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><br class=""></div><div class="">The “fix”, on the authors end, is to create another target that consumes the output framework to ensure my contract is actually what I wanted (and make sure that it’s not a special test target!). No one is going to do this.</div><div class=""><br class=""></div><div class="">Sure, maybe a code review might catch it. Or I can write a custom linter to validate for this. Do we really want `open` members in non `open` types? The issue with `public` members on `internal` types is much less concerning as the `internal` type isn’t being exposed to others to begin with.</div><div class=""><br class=""></div><div class="">-David</div><div class=""><br class=""></div><br class=""><div class=""><blockquote type="cite" class=""><div class="">On Jul 27, 2016, at 3:18 PM, Scott James Remnant via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">I realize that there’s no review needed, but I actually wanted to give a hearty 👏 to the authors and commenters of this proposal, because I genuinely think we’ve reached something good in the result.<div class=""><br class=""></div><div class="">The selling point for me is this:</div><div class=""><br class=""></div><div class=""><pre style="box-sizing: border-box; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 14px; margin-top: 0px; margin-bottom: 0px; line-height: 1.45; word-wrap: normal; padding: 16px; overflow: auto; background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal; color: rgb(51, 51, 51);" class=""><span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// This is allowed since the superclass is `open`.</span>
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">class</span> SubclassB <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">:</span> SubclassableParentClass {
    <span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// This is invalid because it overrides a method that is</span>
    <span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// defined outside of the current module but is not `open'.</span>
    <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">override</span> <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">func</span> <span class="pl-en" style="box-sizing: border-box; color: rgb(121, 93, 163);">foo</span>() { }

    <span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// This is allowed since the superclass's method is overridable.</span>
    <span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// It does not need to be marked `open` because it is defined on</span>
    <span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);">// an `internal` class.</span>
    <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">override</span> <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">func</span> <span class="pl-en" style="box-sizing: border-box; color: rgb(121, 93, 163);">bar</span>() { }
}
</pre></div><div class=""><br class=""></div><div class="">This feels super-clean; it gives Library developers `open` for their APIs, without confusing app developers, and still requires that sub-classing Library developers think about `open`.</div><div class=""><br class=""></div><div class="">Good job, everyone!</div><div class=""><br class=""></div><div class="">Scott</div></div>_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class=""></div></blockquote></div><br class=""></div>_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class=""></div></blockquote></div><br class=""></div></blockquote></div></div></blockquote></div><br class=""></body></html>