-
Notifications
You must be signed in to change notification settings - Fork 511
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
let blocks in nested contexts are executed multiple times #433
Comments
Thanks for the report. I'm planning to take a look into this; you're right that the behaviour doesn't seem quite correct. I have a feeling that we may need to somehow mark a let node as being executed in a given context. I don't think it's quite as simple as that though: for example, what if So it's worth looking into, however I'm not 100% certain yet that the behaviour is necessarily incorrect. Would it be possible for you to post a spec that shows exactly what you mean by the mock losing configuration in an inner context? |
Ah, I see. I was wondering why it didn't just work like Unfortunately, I seem to have deleted the exact spec that initially caused me to look into this, but I've put something else together (below). From doing this exercise, I think there's only an issue if one mixes Besides effectively allowing multiple #import <Kiwi/Kiwi.h>
@protocol Foo <NSObject>
- (NSString*)status;
@end
@interface ExampleClass : NSObject
@property (strong, nonatomic) id<Foo> foo;
@end
@implementation ExampleClass @end
SPEC_BEGIN(KiwiLetBugDemoSpec)
describe(@"Example class with beforeEach", ^{
__block id subject;
__block id testFoo;
beforeEach(^{
testFoo = [KWMock mockForProtocol:@protocol(Foo)];
subject = [ExampleClass new];
[subject setFoo:testFoo];
});
context(@"status method", ^{
beforeEach(^{
[testFoo stub:@selector(status) andReturn:@"bar"];
});
it(@"should receive message", ^{
[[[[subject foo] status] should] equal:@"bar"]; // passes
});
});
});
describe(@"Example class with let", ^{
let(testFoo, ^{ return [KWMock mockForProtocol:@protocol(Foo)]; });
let(subject, ^{ return [ExampleClass new]; });
beforeEach(^{
[subject setFoo:testFoo];
});
context(@"status method", ^{
beforeEach(^{
// this is not the same testFoo that was assigned to the subject above
[testFoo stub:@selector(status) andReturn:@"bar"];
});
it(@"should receive message", ^{
[[[[subject foo] status] should] equal:@"bar"]; // fails
});
});
});
SPEC_END |
I encountered same issue. SPEC_BEGIN(FooSpec)
describe(@"foo", ^{
let(foo, ^id{ return @"foo"; });
beforeEach(^{
NSLog(@"outside before: %@", foo);
});
context(@"bar", ^{
let(foo, ^id{ return @"foobar"; });
beforeEach(^{
NSLog(@"inside before: %@", foo);
});
it(@"should work", ^{
NSLog(@"it: %@", foo);
});
});
});
SPEC_END This code prints following output
I expect to get I ran same kind of spec by rspec and it works as I expected. describe "foo" do
let(:foo) { "foo" }
before do
puts "outside before: #{foo}"
end
context "bar" do
let(:foo) { "foobar" }
before do
puts "inside before: #{foo}"
end
it "should work" do
puts "it: #{foo}"
end
end
end output
rspec behavior is "overwrite all lets by most inside of let" but kiwi does not work like it. |
@y310 what you noticed is a side effect of the fact that you only have one test, declared in the inner context. Basically when rspec evaluates the inner "it" it also evaluates all "let" and "before" declaration, "let" having priority here. So in your example the outer "before" is executed in the context of the inner test, thus the inner "let" is the one that decides the value of "foo". Below is a modified version of your tests, if you run them you'll see that the outer "let" is correctly used for the outer tests: describe "foo" do
let(:foo) { "foo" }
before do
puts "outside before: #{foo}"
end
it "outside test" do
puts "outside test: #{foo}"
end
context "bar" do
let(:foo) { "foobar" }
before do
puts "inside before: #{foo}"
end
it "should work" do
puts "it: #{foo}"
end
end
end Output
|
Just lost an hour of work because of this. |
+1 on replicating RSpec's behavior here. |
Any progress on this ? |
I'm trying out the new "let" feature, and I found that a mock I partly configured in an outer context was losing that configuration when used in an inner context. Digging into the code, it seems that it's build a "let tree", combining all the ancestor let notes with those at the current level. But the
performExample:withBlock:
code inKWContextNode
already builds a tree of contexts to process before executing anit
block (so that outerbeforeEach
andafterEach
blocks still get executed before every innerit
), so the outerlet
nodes are getting executed twice.Demo:
Output:
I don't think this was the intended behavior --- I would have expected it to work the same as
beforeEach
--- but I wanted to check before making any attempt to change it. At best, it's doing unnecessary work, recreating the value multiple times per example, but in the case I ran into, it actually broke the test.The text was updated successfully, but these errors were encountered: