Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Adds shared examples to Kiwi. #600

Merged
merged 3 commits into from
Apr 25, 2015

Conversation

ecaselles
Copy link
Member

This PR adds shared examples to the latest version of Kiwi (2.3.1). It is based on the original implementation proposed by @modocache in #442.

Shared examples allow developers to reuse a set of describe, context, and it nodes to test one or more classes. See the functional tests in Tests/Shared Examples/KWSharedExampleFunctionalTest.m for sample usage.

Addresses issues #175, #138.

Implementation Details

  • KWSharedExampleRegistry is a singleton that maintains a mapping of shared example names to KWSharedExample instances.
  • KWSharedExample is a data structure composed of a name and context block.
  • sharedExamplesFor creates a KWSharedExample and registers it.
  • itBehavesLike looks up the KWSharedExample with the given name and inserts its context block into the current example group.
  • SHARED_EXAMPLES_BEGIN, SHARED_EXAMPLES_END macros are used to ensure sharedExamplesFor are evaluated prior to any examples.
  • In order to allow assertion macros like should to be compiled outside of the implementation block of a KWSpec--like within sharedExamplesFor, for example--changed macro to reference global state by using KWSpec class method.
  • Changed all pragma marks referencing internal/private methods to Internal Methods.
  • Removed unused KWEncodingForVoidMethod function.

Sample Usage

// CruiserSpecHelpers.m

SHARED_EXAMPLES_BEGIN(CruiserSpecHelpers)

sharedExamplesFor(@"a cruiser", ^(NSDictionary *data) {
    __block Cruiser *cruiser = nil;
    beforeEach(^{
        cruiser = [[data[@"class"] alloc] initWithCallsign:@"Planet Express"];
    });

    it(@"has a callsign", ^{
        [[cruiser.callsign should] equal:@"Planet Express"];
    });
});

SHARED_EXAMPLES_END
// CruiserSpec.m

SPEC_BEGIN(CruiserSpec)

describe(@"Cruiser", ^{
    itBehavesLike(@"a cruiser", @{ @"class": [Cruiser class] });
});

describe(@"Carrier", ^{
    itBehavesLike(@"a cruiser", @{ @"class": [Carrier class] });
});

SPEC_END

modocache and others added 2 commits March 17, 2015 15:26
Shared examples allow developers to reuse a set of describe, context,
and it nodes to test one or more classes. See the functional tests in
`Tests/Shared Examples/KWSharedExampleFunctionalTest.m` for sample
usage.

Addresses issues kiwi-bdd#175, kiwi-bdd#138.

- `KWSharedExampleRegistry` is a singleton that maintains a mapping of
  shared example names to `KWSharedExample` instances.
- `KWSharedExample` is a data structure composed of a name and context
  block.
- `sharedExamplesFor` creates a `KWSharedExample` and registers it.
- `itBehavesLike` looks up the `KWSharedExample` with the given name and
  inserts its context block into the current example group.
- `SHARED_EXAMPLES_BEGIN`, `SHARED_EXAMPLES_END` macros are used to
  ensure `sharedExamplesFor` are evaluated prior to any examples.
- In order to assertion macros like `should` could be compiled outside
  of the implementation block of a `KWSpec`--like within a `sharedExamplesFor`,
  for example--changed macro to reference global state referenced by
  `KWSpec` class method.
- Changed all pragma marks referencing internal/private methods to
  `Internal Methods`.
- Removed unused `KWEncodingForVoidMethod` function.
@ecaselles ecaselles mentioned this pull request Mar 17, 2015
@ecaselles ecaselles changed the title Add shared examples Shared examples. Mar 17, 2015
@ecaselles ecaselles changed the title Shared examples. Adds shared examples to Kiwi. Mar 17, 2015
@fcy
Copy link

fcy commented Mar 18, 2015

👍 😍

@modocache
Copy link
Member

I'll defer to @sharplet, since he's more familiar with modern-day Kiwi, but I'm excited to see this released!!

When a failure occurs, are the correct lines highlighted in Xcode?

@ecaselles
Copy link
Member Author

@modocache So am I! Since it is a very useful feature 🤘

About highlighting the failures, unfortunately the implementation is still the one you originally did. I have already started working on how to fix it 👷 (suggestions on how to do it are very much welcome), but since I really want this functionality to be available 🙏, I was hoping to get this one merged first and then open an issue to fix the highlighting of failures when running a shared example. Does it make sense?

