#angular #unit-testing #dependency-injection
#angular #модульное тестирование #внедрение зависимостей
Вопрос:
У меня есть служба Angular, которая использует другую службу для сохранения некоторых своих данных между загрузками страницы:
@Service({providedIn: "root"})
class PersistenceService {
save(key: string, value: string) { ... }
load(key: string): string { ... }
}
@Service({providedIn: "root"})
class DataService {
user?: string;
constructor(private persistence: PersistenceService) {}
init() { this.user = this.persistence.load("user"); }
setUser(str: string) {
this.user = str;
this.persistence.save("user", str);
}
}
Когда я хочу протестировать DataService
, я внедряю отключенный шпион для PersistenceService
использования класса Angular TestBed
:
beforeEach(() => {
fakeValues = {};
persistence = jasmine.createSpyObj("PersistenceService", ["save", "load"]);
persistence.load.and.callFake(key => fakeValues[key]);
TestBed.configureTestingModule({
providers: [
DataService,
{provide: PersistenceService, useValue: persistence},
]
});
instance = TestBed.inject(DataService);
})
Я хочу протестировать способ DataService
использования сохраняемых значений. Я могу сделать это достаточно легко, манипулируя макетом PersistenceService
( fakeValues
в приведенном выше примере). Например:
it("saves the user", () => {
instance.init();
instance.setUser("Joe");
expect(persistence.save).toHaveBeenCalledWith("user, "Joe");
});
it("loads the user", () => {
fakeValues.user = "Joe";
instance.init();
expect(instance.user).toBe("Joe");
});
Я думаю, что это может быть неправильный способ сделать это, потому что для этого требуется, чтобы я знал детали реализации DataService
: тот факт, что пользователь сохраняется с использованием ключа «user». На самом деле я хочу протестировать, когда я вызываю setUser("Joe")
, затем перезагружаю страницу (создавая новый экземпляр DataService
), проверяю, что instance.user
она извлекается из службы сохраняемости, и устанавливается значение «Joe».
В приведенном выше примере я мог бы, конечно, просто init
снова вызвать метод или создать второй экземпляр напрямую new DataService(TestBed.inject(PersistenceService))
, но в моем реальном коде может быть невозможно сбросить состояние экземпляра службы и повторно инициализировать, а некоторые службы имеют 4-6 зависимостей, поэтому создание одного вручную является проблемой. Похоже, что лучшим шаблоном был бы тот, в котором я могу внедрить тестируемый сервис, внести некоторые изменения, затем имитировать перезагрузку страницы и создать совершенно новый экземпляр и проверить его поведение, и все это в одном тестовом примере. Есть ли простой способ сделать это?
Ответ №1:
Я слишком много думал об этом.
Вызов TestBed.resetTestingModule()
уничтожает все экземпляры одноэлементной службы. Таким образом, я могу переместить поведение настройки из beforeEach
в его собственную функцию (назовем ее setup
), затем в тесте я могу просто сделать:
instance.init();
expect(instance.user).toBeUndefined();
instance.setUser("Joe");
// Simulate page reload
TestBed.resetTestingModule();
setup();
expect(instance.user).toBeUndefined();
instance.init();
expect(instance.user).toBe("Joe");