开玩笑-模拟update()函数

时间:2020-02-07 02:37:42

标签: mocking jestjs nestjs

我正在尝试模拟对update()的调用,但是我一直在恢复与传递的值相同的值。

我想这是因为我没有连接数据库,而Jest正在嘲笑行为。

使用邮递员,我可以验证PUT是否确实更新了数据库中的用户。

如何在使用Jest的单元测试中模拟更新功能?

user.entity.ts


@Entity('users')
export class User {
  @PrimaryGeneratedColumn('uuid') user_id: string;
  @Column('text') name: string;
  @OneToMany(
    type => Post,
    post => post.user,
    {
      eager: true,
    },
  )
  posts: Post[];
  @OneToMany(
    type => Comment,
    comment => comment.user,
  )
  comments: Comment[];

  constructor(name?: string, posts?: []);
  constructor(name?: string) {
    this.name = name || '';
  }
}

user.service.ts

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    @Inject(forwardRef(() => PostService))
    private postService: PostService,
  ) {}

  /**
   * Create a new user
   * @param data Object
   */
  async add(data: Partial<UserDTO>): Promise<User> {
    // create object with new user props
    const newUser = await this.userRepository.create(data);
    await this.userRepository.save(newUser);
    return newUser;
  }

 /**
   * Update an existing user
   * @param data Object
   */
  async edit(user_id: string, data: Partial<UserDTO>): Promise<User> {
    await this.userRepository.update({ user_id }, data);
    return this.userRepository.findOne({ user_id });
  }

  /**
   * Return one user
   */
  async findOne(user_id: string): Promise<User> {
    return await this.userRepository.findOne({
      relations: ['posts', 'comments'],
      where: { user_id },
    });
  }

... more methods

}

user.service.spec.ts

import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from './user.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import * as faker from 'faker';
import { PostService } from '../post/post.service';

// test data for user
const testUserName1 = `${faker.name.firstName()} ${faker.name.lastName()}`;

const testUserName2 = `${faker.name.firstName()} ${faker.name.lastName()}`;

// user test object
const testUser = new User(testUserName1);
const testUser2 = new User(testUserName2);

// users test array
const testUsers = [testUser, testUser2];

// mock of Post Service class
// required as these two models are related
class PostServiceMock extends PostService {}
/**
 * User Model Unit Test
 */
describe('UserService', () => {
  let userService: UserService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: getRepositoryToken(User),
          // mocks of all the methods from the User Service
          useValue: {
            save: jest.fn(),
            create: jest.fn().mockReturnValue(testUser),
            find: jest.fn().mockResolvedValue(testUsers),
            findOne: jest.fn().mockResolvedValue(testUser),
            update: jest.fn().mockResolvedValue(testUser),
            delete: jest.fn().mockResolvedValue(true),
          },
        },
        {
          provide: PostService,
          useValue: PostServiceMock,
        },
      ],
    }).compile();

    userService = module.get<UserService>(UserService);
  });


  it('should be able to update a user', async () => {
    const newUser = await userService.add({
      name: testUserName1,
    });

    const updatedUserName = `${faker.name.firstName()} ${faker.name.firstName()}`;
    const updatedUser = await userService.edit(newUser.user_id, {
      name: updatedUserName,
    });

    expect(updatedUser).not.toBe(newUser);
  });

  afterEach(() => {
    jest.resetAllMocks();
  });
});

1 个答案:

答案 0 :(得分:1)

在模拟与数据库的连接时,需要查看模拟函数返回的内容。完成更新测试后,让我们逐步进行此步骤:

const newUser = await userService.add({name: testUser1});

根据您的服务代码,userService.add运行userRepostiory.create()userRepository.save()save只是一个存根方法,因此让我们看一下create。在提供模拟时,我们将看看模拟返回的结果

create: jest.fn().mockReturnValue(testUser)

所以现在我们知道,只要我们调用userRepostiory.create(),就会返回testUser。现在

const newUser = testUser

本质上是替换代码。继续前进,我们正在生成一个伪造的updateUserName,这很容易理解。然后我们运行userService.edit(),让我们来看一下

async edit(user_id: string, data: Partial<UserDTO>): Promise<User> {
  await this.userRepository.update({ user_id }, data);
  return this.userRepository.findOne({ user_id });
}

因此,我们首先运行userRepository.update(),然后运行userRepository.findOne(),由于我们仍在模拟userRepository,因此请看一下这些模拟。特别是findOne,因为我们对update的返回值不做任何事情。

update: jest.fn().mockResolvedValue(testUser)

所以update也返回了testUser。现在,如果我们将测试代码替换为模拟替换:

const updateUser = await Promise.resolve(testUser)

或更简单地

const updateUser = testUser

您的断言就是

expect(udpateUser).not.toBe(newUser)

并通过替换得到

expect(testUser).not.toBe(testUser)

瞧,这里有问题。由于create()findOne()模拟都返回相同的值和相同的对象,因此更新前后您的用户将始终相同!为了解决这个问题,您可以做的一件事就是模拟该函数以使其具有一个不同于以下的一次性解析值:

const findSpy = jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(someUpdatedUser)

这将要求您像引用UserRepository一样获得对UserService的引用,否则,您可以自由地进行期望的声明。