问题
- have written test case for jumping method,
- but its not going inside onloadend method seat.onloadend, when I see code coverage report.
- in createSpyObj i called loadend but still its not going inside
- can you guys tell me how to fix it.
- providing my code and test case below.
- I am trying to wite test case for each and every line.
jumping(inputValue: any): void {
var that = this;
var file: File = inputValue.files[0];
var seat: FileReader = new FileReader();
seat.onloadend = (e) => {
this.encodeBase64 = seat.result;
that.fileSelect = $("#laptop").val().replace(/^.*\\/, "");
if (that.fileSelect == '') {
that.dragDrop = that.swimming;
} else {
that.dragDrop = "";
that.dragDrop = that.fileSelect;
}
}
$('.running').show();
if (inputValue.files.length > 0) {
var wholeQuantity = 0;
wholeQuantity = inputValue.files[0].size / 1048576; //size in mb
if (wholeQuantity > 5) {
$('.stars').show();
$("#laptop").val('');
this.fileSelect = "";
}
seat.readAsDataURL(file);
}
}
describe('Jasmine Unit Tests: hand-Basketball-Manage-mobiles', () => {
let rainSPORTSService:SPORTSService;
let SPORTSService: SPORTSService;
let decodeService: DecodeService;
let BasketballChainComponent: handBasketballChain;
let kickViewrainsComponent: kickViewrains;
let tiger: Componenttiger<handBasketballChain>;
let raintiger: Componenttiger<kickViewrains>;
let foodktiger: Componenttiger<foodkCarousel>;
let kendotiger: Componenttiger<KendoGridComponent>;
let foodkComponent:foodkCarousel;
let kendoComponent:KendoGridComponent;
beforeEach(async(() => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
TestBed.configureTestingModule({
imports: [HttpModule, FormsModule,BrowserModule ],
declarations:[handBasketballChain, KendoGridComponent,ProgressCircle,
kickViewrains,handLeftSliderComponent,foodkCarousel,kickmobiles],
providers:[SPORTSService,DecodeService,recentPinnedHistoryService,
{provide: Router, useClass: RouterModule}, validationService,saveService,
ChainService]
}).compileComponents().then(() =>{
foodktiger = TestBed.createComponent(foodkCarousel);
kendotiger = TestBed.createComponent(KendoGridComponent);
foodkComponent = foodktiger.componentInstance;
kendoComponent = kendotiger.componentInstance;
tiger = TestBed.createComponent(handBasketballChain);
BasketballChainComponent = tiger.componentInstance;
SPORTSService = tiger.debugElement.injector.get(SPORTSService);
tiger.componentInstance.kickmobiles.SPORTSService=tiger.debugElement.injector.get(SPORTSService);
tiger.componentInstance.kickViewrains.SPORTSService=tiger.debugElement.injector.get(SPORTSService);
decodeService = tiger.debugElement.injector.get(DecodeService);
BasketballChainComponent.inputfoodkCarousel = foodkComponent; //jasmine.createSpy('foodkCarousel');//.andCallFake(function(msg) { return this });
BasketballChainComponent.kickmobiles.gridkendo=kendoComponent;
})}
));
it('Read kick mobile', (done) => {
let callFirstTime : boolean = true;
let url=
spyOn(BasketballChainComponent.kickmobiles.SPORTSService,'getResponse').and.
callFake(() => {
if(callFirstTime) {
callFirstTime = false; // Invoked by detectChanges()
return Observable.of([{
"mobileId": "100",
"mobileName": "http://localhost:3000/assets/js/actualairings.json",
"mobileType": "TITLE",
"mobileData": "YWZjYXJlZ2Vyamh2dmFyZWdoYnZi",
"notes": "",
"notesId": "100",
"elfDocID": "100",
"url": "http://localhost:3000/upload",
"date": "06/27/2017",
"addedByName": "Kamal",
"userID": "206509786",
"operationType": "create"
}]);
}
});
const fileReaderSpy = jasmine.createSpyObj('FileReader', ['readAsDataURL', 'onloadend']);
spyOn(window, 'FileReader').and.returnValue(fileReaderSpy);
BasketballChainComponent.kickmobiles.jumping({
files: "Untitled-2.txt"
});
var seat = new FileReader();
//seat.onloadend(e);
//BasketballChainComponent.kickmobiles.jumping.onloadend()
tiger.whenStable().then(() => {
done();
});
});
});
回答1:
Remember, the key to Unit Testing is to write small testable Units of code.Unit Testing - Wikipedia
You're on the right track for the most part, stubbing FileReader and so forth before calling the 'jumping' function. This is very much the way to test code that relies on another external library/function/framework. The relevant portion of the Wikipedia page for Unit Testing states
Because some classes may have references to other classes, testing a class can frequently spill over into testing another class. A common example of this is classes that depend on a database: in order to test the class, the tester often writes code that interacts with the database. This is a mistake, because a unit test should usually not go outside of its own class boundary, and especially should not cross such process/network boundaries because this can introduce unacceptable performance problems to the unit test-suite.
Here's the thing though, when you create your dummy FileReader or mock it never calls 'onloadend' because the mock/stub doesn't have that event and eventing system implemented. This means the mock is incomplete on your part. Wikipedia states
Instead, the software developer should create an abstract interface around the database queries, and then implement that interface with their own mock object. By abstracting this necessary attachment from the code (temporarily reducing the net effective coupling)
In your case, rather than a database, you'd be mocking the FileReader loadend event.
From a test perspective your current code needs a small refactor to become testable. The general objective of Unit Tests is to test small units of functionality in isolation.
The objective in unit testing is to isolate a unit and validate its correctness.
The 'jumping' function relies on a nested arrow function attached to onloadend. Your code has a direct call to that commented out in the test, I'm a little surprised that didn't work to up your code coverage to be honest and would suggest perhaps making sure your code coverage tool, probably Istanbul if you're using Jasmine is configured correctly.
With the above aside, you should refactor that nested function and instead create a named function that you can then call directly for your unit tests.
This is an (untested on my end) example of a better way to implement your function.
jumping(inputValue: any): void {
var that = this;
var file: File = inputValue.files[0];
var seat: FileReader = new FileReader();
// bind the arguments for the event handler, first arg will be 'this' of the
// loaded named function
// second is 'that' variable, seat is seat and the final 'e' variable is
// implicit and shouldn't be specified.
seat.onloadend = loaded.bind(seat, that, seat);
$('.running').show();
if (inputValue.files.length > 0) {
var wholeQuantity = 0;
wholeQuantity = inputValue.files[0].size / 1048576; //size in mb
if (wholeQuantity > 5) {
$('.stars').show();
$("#laptop").val('');
this.fileSelect = "";
}
seat.readAsDataURL(file);
}
}
loaded(that: any, seat: any, e: any): void { // now a testable named function
this.encodeBase64 = seat.result;
that.fileSelect = $("#laptop").val().replace(/^.*\\/, "");
if (that.fileSelect == '') {
that.dragDrop = that.swimming;
} else {
that.dragDrop = "";
that.dragDrop = that.fileSelect;
}
}
An example of a test that will cover all the lines of code of the 'loaded' function as written above is as follows:
describe('test suite', function () {
var old$ = $;
afterEach(function () {
$ = old$;
});
it('covers all lines and else path on if but does not actually test anything', function () {
$ = function () {
val: function () {
return 'Untitled-2.txt';
}
}; // stub JQuery
var seat = {
result: 'Base64encoded'
};
var scope = {};
var that = {
swimming: false,
dragDrop: null
};
BasketballChainComponent.kickmobiles.loaded.call(scope, that, seat, null);
});
it('covers all lines and on if but not else and does not actually test anything', function () {
$ = function () {
val: function () {
return '';
}
}; // stub JQuery
var seat = {
result: 'Base64encoded'
};
var scope = {};
var that = {
swimming: false,
dragDrop: null
};
BasketballChainComponent.kickmobiles.loaded.call(scope, that, seat, null);
});
});
Now please take note, in the real world you should never write tests just for code coverage that do not actually test the given functions. It will lead you into a false sense of security and not actually TEST your code. The MSDN has this to say:
The primary goal of unit testing is to take the smallest piece of testable software in the application, isolate it from the remainder of the code, and determine whether it behaves exactly as you expect.
An analogy of what you're doing would be as follows:
You're working as a car crash tester. Your job is to verify the car is safe in a crash. So a car is crashed at 10 km/h and you need to check it over.
You take a check list of things that you need to confirm. So in a 10 km/h crash you only expect paint to be scratched. So you look at the paint, if the paint is scratched but there's no other damage, the test passes. If the car is dented, the test fails.
That's a good test overall as it is testing something quantifiable and it's testing an intention.
What you're doing by trying to achieve 100% code coverage without actually testing functionality is crashing the car and then not verifying anything.
You're saying "Well I crashed the car, I don't really need to check it did what it's supposed to do in a crash so long as I crashed it right?".
Sure, you got 100% crash coverage by looking at the car, but by not actually testing it, you may as well not have even bothered. Code coverage is a useful tool for spotting code that isn't tested, it's not used to achieve the arbitrary metric of gaining full code coverage. Further reading on this and an excellent write up can be read at Broken promise of 100% code coverage
Essentially the crux of it is
The fact that it is easy to measure code coverage doesn’t make it a good metric though. You can get into trouble even if your code coverage is 100%.
I've omitted the code from the medium article, however it goes on to state:
This unit test produces the perfect 100% test coverage for the elementAtIndex: function. Does it prove that the function works correctly? The answer is, obviously, no. What happens when we exceed the array boundaries? Why did that happen? When you try to focus on the code coverage metric you write code that looks at the implementation of the tested function/method. But the implementation is not proven to be correct yet. That is the reason why we want to test it. Even with that simple function code coverage failed as a good metric to measure the quality of unit-tests.
Further, above I state that you should test the intention of your code. The Medium article also states this.
What to do instead? Don’t look at the actual implementation of the method, look at the contract instead. Look precisely at the outputs of the function/method for any specific inputs. Look at the side-effects that this function does or uses. Take into account the possible edge cases that might exist. List this information and make tests according to that.
Remember 100% code coverage doesn’t mean that your code is 100% correct.
I hope this helps you understand Unit Testing as a concept a little better.
来源:https://stackoverflow.com/questions/46017512/trying-to-write-test-cases-for-each-and-every-line