reactjs - jest spyOn index dosyasında çalışmıyor, özelliği yeniden tanımlayamıyor


UserContext adlı bir nesnem ve src/app/context/user-context.tsx dosyasından dışa aktarılan useUser adlı bir hook'um var. Ayrıca, tüm alt modülleri dışa aktaran src/app/context içinde bir index.tsx dosyam var.

Eğer src/app/context/user-context üzerine spyOn yaparsam çalışıyor ama importu src/app/context olarak değiştirirsem şu hatayı alıyorum:

TypeError: Cannot redefine property: useUser at Function.defineProperty (<anonymous>)

Bu neden?

Kaynak kodu:

// src/app/context/user-context.tsx

export const UserContext = React.createContext({});

export function useUser() {
  return useContext(UserContext);;
}

// src/app/context/index.tsx

export * from "./user-context";
// *.spec.tsx

// This works:
import * as UserContext from "src/app/context/user-context";

// This does not work:
// import * as UserContext from "src/app/context";

it("should render complete navigation when user is logged in", () => {

    jest.spyOn(UserContext, "useUser").mockReturnValue({
        user: mockUser,
        update: (user) => null,
        initialized: true,
    });
})

Cevap #1

spyOn() yerine jest.mock() kullanmanın iyi bir çözüm olduğu konusunda katılamıyorum. Bu yaklaşımla, tüm fonksiyonları spec dosyanızın başında bir kez taklit etmeniz gerekir.

Ama bazı test durumları farklı bir kurulum gerektirebilir veya gerçek kalabilir.

Her neyse, spyOn() kullanmaya devam etmek istiyorum.

Daha iyi bir yol, spec dosyanızın en üstüne (importlardan hemen sonra ve describe()'den önce) aşağıdaki satırları eklemektir.

import * as Foo from 'path/to/file'; 

jest.mock('path/to/file', () => {
  return {
    __esModule: true,    //    <----- this __esModule: true is important
    ...jest.requireActual('path/to/file')
  };
});

...

//just do a normal spyOn() as you did before somewhere in your test:
jest.spyOn(Foo, 'fn');

Not: Ayrıca bir satırlık da olabilir:

jest.mock('path/to/file', () => ({ __esModule: true, ...jest.requireActual('path/to/file') }));

P.P.S. 'path/to/file' öğesini bir sabite koymak işe yaramayabilir. En azından benim için işe yaramadı. Yani ne yazık ki gerçek yolu 3 kez tekrar etmeniz gerekiyor.

Cevap #2

Eğer bir yeniden ihracat için üretilen js koduna bir göz atarsanız, bu şekilde görünüyor

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _context = require("context");

Object.keys(_context).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (key in exports && exports[key] === _context[key]) return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function get() {
      return _context[key];
    }
  });
});

ve aldığınız hata, jest'in dokümana göre export'u yeniden tanımlayarak taklit etmesine izin verecek olan defineProperty seçeneklerine configurable: true eklememesi nedeniyle oluşuyor

konfigüre edilebilir

true, bu özellik tanımlayıcısının türünün değiştirilebileceği ve özelliğin ilgili nesneden silinebileceği anlamına gelir. Varsayılan olarak false.

Bence derleyicinin bunu eklemesini sağlamak için yapılandırmanızı bir şekilde değiştirebilirsiniz, ama hepsi kullandığınız araçlara bağlı.

Daha erişilebilir bir yaklaşım, yapılandırılamayan bir ihracatı yeniden tanımlamaya çalışmak yerine kullanıcı-bağlam dosyasını taklit etmek için jest.spyOn yerine jest.mock kullanmak olacaktır

it("should render complete navigation when user is logged in", () => {
  jest.mock('./user-context', () => {
    return {
      ...jest.requireActual('./user-context'),
      useUser: jest.fn().mockReturnValue({
        user: {},
        update: (user: any) => null,
        initialized: true
      })
    }
  })
});

İlgili makaleler