drewbourne / mockolate

fake chocolate, mock objects and test spies for AS3
http://mockolate.org/
MIT License
146 stars 26 forks source link

Overriding return values #53

Closed ghost closed 12 years ago

ghost commented 12 years ago

This isn't currently supported but is very useful for when we write unit tests which have matrix's of data/expected results.

i.e. var testMatrix : Array = [ {foo: "0", bar:10, mockthis:"5", mockThat:3, expected:Number.POSITIVE_INFINITY}, //FIXME - pretty sure it should not be that!! {foo: "", bar:10, mockthis:"5", mockThat:3, expected:Number.POSITIVE_INFINITY}, {foo: "1", bar:10, mockthis:"2", mockThat:3, expected:3}, {foo: "1", bar:10, mockthis:"2", mockThat:3, expected:3}, {foo: "5", bar:10, mockthis:"1", mockThat:1, expected:2}, {foo: "2", bar:10, mockthis:"1", mockThat:1, expected:5}, {foo: "2", bar:10, mockthis:"8", mockThat:1, expected:1}, {foo: "2", bar:10, mockthis:"1", mockThat:8, expected:1}, ];

        for ( var i : int = 0; i < testMatrix.length; i++){
            var test : Object = testMatrix[i];
            stub(model.thing).method("getFoo").noArgs().returns(test.mockThat);
            stub(model.thing).getter("mockThis").noArgs().returns(test.mockThis);
            model.bar = test.bar;
            data.foo = test.foo;
            model.doStuff();
            assertThat("test " + i, model.somethingFunky, equalTo(test.expected));
        }

//names have been changed to protect the innocent.

I've fixed this locally by updating the following method in mockingCoverture.as -- too busy to do a pull request/blog post, thought others might appreciate this code here, and perhaps you might implement it at some point.

great work - thanks..

pectation(expectation:Expectation):Expectation { var currentIndex : int = _expectations.length; var currentExpectation : Expectation; var isReplacingExpectation : Boolean; for (var i : int = 0; i < _expectations.length; i++) { currentExpectation = _expectations[i] as Expectation; if ( currentExpectation.name == expectation.name && currentExpectation.invocationType == expectation.invocationType){ currentIndex = i; isReplacingExpectation = true; break; } } _expectations[currentIndex] = expectation;

        var arrayToUse : Array = _expectationsAsMocks ? _mockExpectations : _stubExpectations;
        if ( isReplacingExpectation){
            currentIndex = arrayToUse.indexOf(currentExpectation);  
        } else {
            currentIndex = arrayToUse.length; 
        }
        arrayToUse[currentIndex] = expectation;

        expectationAdded();

        return expectation;
    }
drewbourne commented 12 years ago

I don't like the idea of overriding or mutating existing Expectations. The example case you've shown could be implemented slightly differently to get the same result without modifying Mockolate.

// this creates a new Thing with no existing expectations
model.thing = nice(Thing); 
stub(model.thing).method("getFoo").noArgs().returns(test.mockThat);
stub(model.thing).getter("mockThis").noArgs().returns(test.mockThis);

Another option (although it may not be appropriate for your use case), is .returns() can take multiple arguments, and will return them in order for each subsequent invocation, repeating the last value.

stub(model.thing).method("getFoo").noArgs().returns(test.mockThat).returns(1, 2, 3);

assertThat(model.thing.getFoo(), equalTo(1);
assertThat(model.thing.getFoo(), equalTo(2);
assertThat(model.thing.getFoo(), equalTo(3);
assertThat(model.thing.getFoo(), equalTo(3);

Are you using FlexUnit 4? It has a runner called Parameterized which is better suited to matrix style tests. I have created an example of using FlexUnit Parameterized Test with Mockolate.

georgejecook commented 12 years ago

Ok,

thanks for taking the time. As it goes, I am aware of paramterized tests but I find the flexunit 4 parameterized tests clunky and harder to debug than the style I've written in this defect for many kinds of tests. I have some tests that if I was to rewrite using the parameterized style, would have to be split into 3 almost identical tests, just because of mockoloate's policy of not allowing expectation overrides.

However I can see the benefit of keeping expectations immutable; but is there not a middle ground? Perhaps we could explicitly have an override method? That way we can choose, and the syntax for overriding could provide protection from misuse..

e.g stub(model.thing).override.method("getFoo")...etc.etc

I don't think I'm the only person who hates the FBuilder integration with parameterized tests, how long it takes to write them for certain cases, and the inflexibility we get when using them with mockolate - a lot of code has to be rewritten, things split into separate tests etc.. it's a pita. :)