I am writing a REST app in Angular and I want to write unit tests for it (of course!). I have a controller which gets a list of blog posts from a REST service in json and puts the summaries into the $scope, so I can display them in the view.
我正在Angular中编写一个REST应用程序,我想为它编写单元测试(当然!)。我有一个控制器,它从json中的REST服务获取博客文章列表,并将摘要放入$ scope,所以我可以在视图中显示它们。
At first the blog posts were just displaying as text ie <p>Blog body</p>
, rather than rendering as parsed HTML, until I discovered that you can use ng-bind-html in conjunction with the $sce service. This now works fine in terms of displaying the blog posts correctly.
起初,博客文章只是显示为文本,即
博客主体 ,而不是呈现为已解析的HTML,直到我发现您可以将ng-bind-html与$ sce服务结合使用。这在正确显示博客文章方面现在可以正常工作。
The problem arises when unit testing. I am trying to mock a json response with some HTML and then test that my controller is correctly dealing with the HTML. Here is my code:
单元测试时出现问题。我试图用一些HTML模拟一个json响应,然后测试我的控制器是否正确处理HTML。这是我的代码:
Controller
.controller( 'HomeCtrl', function HomeController( $scope, $http, $sce ) {
$scope.posts = {};
$http.get('../drupal/node.json').success(function (data) {
var posts;
posts = data.list;
for(var i = 0; i < posts.length; i ++) {
posts[i].previewText = $sce.trustAsHtml(posts[i].body.summary);
posts[i].created = posts[i].created + '000'; // add milliseconds so it can be properly formatted
}
$scope.posts = posts;
});
})
unit test
describe('HomeCtrl', function() {
var $httpBackend, $rootScope, $sce, createController;
beforeEach(inject(function ($injector) {
// Set up the mock http service responses
$httpBackend = $injector.get('$httpBackend');
// Get hold of a scope (i.e. the root scope)
$rootScope = $injector.get('$rootScope');
// The $controller service is used to create instances of controllers
var $controller = $injector.get('$controller');
$sce = $injector.get('$sce');
createController = function() {
return $controller('HomeCtrl', {
'$scope': $rootScope
});
};
}));
it('should get a list of blog posts', function() {
var rawResponse = {
"list": [
{
"body": {
"value": "\u003Cp\u003EPost body.\u003C\/p\u003E\n",
"summary": "\u003Cp\u003ESummary.\u003C\/p\u003E\n"
},
"created": "1388415860"
}
]};
var processedResponse = [{
"body": {
"value": "\u003Cp\u003EPost body.\u003C\/p\u003E\n",
"summary": "\u003Cp\u003ESummary.\u003C\/p\u003E\n"
},
"created": "1388415860000",
previewText: $sce.trustAsHtml("\u003Cp\u003ESummary.\u003C\/p\u003E\n")
}];
$httpBackend.when('GET', '../drupal/node.json').respond(rawResponse);
$httpBackend.expectGET("../drupal/node.json").respond(rawResponse);
var homeCtrl = createController();
expect(homeCtrl).toBeTruthy();
$httpBackend.flush();
expect($rootScope.posts).toEqual(processedResponse);
});
});
When I run the above through the Karma test runner, I get the following response:
当我通过Karma测试运行器运行上述内容时,我收到以下响应:
Chrome 31.0.1650 (Windows) home section HomeCtrl should get a list of blog posts FAILED
Expected [ { body : { value : '<p>Post body.</p>
', summary : '<p>Summary.</p>
' }, created : '1388415860000', previewText : { $$unwrapTrustedValue : Function } } ] to equal [ { body
: { value : '<p>Post body.</p>
', summary : '<p>Summary.</p>
' }, created : '1388415860000', previewText : { $$unwrapTrustedValue : Function } } ].
I suspect the problem is due to the fact that $sce.trustAsHtml
returns an object containing a function, rather than a string.
我怀疑问题是由于$ sce.trustAsHtml返回一个包含函数的对象,而不是字符串。
My question is, firstly, am I approaching this problem in the correct way?
我的问题是,首先,我是否以正确的方式处理这个问题?
Secondly, if so, how should I go about testing the output of $sce.trustAsHtml
?
其次,如果是这样,我应该如何测试$ sce.trustAsHtml的输出?
4 个解决方案
#1
33
Since the answer given by michael-bromley didn't work for me I want to point out another solution. In my case I was using a filter that wraps each occurrence of a string in another string with a span that has a class of 'highlight'. In other words, I want words to be highlighted. Here is the code:
由于michael-bromley给出的答案对我不起作用,我想指出另一种解决方案。在我的情况下,我使用的是一个过滤器,它将每次出现的字符串包装在另一个字符串中,其字符串的类别为“highlight”。换句话说,我希望突出显示单词。这是代码:
angular.module('myModule').filter('highlight', function ($sce) {
return function (input, str) {
return $sce.trustAsHtml((input || '').replace(new RegExp(str, 'gi'), '<span class=\"highlighted\">$&</span>'));
};
});
I use the $sce service to trust the resulting value as HTML. To test this I need to use the $$unwrapTrustedValue function on the resulting value to get my test working:
我使用$ sce服务将结果值信任为HTML。为了测试这个,我需要在结果值上使用$$ unwrapTrustedValue函数来使我的测试工作:
it('01: should add a span with class \'highlight\' around each mathing string.', inject(function ($filter) {
// Execute
var result = $filter('highlight')('this str contains a str that will be a highlighted str.', 'str');
// Test
expect(result.$$unwrapTrustedValue()).toEqual('this <span class="highlighted">str</span> contains a <span class="highlighted">str</span> that will be a highlighted <span class="highlighted">str</span>.');
}));
UPDATE:
更新:
As @gugol kindly pointed out it is preferred not to use Angular internal methods like $$unwrapTrustedValue. A better approach is to use the public getTrustedHtml method on the $sce service. Like so:
正如@gugol所指出的那样,最好不要使用像$$ unwrapTrustedValue这样的Angular内部方法。更好的方法是在$ sce服务上使用public getTrustedHtml方法。像这样:
it('01: should add a span with class \'highlight\' around each mathing string.', inject(function ($sce, $filter) {
// Execute
var result = $filter('highlight')('this str contains a str that will be a highlighted str.', 'str');
// Test
expect($sce.getTrustedHtml(result)).toEqual('this <span class="highlighted">str</span> contains a <span class="highlighted">str</span> that will be a highlighted <span class="highlighted">str</span>.');
}));
#2
18
You have to disable $sce using its provider before each test.
您必须在每次测试之前使用其提供程序禁用$ sce。
When $sce is disabled all $sce.trust* methods just return original value instead of a wrapper function.
当$ sce被禁用时,所有$ sce.trust *方法只返回原始值而不是包装函数。
beforeEach(module(function ($sceProvider) {
$sceProvider.enabled(false);
}));
it('shall pass', inject(function($sce){
expect($sce.trustAsHtml('<span>text</span>')).toBe('<span>text</span>');
}));
In your particular example just do this:
在您的特定示例中,只需这样做:
describe('HomeCtrl', function() {
var $httpBackend, $rootScope, $sce, createController;
beforeEach(module(function ($sceProvider) {
$sceProvider.enabled(false);
}));
// rest of the file
});
#3
8
I discovered that you can use $sce.getTrusted
which will return the value originally passed to $sce.trustAsHtml
, in this case a string containing HTML, which you can then test for equality in the usual way.
我发现你可以使用$ sce.getTrusted,它将返回最初传递给$ sce.trustAsHtml的值,在这种情况下是一个包含HTML的字符串,然后你可以用通常的方式测试它是否相等。
So my test now looks like this:
所以我的测试现在看起来像这样:
it('should create a previewText property using $sce.trustAsHtml', function() {
// confirms that it is an object, as should be the case when
// it has been through $sce.trustAsHtml
expect(typeof result.previewText === 'object').toEqual(true);
expect($sce.getTrusted($sce.HTML, result.previewText))
.toEqual('<p>Original HTML content string</p>');
});
#4
2
Another option is to use the getTrustedHtml() function to get the html string value from $$unwrapTrustedValue.
另一种选择是使用getTrustedHtml()函数从$$ unwrapTrustedValue获取html字符串值。
vm.user.bio = $sce.getTrustedHtml(vm.user.bio);
#1
33
Since the answer given by michael-bromley didn't work for me I want to point out another solution. In my case I was using a filter that wraps each occurrence of a string in another string with a span that has a class of 'highlight'. In other words, I want words to be highlighted. Here is the code:
由于michael-bromley给出的答案对我不起作用,我想指出另一种解决方案。在我的情况下,我使用的是一个过滤器,它将每次出现的字符串包装在另一个字符串中,其字符串的类别为“highlight”。换句话说,我希望突出显示单词。这是代码:
angular.module('myModule').filter('highlight', function ($sce) {
return function (input, str) {
return $sce.trustAsHtml((input || '').replace(new RegExp(str, 'gi'), '<span class=\"highlighted\">$&</span>'));
};
});
I use the $sce service to trust the resulting value as HTML. To test this I need to use the $$unwrapTrustedValue function on the resulting value to get my test working:
我使用$ sce服务将结果值信任为HTML。为了测试这个,我需要在结果值上使用$$ unwrapTrustedValue函数来使我的测试工作:
it('01: should add a span with class \'highlight\' around each mathing string.', inject(function ($filter) {
// Execute
var result = $filter('highlight')('this str contains a str that will be a highlighted str.', 'str');
// Test
expect(result.$$unwrapTrustedValue()).toEqual('this <span class="highlighted">str</span> contains a <span class="highlighted">str</span> that will be a highlighted <span class="highlighted">str</span>.');
}));
UPDATE:
更新:
As @gugol kindly pointed out it is preferred not to use Angular internal methods like $$unwrapTrustedValue. A better approach is to use the public getTrustedHtml method on the $sce service. Like so:
正如@gugol所指出的那样,最好不要使用像$$ unwrapTrustedValue这样的Angular内部方法。更好的方法是在$ sce服务上使用public getTrustedHtml方法。像这样:
it('01: should add a span with class \'highlight\' around each mathing string.', inject(function ($sce, $filter) {
// Execute
var result = $filter('highlight')('this str contains a str that will be a highlighted str.', 'str');
// Test
expect($sce.getTrustedHtml(result)).toEqual('this <span class="highlighted">str</span> contains a <span class="highlighted">str</span> that will be a highlighted <span class="highlighted">str</span>.');
}));
#2
18
You have to disable $sce using its provider before each test.
您必须在每次测试之前使用其提供程序禁用$ sce。
When $sce is disabled all $sce.trust* methods just return original value instead of a wrapper function.
当$ sce被禁用时,所有$ sce.trust *方法只返回原始值而不是包装函数。
beforeEach(module(function ($sceProvider) {
$sceProvider.enabled(false);
}));
it('shall pass', inject(function($sce){
expect($sce.trustAsHtml('<span>text</span>')).toBe('<span>text</span>');
}));
In your particular example just do this:
在您的特定示例中,只需这样做:
describe('HomeCtrl', function() {
var $httpBackend, $rootScope, $sce, createController;
beforeEach(module(function ($sceProvider) {
$sceProvider.enabled(false);
}));
// rest of the file
});
#3
8
I discovered that you can use $sce.getTrusted
which will return the value originally passed to $sce.trustAsHtml
, in this case a string containing HTML, which you can then test for equality in the usual way.
我发现你可以使用$ sce.getTrusted,它将返回最初传递给$ sce.trustAsHtml的值,在这种情况下是一个包含HTML的字符串,然后你可以用通常的方式测试它是否相等。
So my test now looks like this:
所以我的测试现在看起来像这样:
it('should create a previewText property using $sce.trustAsHtml', function() {
// confirms that it is an object, as should be the case when
// it has been through $sce.trustAsHtml
expect(typeof result.previewText === 'object').toEqual(true);
expect($sce.getTrusted($sce.HTML, result.previewText))
.toEqual('<p>Original HTML content string</p>');
});
#4
2
Another option is to use the getTrustedHtml() function to get the html string value from $$unwrapTrustedValue.
另一种选择是使用getTrustedHtml()函数从$$ unwrapTrustedValue获取html字符串值。
vm.user.bio = $sce.getTrustedHtml(vm.user.bio);