Testar se o objeto é uma instância de uma classe / função quando o construtor não está disponível no escopo

Antes de fazer qualquer teste de unidade sério em JS, eu fiz muito em PHP. Quando queria verificar se algo é uma instância de outra coisa, simplesmente fiz algo como:

$this->assertTrue($foo instanceof Bar);

Isso também é possível em JS. No entanto, você precisa de acesso ao construtor para fazer isso.

function foo(x) {
function Foo(x) {
// something
}

return new Foo(x);
}

y
= foo(1);
// can't do "y instanceof Foo" because Foo is not in the current scope

Agora imagine que você tem este serviço de repositório em seu aplicativo angular e deseja verificar se ‘getCurrent’ responde com uma promessa:

(function (angular) {
var app = angular.module('app');

var UserRepository = function ($http, CONFIG) {
var baseUrl = CONFIG.api.baseUrl;

this.getCurrent = function () {
return $http.get(baseUrl + '/me');
};

// more code here
};

app
.service('UserRepository', ['$http', 'CONFIG', UserRepository]);
}(window.angular));

Protótipos para o resgate:

// we have $q service already injected
output
= UserRepository.getCurrent(); expect(output.__proto__).toBe($q.defer().promise.__proto__);

Post Scriptum

Lembre-se de que JS é uma linguagem digitada dinamicamente e mesmo que seu objeto seja baseado em algum protótipo, isso não significa que os métodos exigidos por você não serão desvinculados no caminho. A maioria dos desenvolvedores não fará coisas assim, mas ainda é possível. Vamos ficar um pouco paranóicos por um tempo. Então…

Outra maneira de testar seu objeto seria verificar se ele possui os métodos de que você precisa:

expect(typeof output.then).toBe('function');
expect
(typeof output.success).toBe('function');
expect
(typeof output.error).toBe('function');

É a chamada digitação de pato – se grasna e voa – é um pato. Se ele tiver o mesmo conjunto de métodos que um objeto de promessa de que precisamos – isso significa que é nosso objeto de promessa.

Mesmo assim, isso não significa que essas funções corresponderão à interface de que você precisa (os argumentos de entrada e o valor de retorno podem ser diferentes do que você precisa).

Se você deseja ter 100% de certeza de que nosso ‘getCurrent’ retorna exatamente a mesma coisa que $ http.get – você pode, por exemplo, simular o serviço $ http para esse teste de unidade. Uma maneira de fazer isso seria:

describe('UserRepository.getCurrent', function () {
var httpMock;
var UserRepository;
var getReturnValue = {}; // just a plain object to be used to verify if both $http.get and UserRepository.getCurrent will return that exact object

beforeEach
(function () {
module('app', function ($provide) {

// create a mock
httpMock
= jasmine.createSpyObj('$http', ['get']);
httpMock
.get.and.callFake(function () {
return getReturnValue;
});

// replace $http definition with our mock
$provide
.service('$http', function () {
return httpMock;
});
});

// inject service we're about to test - it will get our mocked $http
inject
(function (_UserRepository_) {
UserRepository = _UserRepository_;
});
});

// test
it
('should return $http's promise', function () {
expect
(UserRepository.getCurrent()).toBe(getReturnValue);
});
});

E é isso. Você conhece uma maneira melhor? Eu sou todo seu.