@modocache
Copy link
Member

I think it's possible to have the correct lines highlighted by using macros for sharedExamplesFor and itBehavesLike...

#define sharedExamplesFor(name, block) kiwi_sharedExamplesFor(name, block, __FILE__, __LINE__)

...and define functions that take file name and line number parameters:

void kiwi_sharedExamplesFor(NSString *name,
                            KWSharedExampleBlock block,
                            NSString *file, 
                            NSUInteger line);

Then, use the file and line arguments when reporting failures via -[XCTestCase recordFailureWithDescription:inFile:atLine:expected:] (Kiwi does it here).

That should work, provided you can fit it into Kiwi's framework of using KWExample objects to report failures. Good luck! 🎏

@ecaselles
Copy link
Member Author

@modocache Thanks for the hints! Let's see what I achieve.

On the meanwhile, let's hope @sharplet can get this one out soon 😊

@bilby91
Copy link

bilby91 commented Mar 26, 2015

+1

@sharplet
Copy link
Contributor

Ah sorry everyone, really want to take a look at this! @ecaselles really appreciate you taking the time and hopefully I can get a chance this weekend!

@sharplet
Copy link
Contributor

Just one thought while I'm thinking it:

@modocache what are your thoughts on passing through an NSDictionary instead of a Class? I've been using Specta recently and the flexibility has already come in handy. For example, I was able to pass a block into the dictionary, and then invoke it with different parameters in different instances of itBehavesLike(). Very powerful!

@ecaselles
Copy link
Member Author

@sharplet Would the NSDictionary passed in the itBehavesLike() have a defined (maybe extendable) structure? Or would it be totally up open to hold anything inside?

@sharplet
Copy link
Contributor

It's totally open. So here's how you could pass a class in:

itBehavesLike(@"a foobar", @{
    @"class": [Foobar class],
});

sharedExamplesFor(@"a foobar", ^(NSDictionary *data) {
    __block id foobar;

    beforeEach(^{
        foobar = [data[@"class"] new];
    });

    // ...
});

But you could pass anything, for example a block that lazily instantiates your object.

@ecaselles
Copy link
Member Author

@sharplet Done! Now you can use the itBehavesLike() and sharedExamplesFor() by passing an NSDictionary as you described above. I have also updated the examples in the PR to reflect this changes.

Please 🙏 review the changes and let me know your thoughts 💭

@ecaselles
Copy link
Member Author

BTW, I was thinking on renaming the folder/group with the source files to Shared Examples and moving it to the same level of Nodes, Matchers, Mocking, Stubbing, etc. Instead of being Core/Examples. Does it make sense?

@ecaselles
Copy link
Member Author

pinging @sharplet and @modocache for feedback 🙇

@delebedev
Copy link

can someone review and merge this one, please?

@sharplet sharplet merged commit e9db977 into kiwi-bdd:master Apr 25, 2015
@sharplet
Copy link
Contributor

Very sorry for the wait everyone—this is great. Thanks @ecaselles for doing the work to bring this into the future! 😉

In the interest of expediency, I went ahead and implemented your suggestion to move Classes/Core/Examples to Classes/Shared Examples.

🎉

@sharplet sharplet mentioned this pull request Aug 3, 2015
@ecaselles
Copy link
Member Author

@modocache a side note about failure highlighting 📝

After spending a few hours understanding how to highlight the line where the itBehavesLike() is and implementing a few examples to play with it, I have realized there is probably a good reason for not doing it: when your shared example contains multiple tests.

IMHO, it is common to write shared examples containing multiple tests and in that case, the current implementation of highlighting the failing line in the test is more useful, because it is very easy to see which tests are not passing and the class/subject invoking the shared examples appears first in the description (e.g. 'Carrier, behaves like a cruiser, has a callsign' [FAILED]).

Otherwise, if we highlight the itBehavesLike() line, finding which of the shared examples tests are failing will require you to read the the full description... which is usually quite long (specially if you nest contexts) and it will not fit the screen, forcing you to read go to the Console output scream:.

Finally, I have checked Specta's behavior on these situations and it also highlights the failing tests inside the shared example 👏.

Therefore, I believe there is no need to modify the current implementation 🎉

This was referenced Jun 6, 2016
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants